Commit Graph
20 Commits
Author SHA1 Message Date
branderson 5c5b11722d docs(streaming): document the session manager settings 2026-10-11 01:01:27 -07:00
Darkraise 0ce39da805 docs(ruffle): document the persisted install path 2026-08-13 17:31:04 +07:00
Darkraise 74aa0cf067 feat(startup): serve the app while the server warms up
On a network share the database copy and cache pre-warm run for minutes,
and the listener only opened after all of it, so anyone who loaded the
page during that window got a connection error.

Open the port immediately after registering the static frontend, and
hold every route that needs the database behind a gate that answers 503
with a machine-readable marker and the current startup phase. The SPA
shell needs nothing the gate blocks, so the frontend renders a "server
is starting" screen on the login page's own background, polls /health,
and reloads once the server reports ready.

Move the database copy out of the entrypoint: it does the same freshness
check the application does, and doing it before node starts kept the port
closed for the longest part of startup. Distinguish a dead server from a
slow one by requiring the backend's own marker, since a proxy in front of
a stopped backend still answers.
2026-08-12 14:05:14 +07:00
Darkraise 9a32471f61 fix(docker): mount Flashpoint read-write by default
The shipped compose mounted the Flashpoint directory read-only, so every write
the app makes to it failed: recording downloaded state in flashpoint.sqlite,
saving downloaded game data under Data/Games, and metadata sync. The Downloaded
page marked nothing and the reconciler warned on every boot.

Drop :ro from both compose files and the README's snippet, and correct the docs
that described the mount as read-only.
2026-08-12 03:12:57 +07:00
Darkraise 13c4ccb19e perf(db): run heavy reads in worker threads, raise I/O parallelism
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.
2026-08-12 02:18:44 +07:00
Darkraise 3ba60aedf9 refactor(docker): build and ship a single API + UI image
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".
2026-08-11 16:02:06 +07:00
Darkraise bd2514f1f7 chore(config): centralize and document environment variables
- Read game paths and NODE_ENV through the config module in
  gamezipserver instead of process.env directly
- Harden SQLite cache/mmap parsing with isNaN checks so invalid
  values fall back to defaults instead of NaN
- Pass auth, rate-limit, application, and OpenTelemetry variables
  through both docker-compose files with documented defaults
- Expand env var documentation and .env examples to match
2026-08-11 16:02:06 +07:00
Darkraise cbc94db79c feat: add shared download registry, type safety, and test infra
feat: add shared download registry, type safety, and test infra

Add DownloadRegistry to prevent duplicate downloads between
DownloadManager and GameZipServer. Consolidate PreferencesService
(delete game/services copy, add compatibility aliases). Replace
`any` defaults with `unknown` in DatabaseService/UserDatabaseService
and add explicit type parameters to ~40 query call sites. Add
vitest config, test setup, test helpers, and unit tests for auth
middleware, RBAC, pagination, and time range utils. Enable
strictPropertyInitialization in backend tsconfig. Add SHA-256
checksum verification to RuffleService updates. Harden Docker
Compose with log rotation, resource limits, and bridge network.
Fix NODE_ENV default in dev compose, exclude guest userId from
activity logs, add activity logging to password change, guard
nullable permission descriptions, and align frontend Game/Permission
types with actual API shape.
2026-02-11 14:43:00 +07:00
Darkraise ee9c2a296f fix: harden security, improve type safety, and clean up code
- Pin DNS resolution to prevent SSRF rebinding between
  check and connect (GameDataDownloader)
- Fail-closed on realpath failure in GameZipServer
- Zip Slip validation before extraction (RuffleService)
- Escape LIKE wildcards in ActivityService queries
- Sanitize sort order to prevent SQL injection
- Whitelist sort order instead of passing raw user input
- Add headersSent guard to global error handler
- Remove cookie from CGI allowed headers, sanitize
  content-type against null/CR/LF injection
- Revoke refresh tokens on password change and user
  deactivation
- Wrap role/user uniqueness checks in transactions
  (TOCTOU fix)
- Fix concurrent ZIP mount race with deferred promise
  and double-close prevention via _manuallyClosed flag
- Handle client disconnect in collectRequestBody
- Tighten nginx CSP, repeat headers in location blocks,
  hide server version, pin nginx:1.27-alpine
- Fix health check endpoint path in backend Dockerfile
- Use wget instead of curl for frontend health checks
- Replace any types with proper types across backend
  and frontend
- Replace || with ?? for config/form defaults
- Replace hardcoded Tailwind colors with theme tokens
- Replace console.log/error with logger in ErrorReporter
  and ChartErrorBoundary
- Conditionally render AddToPlaylistModal to avoid
  hidden instances
- Add NaN guards for parseInt in GameBrowseLayout
- Standardize query key for publicSettings
- Simplify README to Docker-first quickstart with wiki
  link
- Fix license to GPL-3.0 in backend package.json
- Slow down particle animation speed
- Add Zod max-length limits on playlist/error schemas
- Ensure positive limit in parsePaginationQuery
- Validate settings category param against allowlist
- Fix static/parameterized route ordering in favorites
- Add asyncHandler to game-zip wildcard route
- Close readline/stream on error in getRecentErrors
- Add .catch() on fire-and-forget syncAndReload
- Fix LazyBackgroundImage cleanup on unmount
- Store NetworkStatusIndicator timeout in ref
- Add Flash/SWF condition to random game query
2026-02-09 09:17:18 +07:00
Darkraise 96cf4a5b00 feat: revamp the game serving feature 2026-02-07 23:36:45 +07:00
Darkraise 8af54de927 feat: remove edition settings, use version.txt instead 2026-02-03 22:33:25 +07:00
Darkraise 9e32d90c46 feat: add Flashpoint edition support (Infinity/Ultimate)
- Add edition config to backend, .env.example, and Docker
- Gate metadata sync and update checks on edition
- Conditionally select logoPath/screenshotPath in queries
- Add edition selector to General Settings (admin only)
- Hide metadata sync UI for Ultimate in Update Settings
- Add `metadata.flashpoint_edition` system setting with
  migration seed data
2026-02-03 16:33:33 +07:00
Darkraise 39f9957670 feat: add nginx proxy for game service in Docker
Routes game content through nginx instead of requiring direct
localhost:22500 access from browser. This enables proper Docker
networking where containers communicate internally.

Changes:
- Add /game-proxy and /gamezip-proxy locations to nginx config
- Add GAME_SERVICE_EXTERNAL_URL config for browser-facing URLs
- Update games route to use external URL config
- Add GAME_SERVICE_HOST env var to frontend container
- Add game-service dependency to frontend in docker-compose
2026-02-02 23:26:52 +07:00
Darkraise ee30b40a03 fix: docker permission 2026-02-02 16:43:17 +07:00
Darkraise b5feb04604 refactor: simplify Docker config and add db optimizations
- Consolidate docker-compose files: dev.yml builds locally, main uses
  pre-built images; remove redundant prod.yml and images.yml
- Add docker-entrypoint.sh for database copy from network storage
- Add SQLite performance tuning (PRAGMA optimizations, mmap, cache)
- Add cache pre-warming for instant first page load
- Enhance logging with OTEL-compatible JSON format and trace context
- Simplify .env.example with clearer sections and fewer variables
- Remove EXTERNAL_IMAGE_URLS env var (auto-read from FP preferences)
- Hardcode container ports (use docker port mapping to customize)
- Add /api/errors/stats endpoint for admin dashboard
2026-02-02 15:43:31 +07:00
Darkraise f76f731efe chore: add config for logging and update dockerfile 2026-02-01 11:40:07 +07:00
Darkraise 9e9a116f7f fix: frontend nginx 2026-02-01 11:14:02 +07:00
Darkraise e1a691e497 feat: share playlist feature 2026-01-31 17:29:07 +07:00
Darkraise 0a3272d613 feat: polish the features and enhance UI/UX 2026-01-23 00:49:12 +07:00
Darkraise c7b617cf08 feat: initial features 2026-01-16 23:58:21 +07:00