- Convert CachedSystemSettingsService to singleton pattern
- Add scheduled cleanup for login attempts (24h) and activity logs
- Add batch size limits (100) for favorites and playlist operations
- Enhance health check with game-service connectivity verification
- Add cache stampede prevention in GameSearchCache
- Fix N+1 query in RoleService.getRoles() with single JOIN query
- Consolidate domains migration into 001_complete_schema.sql
- Replace `any` types with `unknown` and explicit type parameters
- Extract MetadataUpdateCard and RuffleManagementCard components
- Fix race condition in PlayTrackingService duration calculation
fix: harden security and improve code quality across services
Add Zod validation schemas to backend routes, replace z.any() with
explicit types, and convert SQL string interpolation to parameterized
queries to prevent injection attacks.
Security improvements:
- Add SSRF protection with domain allowlist in CommunityPlaylistService
- Enforce JWT algorithm restriction and token type validation
- Sanitize error messages to prevent path leakage in game-service
- Add MIME type enforcement based on file extension, not server headers
- Add request timeouts with exponential backoff retry logic
- Prevent Flashpoint database writes (read-only enforcement)
Backend improvements:
- Use UPSERT for play tracking to avoid race conditions
- Add dispose() method to CachedSystemSettingsService for cleanup
- Use crypto.randomUUID() instead of custom UUID generator
- Batch fetch games in PlaylistService to fix N+1 query pattern
- Use centralized verifyPassword utility in UserService
Frontend improvements:
- Add PlayerErrorBoundary to prevent app crashes from Ruffle errors
- Add theme initialization script to prevent FOUC
- Add custom favicon.svg with Flashpoint branding
- Fix token refresh race condition with subscriber queue
- Add AbortSignal support to games search API
- Replace EventSource with fetch for authenticated SSE streams
- Improve error handling in UserForm and CreatePlaylistModal
- Stream large files instead of buffering in game-service
- Add max domain limit (50) to prevent unbounded growth
- Auto-promote next domain as default when deleting current
default inside a transaction
- Cache CORS allowed origins alongside domain cache
- Validate share URLs with URL constructor and restrict to
http/https protocols
- Track per-domain delete state to avoid disabling all buttons
- Prevent domain selector reset after manual user selection
- Refetch domains after delete to reflect auto-promoted default
- Replace inline require() calls with top-level config import
- Add debug/warn logging for swallowed catch blocks
Move cache state from module-level variables into instance
properties and use getInstance() to ensure a single shared
instance across routes and CORS configuration.
Replace `{condition && (<JSX>)}` pattern with
`{condition ? (<JSX>) : null}` across all frontend components.
This avoids potential falsy value rendering issues with
the logical AND operator.
- 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
Legacy Flash games sometimes emit URLs like /http:/domain.com/path
instead of /http://domain.com/path. Add normalization to detect
and fix these malformed URLs before parsing in both the proxy
handler and GameZip server.
The nginx envsubst command doesn't support the ${VAR:-default}
syntax. Move default values for BACKEND_HOST, BACKEND_PORT, and
GAME_SERVICE_HOST to docker-entrypoint.sh where they're properly
handled by bash before envsubst runs.
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
Remove logoPath and screenshotPath fields from Game interface
and database queries. Images are stored on disk with paths
derived from game ID (e.g., Logos/ab/cd/{game-id}.png).
- Add gameUtils.ts with path/URL derivation functions
- Update GameCard, GameListItem, GameDetailView, GamePlayerView
- Update GameService to exclude unused columns from queries
- Update tests with valid UUID format for image URL testing
- Update database schema docs to clarify image storage