The card always displayed 1.0.0. VITE_APP_VERSION was never set: CI
passes the release tag as a VERSION build arg, but the Dockerfile never
declared it or forwarded it to the frontend build, so the hardcoded
fallback always won.
Declare the build arg and export it as VITE_APP_VERSION, and resolve the
version in vite.config.ts for every other build: the env var, then the
release tag via git describe, then package.json. The image version label
was pinned at 1.0.0 for the same reason and now follows the arg too.
better-sqlite3 is synchronous, so a slow read froze the entire server — a
filter-options request over a slow mount was measured blocking it for 167
seconds, during which even /health timed out.
Add DbQueryPool: read-only SQLite connections in worker threads, with
DatabaseService.allAsync routing the full-table scans through them and falling
back in process when workers are unavailable. Filter-options queries now run
concurrently rather than serially. Workers reopen on database reload and stop
on shutdown.
Also set UV_THREADPOOL_SIZE=16: libuv's default of 4 throttled every image and
ZIP read in the process, which dominates on a network mount.
Collapse the separate backend (Node) and frontend (nginx) images into
one image where the Express backend serves the built React app in
addition to the API and game content, so deployment is a single
container and one origin.
- Add a root Dockerfile with frontend-builder + backend-builder stages
producing one runtime image; remove backend/ and frontend/ Dockerfiles,
the nginx config, and the frontend entrypoint
- Serve the frontend dist and SPA fallback from Express (config.serveFrontend
/ FRONTEND_DIST_PATH), before auth/maintenance so the shell always loads and
before the API 404 so client routes resolve to index.html
- Port the SPA-oriented CSP from nginx into Helmet, gated on serveFrontend;
keep the strict API-only policy otherwise
- Reduce both compose files to a single flashpoint-web service on WEB_PORT
-> 3100; drop API_PORT, BACKEND_HOST, BACKEND_PORT
- Build one image in the release workflow instead of a two-service matrix
- Update .env.example, README, workflow README, and env var docs
Also add .gitattributes (*.sh eol=lf) and normalize the entrypoint line
endings — without it, Windows checkouts bake CRLF into the image and the
shebang fails with "no such file or directory".