fix: badge import uses configured gamedata root for ExternalTexts.json
- import-badge.ts, badges route, and badges edit route now resolve ExternalTexts.json via getGamedataRoot() instead of the hardcoded public/nitro-assets/gamedata/ path - writeBadgeToExternalTexts creates the file (and parent dirs) when missing, so fresh deployments no longer fail with ENOENT - upload-import SQL maker now classifies furni via classifyFurni and generates correct category sub-pages (imp_<catKey>) instead of hardcoded imp_other
This commit is contained in:
1 parent
4d4cbd2211
commit
79b82e0a31
4 files changed
+439
-26
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -3,11 +3,11 @@ import path from "node:path";
|
|||||||
import { withAdmin } from "@/lib/api-handler";
|
import { withAdmin } from "@/lib/api-handler";
|
||||||
import { apiError, apiOk } from "@/lib/api-response";
|
import { apiError, apiOk } from "@/lib/api-response";
|
||||||
import { PERMS } from "@/lib/permissions";
|
import { PERMS } from "@/lib/permissions";
|
||||||
|
import { getGamedataRoot } from "@/lib/services/furni-asset-dirs";
|
||||||
|
|
||||||
const EXTERNAL_TEXTS_PATH = path.join(
|
function externalTextsPath(gamedataRoot: string): string {
|
||||||
process.cwd(),
|
return path.join(gamedataRoot, "config", "ExternalTexts.json");
|
||||||
"public/nitro-assets/gamedata/ExternalTexts.json",
|
}
|
||||||
);
|
|
||||||
|
|
||||||
/** GET - Read badge name/desc from ExternalTexts.json */
|
/** GET - Read badge name/desc from ExternalTexts.json */
|
||||||
export const GET = withAdmin(
|
export const GET = withAdmin(
|
||||||
@@ -16,8 +16,11 @@ export const GET = withAdmin(
|
|||||||
const code = request.nextUrl.searchParams.get("code")?.toUpperCase();
|
const code = request.nextUrl.searchParams.get("code")?.toUpperCase();
|
||||||
if (!code) return apiError("Missing code", 400);
|
if (!code) return apiError("Missing code", 400);
|
||||||
|
|
||||||
|
const gamedataRoot = await getGamedataRoot();
|
||||||
|
if (!gamedataRoot) return apiError("gamedata_root not configured", 500);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const raw = await fs.readFile(EXTERNAL_TEXTS_PATH, "utf-8");
|
const raw = await fs.readFile(externalTextsPath(gamedataRoot), "utf-8");
|
||||||
const texts: Record<string, string> = JSON.parse(raw);
|
const texts: Record<string, string> = JSON.parse(raw);
|
||||||
|
|
||||||
const name =
|
const name =
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import { withAdmin } from "@/lib/api-handler";
|
|||||||
import { apiError, apiOk } from "@/lib/api-response";
|
import { apiError, apiOk } from "@/lib/api-response";
|
||||||
import { PERMS } from "@/lib/permissions";
|
import { PERMS } from "@/lib/permissions";
|
||||||
import { logAudit } from "@/lib/services/audit";
|
import { logAudit } from "@/lib/services/audit";
|
||||||
|
import { getGamedataRoot } from "@/lib/services/furni-asset-dirs";
|
||||||
import { searchBadges } from "@/lib/services/habboassets";
|
import { searchBadges } from "@/lib/services/habboassets";
|
||||||
import { importBadgeSynced } from "@/lib/services/import-badge";
|
import { importBadgeSynced } from "@/lib/services/import-badge";
|
||||||
|
|
||||||
const EXTERNAL_TEXTS_PATH = path.join(
|
function externalTextsPath(gamedataRoot: string): string {
|
||||||
process.cwd(),
|
return path.join(gamedataRoot, "config", "ExternalTexts.json");
|
||||||
"public/nitro-assets/gamedata/ExternalTexts.json",
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// ── Search badges via official Habbo external texts ────────────────
|
// ── Search badges via official Habbo external texts ────────────────
|
||||||
export const GET = withAdmin(
|
export const GET = withAdmin(
|
||||||
@@ -33,11 +33,14 @@ export const GET = withAdmin(
|
|||||||
// Check which badges already exist in ExternalTexts.json
|
// Check which badges already exist in ExternalTexts.json
|
||||||
const existingCodes = new Set<string>();
|
const existingCodes = new Set<string>();
|
||||||
try {
|
try {
|
||||||
const raw = await fs.readFile(EXTERNAL_TEXTS_PATH, "utf-8");
|
const gamedataRoot = await getGamedataRoot();
|
||||||
const texts = JSON.parse(raw);
|
if (gamedataRoot) {
|
||||||
for (const badge of badges) {
|
const raw = await fs.readFile(externalTextsPath(gamedataRoot), "utf-8");
|
||||||
if (texts[`badge_name_${badge.code}`] !== undefined) {
|
const texts = JSON.parse(raw);
|
||||||
existingCodes.add(badge.code);
|
for (const badge of badges) {
|
||||||
|
if (texts[`badge_name_${badge.code}`] !== undefined) {
|
||||||
|
existingCodes.add(badge.code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -1,28 +1,37 @@
|
|||||||
import { promises as fs } from "node:fs";
|
import { promises as fs } from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { db, WebsiteBadges } from "@/lib/db";
|
import { db, WebsiteBadges } from "@/lib/db";
|
||||||
|
import { getGamedataRoot } from "@/lib/services/furni-asset-dirs";
|
||||||
const EXTERNAL_TEXTS_PATH = path.join(
|
|
||||||
process.cwd(),
|
|
||||||
"public/nitro-assets/gamedata/ExternalTexts.json",
|
|
||||||
);
|
|
||||||
|
|
||||||
export type ImportBadgeResult =
|
export type ImportBadgeResult =
|
||||||
| { ok: true }
|
| { ok: true }
|
||||||
| { ok: false; error: string; status: number };
|
| { ok: false; error: string; status: number };
|
||||||
|
|
||||||
|
function externalTextsPath(gamedataRoot: string): string {
|
||||||
|
return path.join(gamedataRoot, "config", "ExternalTexts.json");
|
||||||
|
}
|
||||||
|
|
||||||
/** Write badge name/desc into ExternalTexts.json (409 if already present). */
|
/** Write badge name/desc into ExternalTexts.json (409 if already present). */
|
||||||
export async function writeBadgeToExternalTexts(
|
export async function writeBadgeToExternalTexts(
|
||||||
code: string,
|
code: string,
|
||||||
name: string,
|
name: string,
|
||||||
description: string,
|
description: string,
|
||||||
): Promise<ImportBadgeResult> {
|
): Promise<ImportBadgeResult> {
|
||||||
|
const gamedataRoot = await getGamedataRoot();
|
||||||
|
if (!gamedataRoot) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
error: "gamedata_root not configured",
|
||||||
|
status: 500,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const extPath = externalTextsPath(gamedataRoot);
|
||||||
const nameKey = `badge_name_${code}`;
|
const nameKey = `badge_name_${code}`;
|
||||||
const descKey = `badge_desc_${code}`;
|
const descKey = `badge_desc_${code}`;
|
||||||
|
|
||||||
let texts: Record<string, string>;
|
let texts: Record<string, string>;
|
||||||
try {
|
try {
|
||||||
const raw = await fs.readFile(EXTERNAL_TEXTS_PATH, "utf-8");
|
const raw = await fs.readFile(extPath, "utf-8");
|
||||||
texts = JSON.parse(raw) as Record<string, string>;
|
texts = JSON.parse(raw) as Record<string, string>;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// File not present yet (fresh deployment) — start with an empty object.
|
// File not present yet (fresh deployment) — start with an empty object.
|
||||||
@@ -52,12 +61,8 @@ export async function writeBadgeToExternalTexts(
|
|||||||
texts[descKey] = description || "";
|
texts[descKey] = description || "";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.mkdir(path.dirname(EXTERNAL_TEXTS_PATH), { recursive: true });
|
await fs.mkdir(path.dirname(extPath), { recursive: true });
|
||||||
await fs.writeFile(
|
await fs.writeFile(extPath, JSON.stringify(texts, null, 4), "utf-8");
|
||||||
EXTERNAL_TEXTS_PATH,
|
|
||||||
JSON.stringify(texts, null, 4),
|
|
||||||
"utf-8",
|
|
||||||
);
|
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
|||||||
Reference in new issue
Block a user