- Fix platform filter to properly trim strings in array format
- Remove redundant breadcrumb context from random game navigation
- Add comprehensive tests for useRandomGame hook
- Reorder mock handlers to place static routes before parameterized
- Add explicit type annotation to breadcrumb items array
- Add glassmorphism effect to dialog, sheet, popover, dropdown, and
alert-dialog components with backdrop blur and subtle borders
- Improve sidebar highlighting by prioritizing breadcrumb context over
URL prefix matching for accurate section detection
- Add parent breadcrumb support for nested navigation (e.g., Flashpoint
Playlists > Playlist Name > Game)
- Replace "Back to" links with consistent Breadcrumbs component in
playlist detail views
- Add platform filter to random game endpoint (limit Lucky button to
Flash/HTML5)
- Remove unused role priority field from schema, types, and UI
- Sort roles table with system roles pinned at top, show "System Role"
badge instead of disabled actions
Introduce URL-based navigation structure where games retain their
section context (/flash/:id, /html5/:id, /animations/:id, /browse/:id).
Key changes:
- Shorten route paths: /flash-games -> /flash, /html5-games -> /html5
- Add sectionRoutes.ts with centralized section configuration
- Update sidebar active state detection to track section routes
- Enhance Breadcrumbs component with icon support and sticky styling
- Derive breadcrumb context from URL path, falling back to state
- Remove unused imports across backend files
- Add progress bar indicator to toast notifications showing auto-dismiss
countdown, pauses on hover for improved UX
- Add close button to toasts with proper styling per toast type
- Enhance "Feeling Lucky" button with bounce animation, ping effect,
and smooth hover/active transitions
- Update favicon to official Flashpoint branding
- Convert Tailwind config to use ESM imports instead of require()
- Replace generic Error with AppError for proper HTTP status codes
in SystemSettingsService validation (400 for client errors, 404
for missing settings, 500 for schema errors)
- Add useMediaQuery hook for reactive viewport tracking
- Replace window.innerWidth checks with useMediaQuery in Sidebar
to properly react to viewport changes
- Make currentPassword optional in ChangePasswordData type to
support admin password resets
- Integrate DownloadRegistry into concurrent download limit
and clean up timers on removal/clear
- Wrap MetadataSyncService batch deletes in single transaction
- Always re-throw checksum errors in RuffleService to prevent
verification bypass
- Fix system-settings early return skipping second job update
when first cron expression is invalid
- Increase DatabaseService hot-reload grace period to 30s
- Add permission guard to metadata sync status endpoint
- Use encodeURIComponent for game launch content URLs
- Clamp limit params to minimum of 1 in jobs routes
- Use non-null assertion for authenticated user permissions
- Fix ConfirmDialog not calling onCancel on overlay dismiss
- Guard FormattedDate against invalid date strings
- Add breadcrumbContext to GameListItem memo comparison
- Fix MetadataUpdateCard stale closure in interval callback
- Move Dockerfile healthcheck to docker-compose for flexibility
- Fix build:analyze script to set VITE_ANALYZE env var
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.
- Move health check from Dockerfile to docker-compose
for easier override
- Remove unused VITE_API_URL build arg from frontend
Dockerfile
- Simplify build:analyze and check-bundle scripts
- Document PUID/PGID container permissions in
.env.example
mock data with actual API contracts
- Normalize all `cn` imports to `@/lib/utils` across
30+ UI and component files
- Replace relative imports with `@/` alias in auth,
roles, users, stats, and layout components
- Remove redundant `as` type assertions in playlist
routes (Zod parse already returns typed data)
- Replace `any` with proper types in FallbackCache,
mock handlers, and validateIcons
- Use `type` import for type-only imports
- Add type-safe `.filter(Boolean)` with type guard
in Sidebar nav item arrays
- Update MSW mock data to match actual API shape:
numeric IDs, `role` field, HTTP-only cookie auth,
paginated response format, and settings schema
- Add JSDoc to AuthTokens clarifying cookie-based
token storage
- Replace console.* calls with logger in icon
validation
- Ignore SQLite WAL files in .gitignore
- Demote per-request game service logs to debug level
- Add reentrant guard to DatabaseService.syncAndReload
- Enable WAL mode + PRAGMA tuning for user.db
- Align CachedSystemSettingsService cleanup to 90s interval
- Extract CACHE_TTL constant in GameSearchCache
- Add PerformanceMetrics hourly cleanup with .unref()
- Fix migration timing to measure exec only, type result
- Add parseInt NaN fallback for OTEL metrics interval
- Make config object readonly with `as const`
- Add error reporting to all error boundary components
- Replace hardcoded colors with theme tokens (destructive,
primary, amber) for dark mode compatibility
- Use `??` instead of `||` for config/setting defaults
- Replace `any` with `unknown` in asyncHandler types
- Add `Identifiable` constraint to createQueryHook generics
- Fix timeout leaks with useRef and componentWillUnmount
- Add `.catch()` on fire-and-forget promises
- Deduplicate theme sync logic into `maybeSyncToServer`
- Extract `resolveActualMode` helper in theme store
- Add Zustand `useShallow` selector to CardSizeControl
- Add axios module augmentation for custom request config
- Set 30s default timeout on API client
- Deduplicate toast IDs to prevent session-expired spam
- Fix TOAST_REMOVE_DELAY from 1000000ms to 1000ms
- Fix use-toast useEffect dependency array
- Remove redundant try/catch in asyncHandler-wrapped routes
- Lock body scroll when mobile sidebar is open
- Fix route ordering: static before wildcard in App.tsx
- Quote background-image URLs in LazyBackgroundImage
- Remove duplicate onCancel call in ConfirmDialog
- Add deferred TODO.md for architectural improvements
- Clear isMaintenanceMode on logout, stop persisting it
route handlers
- DRY timeRange validation into shared parseTimeRange()
- Add jobId format validation, cron schedule validation,
and date format validation on route inputs
- Add auth middleware to update check/metadata endpoints
- Harden sync start with atomic tryStartSync() to prevent
race conditions
- Add symlink escape prevention and SVG CSP headers to
proxy routes
- Cache public settings in CachedSystemSettingsService
- Fix MetadataSyncService: validate allowed hosts (SSRF),
batch deletes to avoid SQL variable limits, use ?? over
|| for nullable fields
- Remove unnecessary async from synchronous DB methods in
GameDatabaseUpdater
- Add path traversal check in makeRelativePath()
- Prevent setting creation via set() — only allow updates
- Remove unused getJobStatus/getAllJobs, lastRunTimes map
- Strip internal paths from getSystemInfo() response
- Sanitize update error messages to avoid leaking stderr
- Add job log cleanup interval with unref()
- Replace hardcoded colors with theme tokens, fix React
Fragment keys, use getErrorMessage() utility
- Convert MetadataUpdateCard ref to state for reactivity
- Use ?? instead of || for config defaults across settings
Backend:
- Add Zod UUID validation for game/playlist route params
- Fix parseInt NaN fallback in play-tracking query params
- Add password max-length (128) and self-delete prevention
- Whitelist sort direction to prevent SQL injection in
FavoritesService
- Use ?? over || for yearFrom/yearTo and presentOnDisk
- Fix TOCTOU in updateUserStats with atomic UPSERT subqueries
- Encode game content URL, add 500MB download size limit
- Fix DownloadManager promise double-settlement with guard
- Add optionalAuth to public playlist routes
- Coerce isActive to boolean in UserService responses
- Delete user_stats row on user deletion
- Use single AuthService instance in UserService
- Fix SQL quoting (double → single quotes)
Frontend:
- Extract shared chart-utils (CHART_COLORS, formatPlaytimeCompact,
truncateTitle, getCSSVar) from 3 chart components
- Replace hardcoded colors with theme-aware CSS variables in
PlaytimeChart and TopGamesChart
- Wrap GameListItem in React.memo with custom comparator
- Move Statistics type to API layer, re-export from hook
- Type statisticsApi return value
- Add maxLength to playlist title/description inputs
- Fix ChangePasswordDialog: skip current password for admin
resets, remove min-length on current password field
- Add max-length (128) to password fields across forms
- Validate roleId as positive integer in UserForm
- Replace hardcoded Tailwind colors with theme tokens
(text-destructive, bg-destructive/10, border-destructive)
- Fix tag separator from semicolon to comma in browse layout
- Guard division-by-zero in GamesDistributionChart
- Simplify UserTable delete handler, remove duplicate toast
- Fix unused import (toast) in UserTable
- Add permission escalation check to role create/update endpoints
so users cannot assign permissions they do not possess
- Replace permissive `settings.view` with `settings.read` across
cache, health, and metrics routes
- Require authentication and `playlists.create` for community
playlist downloads (was `optionalAuth`)
- Add `X-Content-Type-Options: nosniff` and `X-Frame-Options`
headers to game content responses
- Enable Content-Security-Policy via helmet with restrictive
default-src and frame-ancestors directives
- Set `server.maxConnections = 500` as defense-in-depth limit
- Delete role atomically with its permissions in a transaction
and invalidate permission cache afterward
- Unify registration error messages to prevent username/email
enumeration
- Disable permission management for system roles in RoleTable
- Encode share tokens and game IDs in frontend API URLs
- Add `optionalAuth` to statistics route
- Remove unused `rateLimitAuth` export
- 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
- Clear force-kill timeout on process exit to prevent leaks
- Support LF-only line endings in CGI output parsing
- Add proper timeout handling for request body collection
- Save/restore env vars in tests to prevent pollution
- 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.