Major cleanup and add toml configuration
This commit is contained in:
+50
-44
@@ -1,36 +1,54 @@
|
||||
import { join } from "jsr:@std/path";
|
||||
import { serveDir } from "jsr:@std/http/file-server";
|
||||
import { parse } from "jsr:@std/toml";
|
||||
import type { OverlaySettings } from "../client/types.ts";
|
||||
import type { OverlayConfigApiBody } from "../client/overlay-config.ts";
|
||||
import { overlayTomlToDefaults, type OverlayToml } from "../client/overlay-config.ts";
|
||||
|
||||
const scriptDir = import.meta.dirname ?? ".";
|
||||
const root = join(scriptDir, "..", "..");
|
||||
const port = Number(Deno.env.get("PORT") ?? "8080");
|
||||
|
||||
function trimPathLine(line: string): string {
|
||||
let s = line.trim();
|
||||
if (!s || s.startsWith("#")) return "";
|
||||
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
||||
s = s.slice(1, -1);
|
||||
}
|
||||
return s.trim();
|
||||
}
|
||||
|
||||
/** Optional one-line file in repo root: absolute path to `ChatRequest/Database.json`. */
|
||||
function readOptionalPathFile(): string | undefined {
|
||||
function readOverlayConfig(): {
|
||||
chatRequestDatabase: string | undefined;
|
||||
apiDefaults: Partial<OverlaySettings>;
|
||||
} {
|
||||
const path = join(root, "overlay.toml");
|
||||
let raw: string;
|
||||
try {
|
||||
const pathFile = join(root, "chat-request-database.path");
|
||||
const raw = Deno.readTextFileSync(pathFile);
|
||||
for (const line of raw.split(/\r?\n/)) {
|
||||
const p = trimPathLine(line);
|
||||
if (p) return p;
|
||||
}
|
||||
raw = Deno.readTextFileSync(path);
|
||||
} catch {
|
||||
// missing or unreadable
|
||||
console.warn(
|
||||
"overlay.toml not found; using built-in defaults. Copy overlay.toml.example and set at least chat_request_database and beatleader_player_id.",
|
||||
);
|
||||
return { chatRequestDatabase: undefined, apiDefaults: {} };
|
||||
}
|
||||
return undefined;
|
||||
let parsed: OverlayToml;
|
||||
try {
|
||||
parsed = parse(raw) as OverlayToml;
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
console.error(`overlay.toml: parse error: ${msg}`);
|
||||
console.warn(
|
||||
"beatleader_player_id is not set; fix overlay.toml or set the BeatLeader id in the URL hash for friend scores.",
|
||||
);
|
||||
return { chatRequestDatabase: undefined, apiDefaults: {} };
|
||||
}
|
||||
|
||||
const { chatRequestDatabase, defaults, beatleaderPlayerIdConfigured, warnings } = overlayTomlToDefaults(
|
||||
parsed,
|
||||
);
|
||||
for (const w of warnings) console.warn(w);
|
||||
if (!beatleaderPlayerIdConfigured) {
|
||||
console.warn(
|
||||
"beatleader_player_id is not set; friend scores need a BeatLeader id in overlay.toml or in the URL hash.",
|
||||
);
|
||||
}
|
||||
|
||||
return { chatRequestDatabase, apiDefaults: defaults };
|
||||
}
|
||||
|
||||
const chatRequestDatabase =
|
||||
Deno.env.get("CHAT_REQUEST_DATABASE")?.trim() || readOptionalPathFile();
|
||||
const { chatRequestDatabase, apiDefaults } = readOverlayConfig();
|
||||
|
||||
function isSafeProxyPath(path: string): boolean {
|
||||
if (!path) return false;
|
||||
@@ -68,24 +86,16 @@ async function proxyApiRequest(req: Request, upstreamBase: string): Promise<Resp
|
||||
}
|
||||
}
|
||||
|
||||
function isChatRequestFilename(pathname: string): boolean {
|
||||
function isChatRequestPath(pathname: string): boolean {
|
||||
const base = pathname.split("/").pop() ?? "";
|
||||
return base === "ChatRequest.json" || base === "database.json";
|
||||
return base === "ChatRequest.json";
|
||||
}
|
||||
|
||||
Deno.serve({ port, hostname: "127.0.0.1" }, async (req) => {
|
||||
const url = new URL(req.url);
|
||||
if (req.method === "POST" && url.pathname === "/api/overlay-log") {
|
||||
try {
|
||||
const raw = await req.text();
|
||||
const parsed = JSON.parse(raw) as { scope?: string; phase?: string; detail?: unknown };
|
||||
const scope = typeof parsed.scope === "string" ? parsed.scope : "?";
|
||||
const phase = typeof parsed.phase === "string" ? parsed.phase : "?";
|
||||
console.log(`[overlay] ${scope}:${phase}`, parsed.detail ?? "");
|
||||
} catch {
|
||||
console.log("[overlay] overlay-log: invalid JSON body");
|
||||
}
|
||||
return new Response(null, { status: 204 });
|
||||
if (req.method === "GET" && url.pathname === "/api/overlay-config") {
|
||||
const body: OverlayConfigApiBody = { defaults: apiDefaults };
|
||||
return Response.json(body, { headers: { "cache-control": "no-store" } });
|
||||
}
|
||||
if (req.method === "GET" && url.pathname === "/api/beatleader") {
|
||||
return proxyApiRequest(req, "https://api.beatleader.com");
|
||||
@@ -93,7 +103,7 @@ Deno.serve({ port, hostname: "127.0.0.1" }, async (req) => {
|
||||
if (req.method === "GET" && url.pathname === "/api/beatsaver") {
|
||||
return proxyApiRequest(req, "https://api.beatsaver.com");
|
||||
}
|
||||
if (req.method === "GET" && chatRequestDatabase && isChatRequestFilename(url.pathname)) {
|
||||
if (req.method === "GET" && chatRequestDatabase && isChatRequestPath(url.pathname)) {
|
||||
try {
|
||||
let text = await Deno.readTextFile(chatRequestDatabase);
|
||||
if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);
|
||||
@@ -105,24 +115,20 @@ Deno.serve({ port, hostname: "127.0.0.1" }, async (req) => {
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Deno.errors.NotFound) {
|
||||
console.error(`CHAT_REQUEST_DATABASE not found: ${chatRequestDatabase}`);
|
||||
console.error(`chat_request_database not found: ${chatRequestDatabase}`);
|
||||
return new Response(null, { status: 404 });
|
||||
}
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
console.error(`CHAT_REQUEST_DATABASE read error (${chatRequestDatabase}): ${msg}`);
|
||||
return new Response(`Failed to read CHAT_REQUEST_DATABASE: ${msg}\n`, { status: 500 });
|
||||
console.error(`chat_request_database read error (${chatRequestDatabase}): ${msg}`);
|
||||
return new Response(`Failed to read chat_request_database: ${msg}\n`, { status: 500 });
|
||||
}
|
||||
}
|
||||
return serveDir(req, { fsRoot: root, showDirListing: false });
|
||||
});
|
||||
|
||||
console.log(`Overlay: http://127.0.0.1:${port}/index.html`);
|
||||
console.log("Friend/BeatLeader diagnostics from the page also print here (browser console has the same lines).");
|
||||
if (chatRequestDatabase) {
|
||||
console.log(`Chat request database file: ${chatRequestDatabase}`);
|
||||
} else {
|
||||
if (!chatRequestDatabase) {
|
||||
console.warn(
|
||||
"No database path: set CHAT_REQUEST_DATABASE or create chat-request-database.path (see README). " +
|
||||
"Otherwise /ChatRequest.json is only served from this folder if the file exists.",
|
||||
"No chat_request_database in overlay.toml — place ChatRequest.json in the repo folder or set chat_request_database (see overlay.toml.example).",
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user