Add launcher request board
This commit is contained in:
parent
6c6b1295a3
commit
4eef0d4850
3 changed files with 628 additions and 33 deletions
166
server.js
166
server.js
|
|
@ -38,6 +38,7 @@ const dataRoot = path.resolve(__dirname, "data");
|
|||
const catalogMetaPath = path.join(dataRoot, "catalog_meta.json");
|
||||
const dialogueNodeMetaPath = path.join(dataRoot, "dialogue_node_meta.json");
|
||||
const editorSettingsPath = path.join(dataRoot, "editor_settings.json");
|
||||
const launcherRequestsPath = path.join(dataRoot, "launcher_requests.json");
|
||||
const imagesCatalogPath = path.join(contentRoot, "images.json");
|
||||
const legacyTilesCatalogPath = path.join(contentRoot, "tiles.json");
|
||||
const legacySpritesCatalogPath = path.join(contentRoot, "sprites.json");
|
||||
|
|
@ -218,6 +219,57 @@ function readEditorSettings() {
|
|||
return normalizeEditorSettings(readJsonSafe(editorSettingsPath, createDefaultEditorSettings()));
|
||||
}
|
||||
|
||||
function createLauncherRequestId() {
|
||||
return `request_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
function normalizeLauncherRequestEntry(entry, index = 0) {
|
||||
const source = entry && typeof entry === "object" && !Array.isArray(entry)
|
||||
? entry
|
||||
: null;
|
||||
if (!source) {
|
||||
return null;
|
||||
}
|
||||
const text = String(source.text || "").trim();
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
const createdAt = String(source.createdAt || "").trim() || new Date().toISOString();
|
||||
const updatedAt = String(source.updatedAt || "").trim() || createdAt;
|
||||
const fallbackId = `request_${index + 1}`;
|
||||
return {
|
||||
id: String(source.id || fallbackId).trim() || fallbackId,
|
||||
text,
|
||||
done: source.done === true,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function readLauncherRequestsPayload() {
|
||||
const fallback = { schemaVersion: 1, requests: [] };
|
||||
const payload = readJsonSafe(launcherRequestsPath, fallback);
|
||||
const requests = Array.isArray(payload?.requests)
|
||||
? payload.requests
|
||||
.map((entry, index) => normalizeLauncherRequestEntry(entry, index))
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
return {
|
||||
schemaVersion: typeof payload?.schemaVersion === "number" ? payload.schemaVersion : 1,
|
||||
requests,
|
||||
};
|
||||
}
|
||||
|
||||
function writeLauncherRequestsPayload(payload) {
|
||||
const requests = Array.isArray(payload?.requests) ? payload.requests : [];
|
||||
writeJsonAtomic(launcherRequestsPath, {
|
||||
schemaVersion: typeof payload?.schemaVersion === "number" ? payload.schemaVersion : 1,
|
||||
requests: requests
|
||||
.map((entry, index) => normalizeLauncherRequestEntry(entry, index))
|
||||
.filter(Boolean),
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeBackgroundTileId(value, idToSymbol = null) {
|
||||
const normalizedId = String(value || "").trim();
|
||||
if (!normalizedId) {
|
||||
|
|
@ -2043,6 +2095,120 @@ app.get("/api/debug/recent-saves", (_req, res) => {
|
|||
});
|
||||
});
|
||||
|
||||
app.get("/api/launcher-requests", (_req, res) => {
|
||||
try {
|
||||
res.json(readLauncherRequestsPayload());
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: `Failed to read launcher requests: ${String(err)}` });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/launcher-requests", (req, res) => {
|
||||
try {
|
||||
const text = String(req.body?.text || "").trim();
|
||||
if (!text) {
|
||||
res.status(400).json({ error: "Request text is required." });
|
||||
return;
|
||||
}
|
||||
if (text.length > 1000) {
|
||||
res.status(400).json({ error: "Request text must be 1000 characters or fewer." });
|
||||
return;
|
||||
}
|
||||
const payload = readLauncherRequestsPayload();
|
||||
const now = new Date().toISOString();
|
||||
const requestEntry = normalizeLauncherRequestEntry({
|
||||
id: createLauncherRequestId(),
|
||||
text,
|
||||
done: false,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}, payload.requests.length);
|
||||
const nextPayload = {
|
||||
schemaVersion: payload.schemaVersion,
|
||||
requests: [...payload.requests, requestEntry],
|
||||
};
|
||||
writeLauncherRequestsPayload(nextPayload);
|
||||
recordSaveEvent({
|
||||
type: "launcher-request-add",
|
||||
requestId: requestEntry.id,
|
||||
textPreview: requestEntry.text.slice(0, 80),
|
||||
});
|
||||
res.status(201).json({
|
||||
ok: true,
|
||||
request: requestEntry,
|
||||
requests: nextPayload.requests,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: `Failed to save launcher request: ${String(err)}` });
|
||||
}
|
||||
});
|
||||
|
||||
app.patch("/api/launcher-requests/:requestId", (req, res) => {
|
||||
const requestId = String(req.params.requestId || "").trim();
|
||||
try {
|
||||
const payload = readLauncherRequestsPayload();
|
||||
const index = payload.requests.findIndex((entry) => entry.id === requestId);
|
||||
if (index < 0) {
|
||||
res.status(404).json({ error: "Request not found." });
|
||||
return;
|
||||
}
|
||||
const existing = payload.requests[index];
|
||||
const nextDone = req.body?.done === true;
|
||||
const updated = normalizeLauncherRequestEntry({
|
||||
...existing,
|
||||
done: nextDone,
|
||||
updatedAt: new Date().toISOString(),
|
||||
}, index);
|
||||
const nextRequests = [...payload.requests];
|
||||
nextRequests[index] = updated;
|
||||
writeLauncherRequestsPayload({
|
||||
schemaVersion: payload.schemaVersion,
|
||||
requests: nextRequests,
|
||||
});
|
||||
recordSaveEvent({
|
||||
type: "launcher-request-update",
|
||||
requestId,
|
||||
done: updated.done,
|
||||
});
|
||||
res.json({
|
||||
ok: true,
|
||||
request: updated,
|
||||
requests: nextRequests,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: `Failed to update launcher request: ${String(err)}` });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete("/api/launcher-requests/:requestId", (req, res) => {
|
||||
const requestId = String(req.params.requestId || "").trim();
|
||||
try {
|
||||
const payload = readLauncherRequestsPayload();
|
||||
const existing = payload.requests.find((entry) => entry.id === requestId);
|
||||
if (!existing) {
|
||||
res.status(404).json({ error: "Request not found." });
|
||||
return;
|
||||
}
|
||||
const nextRequests = payload.requests.filter((entry) => entry.id !== requestId);
|
||||
writeLauncherRequestsPayload({
|
||||
schemaVersion: payload.schemaVersion,
|
||||
requests: nextRequests,
|
||||
});
|
||||
recordSaveEvent({
|
||||
type: "launcher-request-delete",
|
||||
requestId,
|
||||
textPreview: existing.text.slice(0, 80),
|
||||
});
|
||||
res.json({
|
||||
ok: true,
|
||||
deletedRequestId: requestId,
|
||||
requests: nextRequests,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: `Failed to delete launcher request: ${String(err)}` });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/world-default", (_req, res) => {
|
||||
try {
|
||||
const indexPayload = readWorldIndexPayload();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue