fix: SQL maker creates wrong categories and badge import fails when ExternalTexts.json missing
- writeSqlMigration now classifies furni via classifyFurni and generates correct category sub-pages (imp_<catKey>) instead of hardcoded imp_other - writeSqlMigration generates idempotent INSERT...SELECT WHERE NOT EXISTS for parent and category catalog_pages, with correct numeric page_id - writeBadgeToExternalTexts creates ExternalTexts.json (and parent dirs) when missing instead of failing with ENOENT
This commit is contained in:
1 parent
1851653afb
commit
4d4cbd2211
3 files changed
+60
-15
No files matched your search
@@ -17,24 +17,42 @@ export async function writeBadgeToExternalTexts(
|
|||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
): Promise<ImportBadgeResult> {
|
): Promise<ImportBadgeResult> {
|
||||||
|
const nameKey = `badge_name_${code}`;
|
||||||
|
const descKey = `badge_desc_${code}`;
|
||||||
|
|
||||||
|
let texts: Record<string, string>;
|
||||||
try {
|
try {
|
||||||
const raw = await fs.readFile(EXTERNAL_TEXTS_PATH, "utf-8");
|
const raw = await fs.readFile(EXTERNAL_TEXTS_PATH, "utf-8");
|
||||||
const texts = JSON.parse(raw) as Record<string, string>;
|
texts = JSON.parse(raw) as Record<string, string>;
|
||||||
|
} catch (err) {
|
||||||
const nameKey = `badge_name_${code}`;
|
// File not present yet (fresh deployment) — start with an empty object.
|
||||||
const descKey = `badge_desc_${code}`;
|
if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
|
||||||
|
console.warn(
|
||||||
if (texts[nameKey] !== undefined) {
|
"[import-badges] Failed to update ExternalTexts.json:",
|
||||||
|
(err as Error).message,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
error: "Badge already exists in ExternalTexts.json",
|
error: "Failed to update ExternalTexts.json",
|
||||||
status: 409,
|
status: 500,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
texts = {};
|
||||||
|
}
|
||||||
|
|
||||||
texts[nameKey] = name;
|
if (texts[nameKey] !== undefined) {
|
||||||
texts[descKey] = description || "";
|
return {
|
||||||
|
ok: false,
|
||||||
|
error: "Badge already exists in ExternalTexts.json",
|
||||||
|
status: 409,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
texts[nameKey] = name;
|
||||||
|
texts[descKey] = description || "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.mkdir(path.dirname(EXTERNAL_TEXTS_PATH), { recursive: true });
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
EXTERNAL_TEXTS_PATH,
|
EXTERNAL_TEXTS_PATH,
|
||||||
JSON.stringify(texts, null, 4),
|
JSON.stringify(texts, null, 4),
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ vi.mock("@/lib/services/furni-import", () => ({
|
|||||||
async (insertFn: (nextId: number) => Promise<unknown>) => insertFn(701),
|
async (insertFn: (nextId: number) => Promise<unknown>) => insertFn(701),
|
||||||
),
|
),
|
||||||
autoPriceFurni: vi.fn(() => ({ credits: 3, points: 0, pointsType: 0 })),
|
autoPriceFurni: vi.fn(() => ({ credits: 3, points: 0, pointsType: 0 })),
|
||||||
|
CATEGORY_PAGE: {},
|
||||||
|
classifyFurni: vi.fn(() => "other"),
|
||||||
ensureDirectories: vi.fn(async () => {
|
ensureDirectories: vi.fn(async () => {
|
||||||
const dirs = [
|
const dirs = [
|
||||||
assetTargets.swfDir,
|
assetTargets.swfDir,
|
||||||
@@ -76,6 +78,8 @@ vi.mock("@/lib/services/furni-import", () => ({
|
|||||||
await Promise.all(dirs.map((dir) => fs.mkdir(dir, { recursive: true })));
|
await Promise.all(dirs.map((dir) => fs.mkdir(dir, { recursive: true })));
|
||||||
}),
|
}),
|
||||||
getOrCreateCategoryPage: getOrCreateCategoryPageMock,
|
getOrCreateCategoryPage: getOrCreateCategoryPageMock,
|
||||||
|
IMPORTED_PAGE_CAPTION: "Imported Furniture",
|
||||||
|
IMPORTED_PAGE_CAPTION_SAVE: "imported_furni",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("@/lib/services/swf/nitro-builder", () => ({
|
vi.mock("@/lib/services/swf/nitro-builder", () => ({
|
||||||
|
|||||||
@@ -7,8 +7,12 @@ import { appendFurniEntry, buildFurniEntry } from "@/lib/services/furni-data";
|
|||||||
import {
|
import {
|
||||||
allocateCatalogItemId,
|
allocateCatalogItemId,
|
||||||
autoPriceFurni,
|
autoPriceFurni,
|
||||||
|
CATEGORY_PAGE,
|
||||||
|
classifyFurni,
|
||||||
ensureDirectories,
|
ensureDirectories,
|
||||||
getOrCreateCategoryPage,
|
getOrCreateCategoryPage,
|
||||||
|
IMPORTED_PAGE_CAPTION,
|
||||||
|
IMPORTED_PAGE_CAPTION_SAVE,
|
||||||
} from "@/lib/services/furni-import";
|
} from "@/lib/services/furni-import";
|
||||||
import { parseNitroBundle } from "@/lib/services/swf/nitro-builder";
|
import { parseNitroBundle } from "@/lib/services/swf/nitro-builder";
|
||||||
|
|
||||||
@@ -153,6 +157,7 @@ async function writeSqlMigration(params: {
|
|||||||
interactionType: string;
|
interactionType: string;
|
||||||
customparams: string;
|
customparams: string;
|
||||||
price: { credits: number; points: number; pointsType: number };
|
price: { credits: number; points: number; pointsType: number };
|
||||||
|
categoryKey: string;
|
||||||
}): Promise<string> {
|
}): Promise<string> {
|
||||||
const num = await findNextMigrationNumber();
|
const num = await findNextMigrationNumber();
|
||||||
const padded = String(num).padStart(4, "0");
|
const padded = String(num).padStart(4, "0");
|
||||||
@@ -163,11 +168,29 @@ async function writeSqlMigration(params: {
|
|||||||
fileName,
|
fileName,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const catKey = params.categoryKey;
|
||||||
|
const captionSave = `imp_${catKey}`;
|
||||||
|
const catInfo = CATEGORY_PAGE[catKey] || CATEGORY_PAGE.other;
|
||||||
|
|
||||||
const lines: string[] = [
|
const lines: string[] = [
|
||||||
`-- Migration: ${fileName}`,
|
`-- Migration: ${fileName}`,
|
||||||
`-- Description: Import furniture "${params.classname}"`,
|
`-- Description: Import furniture "${params.classname}"`,
|
||||||
`-- Generated: ${new Date().toISOString()}`,
|
`-- Generated: ${new Date().toISOString()}`,
|
||||||
"",
|
"",
|
||||||
|
`-- Ensure the "Imported Furniture" parent page exists`,
|
||||||
|
`INSERT INTO catalog_pages (caption_save, caption, page_layout, parent_id, min_rank, order_num, icon_image, enabled, visible, includes, page_headline, page_teaser, page_special, page_text1, page_text2, page_text_details, page_text_teaser)`,
|
||||||
|
`SELECT ${escapeSql(IMPORTED_PAGE_CAPTION_SAVE)}, ${escapeSql(IMPORTED_PAGE_CAPTION)}, 'default_3x3', -1, '1', 9999, 263, '1', '1', '', '', '', '', '', '', '', ''`,
|
||||||
|
`WHERE NOT EXISTS (SELECT 1 FROM catalog_pages WHERE caption_save = ${escapeSql(IMPORTED_PAGE_CAPTION_SAVE)});`,
|
||||||
|
"",
|
||||||
|
`-- Ensure the category sub-page exists (${catKey})`,
|
||||||
|
`INSERT INTO catalog_pages (caption_save, caption, page_layout, parent_id, min_rank, order_num, icon_image, enabled, visible, includes, page_headline, page_teaser, page_special, page_text1, page_text2, page_text_details, page_text_teaser)`,
|
||||||
|
`SELECT ${escapeSql(captionSave)}, ${escapeSql(catInfo.label)}, 'default_3x3', p.id, '1', ${catInfo.order}, ${catInfo.icon}, '1', '1', '', '', '', '', '', '', '', ''`,
|
||||||
|
`FROM catalog_pages p`,
|
||||||
|
`WHERE p.caption_save = ${escapeSql(IMPORTED_PAGE_CAPTION_SAVE)}`,
|
||||||
|
` AND NOT EXISTS (SELECT 1 FROM catalog_pages c WHERE c.caption_save = ${escapeSql(captionSave)} AND c.parent_id = p.id);`,
|
||||||
|
"",
|
||||||
|
`SET @next_id = (SELECT COALESCE(MAX(id), 0) + 1 FROM items_base);`,
|
||||||
|
"",
|
||||||
`INSERT INTO items_base`,
|
`INSERT INTO items_base`,
|
||||||
` (id, sprite_id, public_name, item_name, type, width, length, stack_height,`,
|
` (id, sprite_id, public_name, item_name, type, width, length, stack_height,`,
|
||||||
` allow_stack, allow_sit, allow_lay, allow_walk, interaction_type, customparams)`,
|
` allow_stack, allow_sit, allow_lay, allow_walk, interaction_type, customparams)`,
|
||||||
@@ -177,13 +200,12 @@ async function writeSqlMigration(params: {
|
|||||||
` ${params.allowStack}, ${params.cansiton ? "1" : "0"}, ${params.canlayon ? "1" : "0"}, ${params.canstandon ? "1" : "0"},`,
|
` ${params.allowStack}, ${params.cansiton ? "1" : "0"}, ${params.canlayon ? "1" : "0"}, ${params.canstandon ? "1" : "0"},`,
|
||||||
` ${escapeSql(params.interactionType)}, ${escapeSql(params.customparams)});`,
|
` ${escapeSql(params.interactionType)}, ${escapeSql(params.customparams)});`,
|
||||||
"",
|
"",
|
||||||
`SET @next_id = LAST_INSERT_ID();`,
|
|
||||||
"",
|
|
||||||
`INSERT INTO catalog_items`,
|
`INSERT INTO catalog_items`,
|
||||||
` (id, page_id, item_ids, catalog_name, cost_credits, cost_points, points_type, amount, order_number, offer_id, extradata)`,
|
` (id, page_id, item_ids, catalog_name, cost_credits, cost_points, points_type, amount, order_number, offer_id, extradata)`,
|
||||||
`VALUES`,
|
`SELECT (SELECT COALESCE(MAX(id), 0) + 1 FROM catalog_items), p.id, @next_id, ${escapeSql(params.classname)},`,
|
||||||
` (NULL, ${escapeSql("imp_other")}, @next_id, ${escapeSql(params.classname)},`,
|
` ${params.price.credits}, ${params.price.points}, ${params.price.pointsType}, 1, 1, '-1', ''`,
|
||||||
` ${params.price.credits}, ${params.price.points}, ${params.price.pointsType}, 1, 1, '-1', '');`,
|
`FROM catalog_pages p`,
|
||||||
|
`WHERE p.caption_save = ${escapeSql(captionSave)};`,
|
||||||
"",
|
"",
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -379,6 +401,7 @@ export async function uploadSingleFurni(params: {
|
|||||||
interactionType: userInteractionType,
|
interactionType: userInteractionType,
|
||||||
customparams,
|
customparams,
|
||||||
price,
|
price,
|
||||||
|
categoryKey: classifyFurni(classname, itemType),
|
||||||
});
|
});
|
||||||
warnings.push(`SQL migration written: ${sqlFile}`);
|
warnings.push(`SQL migration written: ${sqlFile}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in new issue
Block a user