commit 7e447423cbf0993a392ea953b9af969093c70ba0
parent f8de9eecef000aee0c6411b8c43a6d22602610a0
Author: Pablo Murad <pblmrd@gmail.com>
Date: Fri, 1 May 2026 11:59:37 -0300
favicon
Diffstat:
2 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
@@ -14,7 +14,7 @@ Retro-styled web player that picks random tracks from [`metadata.tsv`](../data/m
- `METADATA_TSV` — path to `metadata.tsv` (default: `../data/metadata.tsv` relative to this folder).
- **Ports** — defined in [`config/ports.ts`](config/ports.ts) as paired pools. Use `PORT_INDEX` (0–3) to pick a pair, or set `PORT` / `VITE_DEV_PORT` explicitly. Defaults: API `38471`, Vite dev `38472` (index 0).
- `IA_ITEM_ID` (optional) — Internet Archive item id (default `myspace_dragon_hoard_2010`).
- - `SERVE_STATIC` — set to `true` in production so Fastify serves `dist/` and `/api` on one port (required for PM2 single-process deploy).
+ - `SERVE_STATIC` — if `dist/index.html` exists after `npm run build`, the app serves the SPA + `/api` automatically. Set `SERVE_STATIC=false` for API-only. Explicit `true`/`1` is optional; use it when you want a clear flag in PM2/systemd.
2. Install dependencies:
diff --git a/server/index.ts b/server/index.ts
@@ -29,8 +29,15 @@ const METADATA_TSV = process.env.METADATA_TSV
const IA_ITEM_ID = process.env.IA_ITEM_ID?.trim() || IA_DRAGON_HOARD_ID;
const distDir = path.join(__dirname, "..", "dist");
-const serveStatic =
+const distIndexPath = path.join(distDir, "index.html");
+const distExists = fs.existsSync(distIndexPath);
+
+/** Serve Vite build from dist/ when it exists (typical behind nginx). Opt out with SERVE_STATIC=false. */
+const staticDisabled =
+ process.env.SERVE_STATIC === "false" || process.env.SERVE_STATIC === "0";
+const staticExplicit =
process.env.SERVE_STATIC === "true" || process.env.SERVE_STATIC === "1";
+const serveStatic = !staticDisabled && (staticExplicit || distExists);
let pool: TrackMeta[] = [];
@@ -92,19 +99,33 @@ async function main() {
});
});
- if (serveStatic && fs.existsSync(path.join(distDir, "index.html"))) {
- await app.register(fastifyStatic, {
- root: distDir,
- prefix: "/",
- });
- app.setNotFoundHandler((request, reply) => {
- const pathname = request.url.split("?")[0] ?? "";
- if (pathname.startsWith("/api")) {
- return reply.code(404).send({ error: "Not found" });
- }
- return reply.sendFile("index.html");
- });
- console.info(`MyMusics: serving static files from ${distDir}`);
+ if (serveStatic) {
+ if (distExists) {
+ await app.register(fastifyStatic, {
+ root: distDir,
+ prefix: "/",
+ });
+ app.setNotFoundHandler((request, reply) => {
+ const pathname = request.url.split("?")[0] ?? "";
+ if (pathname.startsWith("/api")) {
+ return reply.code(404).send({ error: "Not found" });
+ }
+ return reply.sendFile("index.html");
+ });
+ console.info(`MyMusics: serving SPA + /api from ${distDir}`);
+ } else {
+ console.warn(
+ "MyMusics: SERVE_STATIC requested but dist/index.html is missing — run `npm run build` on the server.",
+ );
+ }
+ } else if (distExists) {
+ console.info(
+ "MyMusics: dist/ exists but SPA is disabled (SERVE_STATIC=false); GET / returns 404, /api only.",
+ );
+ } else {
+ console.info(
+ "MyMusics: API only (no dist/). Use `npm run build` or set up Vite dev; nginx should not proxy / to this port until SPA is served.",
+ );
}
await app.listen({ port: PORT, host: "0.0.0.0" });