The index was scraped from the Flashpoint Datahub wiki on every request.
That page now sits behind a Cloudflare managed challenge that no plain
HTTP client can pass, so the endpoint answered 500 for every caller.
Ship the list instead, extracted from a saved copy of the page into
data/community-playlists.json: 104 playlists across 9 categories. Only
entries whose download URL is on the allowlist are kept, so everything
the modal offers can also be imported. That drops the Old Default
Playlists section, hosted off the allowlist and still in the
pre-Flashpoint 10 format.
The modal now fetches only while open rather than on every render of the
playlists page, and shows when the snapshot was captured. Drops cheerio,
whose only consumer was the scrape.
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.
- 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