Fix TypeScript errors and implement furniture import ID integrity with 18+ age verification
CI / check (push) Successful in 29s
CI / release (push) Skipped
CI / deploy (push) Successful in 1m2s

- Add termsAccepted and ageVerified columns to User table
- Update register schema with new boolean fields
- Fix register form age verification checkbox (th -> t)
- Fix furni-import spriteId declaration order
- Fix batch route variable naming (id -> spriteId)
- Fix catalog-audit import path and ensure correct types
- Hardened import with per-item id conflict checks
- Added audit option for FurnitureData.json spriteId conflicts
- Updated tagline to include Leeftijdsvereiste: 18+
This commit is contained in:
openhands committed 2026-08-14 16:37:00 +02:00
1 parent e5ec3c1f06
commit e76c530e4f
13 files changed
+267 -104

No files matched your search

+58
View File
@@ -16,6 +16,7 @@ import {
repairFurniData,
repairOrphanedCatalog,
} from "./catalog-repair";
import { readFurniData } from "./furni-data";
import { ensureDirectories } from "./furni-import";
import {
assetNameCandidates,
@@ -118,6 +119,7 @@ export interface AuditSummary {
duplicatesMerged: number;
duplicateRowsRemoved: number;
remappedReferences: number;
furniDataIdConflicts: number;
}
export interface CatalogAuditOptions {
@@ -128,6 +130,7 @@ export interface CatalogAuditOptions {
organizeSql?: boolean;
repairFurniData?: boolean;
repairStructure?: boolean;
checkFurniDataIds?: boolean;
}
export async function runCatalogAudit(
@@ -722,6 +725,60 @@ export async function runCatalogAudit(
}
}
// ── FurnitureData.json id conflict check ──────────────
let furniDataIdConflicts: {
classname: string;
itemId: number;
conflictingId: number;
existingClassname: string;
}[] = [];
if (options?.checkFurniDataIds) {
onEvent?.({
type: "progress",
message: "Checking FurnitureData.json for spriteId conflicts…",
});
try {
const furniData = (await readFurniData()) as Record<
string,
{ furnitype: Array<Record<string, unknown>> }
>;
// Build map of id → classname from both sections
const idToClassname = new Map<number, string>();
for (const section of ["roomitemtypes", "wallitemtypes"] as const) {
for (const e of furniData[section]?.furnitype ?? []) {
const id = Number(e?.id);
const classname = typeof e?.classname === "string" ? e.classname : "";
if (!Number.isFinite(id) || id <= 0 || !classname) continue;
const existing = idToClassname.get(id);
if (existing && existing !== classname) {
// Conflict: same id used by different classname
furniDataIdConflicts.push({
classname,
itemId: id,
conflictingId: id,
existingClassname: existing,
});
} else if (!existing) {
idToClassname.set(id, classname);
}
}
}
// Remove duplicates (same classname + id pair counted once)
const seen = new Set<string>();
furniDataIdConflicts = furniDataIdConflicts.filter((c) => {
const key = `${c.classname}:${c.conflictingId}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
} catch (err) {
onEvent?.({
type: "error",
message: `FurnitureData id check failed: ${(err as Error).message}`,
});
}
}
onEvent?.({ type: "progress", message: "Comparing with clone sources…" });
const sources = await listSources();
@@ -811,6 +868,7 @@ export async function runCatalogAudit(
duplicatesMerged,
duplicateRowsRemoved,
remappedReferences,
furniDataIdConflicts: furniDataIdConflicts.length,
};
onEvent?.({