21 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Documentation-First Approach
IMPORTANT: This project has comprehensive documentation in the docs/ directory (100+ files). When handling requests:
-
Use Documentation Instead of Context: Always reference the relevant documentation files instead of repeating information in responses. This prevents filling up context windows with redundant information.
-
Point to Documentation: When answering questions, provide the path to the relevant documentation file (e.g., "See
docs/04-frontend/architecture.mdfor details on frontend architecture"). -
Read Documentation When Needed: Use the Read tool to check documentation files when you need specific information about:
- Architecture:
docs/02-architecture/ - Backend:
docs/03-backend/ - Frontend:
docs/04-frontend/ - Game Service:
docs/05-game-service/ - API Reference:
docs/06-api-reference/ - Design System:
docs/07-design-system/ - Development:
docs/08-development/ - Deployment:
docs/09-deployment/ - Features:
docs/10-features/ - Diagrams:
docs/11-diagrams/ - Reference:
docs/12-reference/
- Architecture:
-
Update Documentation After Changes: After completing any feature, enhancement, bug fix, or significant code change, ALWAYS ask the user:
"I've completed the changes. Should I update the relevant documentation in the
docs/folder to reflect these changes?"Then update the appropriate documentation files to keep them in sync with the codebase.
Quick Documentation Reference
For Common Questions, Point Users/Read From:
| Question | Documentation File |
|---|---|
| How do I set up the project? | docs/08-development/setup-guide.md |
| What commands are available? | docs/08-development/commands.md |
| How does authentication work? | docs/10-features/authentication-authorization.md |
| What are all the API endpoints? | docs/06-api-reference/README.md |
| What's the database schema? | docs/12-reference/database-schema-reference.md |
| How do components work? | docs/04-frontend/components/component-overview.md |
| How do I deploy? | docs/09-deployment/README.md |
| What's the architecture? | docs/02-architecture/system-architecture.md |
| Design system/theming? | docs/07-design-system/theme-system.md |
| Common errors/pitfalls? | docs/08-development/common-pitfalls.md and "Common Pitfalls" section in CLAUDE.md |
| TypeScript types? | docs/12-reference/type-definitions.md |
| Environment variables? | docs/09-deployment/environment-variables.md |
| How to add new API endpoints? | "Frontend API Guidelines" section in CLAUDE.md |
Project Overview
Flashpoint Web is a self-hosted web application for browsing and playing games from the Flashpoint Archive. The project is a monorepo containing three independent services:
- backend: REST API server (Express/TypeScript, port 3100)
- frontend: React web UI (Vite/React/TypeScript, port 5173)
- game-service: Game content proxy and ZIP server (Express/TypeScript, ports 22500/22501)
The architecture separates concerns: the backend handles game metadata and user management via SQLite databases, the game-service serves actual game files and handles proxying, and the frontend provides the user interface.
Development Commands
Monorepo (Root Level)
# Install all dependencies for all services
npm run install:all
# Start all services in development mode (concurrent)
npm run dev
# Start individual services
npm run dev:backend
npm run dev:frontend
npm run dev:game-service
# Build all services
npm run build
# TypeScript type checking across all services
npm run typecheck
# Clean all build artifacts and dependencies
npm run clean
Backend
cd backend
# Development server with hot reload (tsx)
npm run dev
# Build TypeScript to dist/
npm run build
# Run production build
npm start
# TypeScript type checking only
npm run typecheck
# Lint code
npm run lint
# Run tests (vitest)
npm test
Frontend
cd frontend
# Development server with HMR (Vite)
npm run dev
# Build for production (outputs to dist/)
npm run build
# Preview production build locally
npm run preview
# TypeScript type checking only
npm run typecheck
# Lint code
npm run lint
# Copy Ruffle emulator files to public/ (runs automatically on postinstall)
npm run copy-ruffle
Game Service
cd game-service
# Development server with hot reload
npm run dev
# Build TypeScript
npm run build
# Run production build
npm start
# TypeScript type checking
npm run typecheck
Docker
# Build all services
docker-compose build
# Start all services in background
docker-compose up -d
# View logs
docker-compose logs -f
docker-compose logs -f backend
docker-compose logs -f game-service
docker-compose logs -f frontend
# Stop all services
docker-compose down
# Rebuild and restart
docker-compose up -d --build
Set FLASHPOINT_HOST_PATH environment variable to point to your Flashpoint installation before running Docker commands.
Architecture
Backend Service (Port 3100)
The backend is a REST API built with Express and TypeScript. It manages two separate SQLite databases:
-
Flashpoint Database (
flashpoint.sqlite): Read-only access to the official Flashpoint game metadata database. The DatabaseService watches this file for changes and hot-reloads when the Flashpoint Launcher updates it. -
User Database (
user.db): Application-specific database for user accounts, roles, permissions, playlists, favorites, play tracking, and authentication.
Key architectural patterns:
- Service layer pattern: Business logic in
src/services/, routes insrc/routes/ - JWT-based authentication with role-based access control (RBAC)
- File watching: Automatically detects changes to flashpoint.sqlite from the Flashpoint Launcher
- Proxy delegation: Backend proxies game file requests to game-service instead of serving them directly
Important services:
DatabaseService: Manages connection to flashpoint.sqlite with hot-reload supportUserDatabaseService: Manages user.db with schema migrationsGameService: Game metadata queries from flashpoint.sqliteAuthService: User authentication and JWT token managementPlayTrackingService: Tracks user play sessions with cleanup of abandoned sessions
Frontend Service (Port 5173)
React 18 single-page application using modern patterns:
Tech stack:
- Vite for build tooling and dev server
- React Router for routing
- TanStack Query (React Query) for server state management
- Zustand for client-side UI state
- Tailwind CSS for styling
- Ruffle emulator for Flash content
State management approach:
- Server state (games, playlists, user data): TanStack Query with caching
- UI state (sidebar visibility, view modes): Zustand stores
- URL state (filters, search, pagination): React Router search params
- Auth state: Zustand store (
store/auth.ts) with localStorage persistence
Component organization:
components/: Reusable components organized by domain (auth, library, player, playlist, etc.)views/: Top-level page componentslib/api.ts: Centralized API client with typed endpoints
Player implementation:
- Flash games: Ruffle WebAssembly emulator embedded in iframe
- HTML5 games: Served directly via game-service proxy
- Animations: Separate view with same player infrastructure
Frontend API Guidelines:
CRITICAL: Always use the authenticated axios instance for backend API calls.
- Centralized API client: All backend API calls MUST go through
frontend/src/lib/api.ts - Never use raw
fetch()for backend endpoints - it bypasses authentication - Axios interceptors: The
apiinstance automatically addsAuthorization: Bearer <token>headers - Pattern to follow: Create typed API methods in appropriate objects (e.g.,
gamesApi,authApi,githubApi)
Example (CORRECT):
// In frontend/src/lib/api.ts
export const githubApi = {
getStarCount: async (): Promise<{ stars: number }> => {
const { data } = await api.get<{ success: boolean; data: { stars: number } }>('/github/stars');
return data.data;
}
};
// In component
import { githubApi } from '@/lib/api';
const result = await githubApi.getStarCount(); // ✅ Authenticated
Example (WRONG):
// In component - DO NOT DO THIS
const response = await fetch('/api/github/stars'); // ❌ No authentication!
Why this matters:
- Maintenance mode blocks unauthenticated requests with 503
- Protected endpoints require JWT tokens
- Axios interceptors handle token refresh automatically
- Consistent error handling across all API calls
When adding new backend endpoints:
- Create the backend route in
backend/src/routes/ - Add the typed API method to
frontend/src/lib/api.ts - Use the API method in components (never raw
fetch()) - Test with maintenance mode enabled to ensure authentication works
Game Service (Ports 22500, 22501)
Independent Node.js service that replaces the original Go-based Flashpoint Game Server. Runs two HTTP servers:
-
HTTP Proxy Server (22500): Serves legacy web content with fallback chain:
- Local htdocs (
D:/Flashpoint/Legacy/htdocs) - Game data directory
- ZIP archives (via zip-manager)
- External CDN fallback (infinity.flashpointarchive.org)
- Local file cache for downloaded content
- Local htdocs (
-
GameZip Server (22501): Mounts and serves files from ZIP archives in
Data/Games/without extraction using node-stream-zip
Key features:
- MIME type detection for 199+ file types
- CORS headers for cross-domain game content
- Streaming for large files
- External CDN fallback with caching
- File path normalization and URL decoding
Environment Configuration
Backend (.env)
Required variables:
FLASHPOINT_PATH=D:/Flashpoint
FLASHPOINT_DB_PATH=D:/Flashpoint/Data/flashpoint.sqlite
FLASHPOINT_HTDOCS_PATH=D:/Flashpoint/Legacy/htdocs
FLASHPOINT_IMAGES_PATH=D:/Flashpoint/Data/Images
FLASHPOINT_LOGOS_PATH=D:/Flashpoint/Data/Logos
FLASHPOINT_PLAYLISTS_PATH=D:/Flashpoint/Data/Playlists
FLASHPOINT_GAMES_PATH=D:/Flashpoint/Data/Games
# Game service URLs (separate service)
GAME_SERVICE_PROXY_URL=http://localhost:22500
GAME_SERVICE_GAMEZIP_URL=http://localhost:22501
# Frontend origin for CORS
DOMAIN=http://localhost:5173
Optional: JWTSECRET, REDIS_ENABLED, LOG_LEVEL, RATE_LIMIT*
See backend/.env.example for complete configuration options.
Game Service (.env)
PROXY_PORT=22500
GAMEZIPSERVER_PORT=22501
FLASHPOINT_PATH=D:/Flashpoint
FLASHPOINT_HTDOCS_PATH=D:/Flashpoint/Legacy/htdocs
FLASHPOINT_GAMES_PATH=D:/Flashpoint/Data/Games
EXTERNAL_FALLBACK_URLS=http://infinity.flashpointarchive.org/Flashpoint/Legacy/htdocs,http://infinity.unstable.life/Flashpoint/Legacy/htdocs/
LOG_LEVEL=info
Frontend
Environment variables are injected at build time via Vite:
VITE_API_URL=http://localhost:3100
Vite proxy configuration in vite.config.ts handles /api/* and /proxy/* routing to backend.
Database Schema
Flashpoint Database (flashpoint.sqlite)
Read-only database from Flashpoint project. Key tables:
game: Main game metadata (id, title, platform, library, developer, publisher, etc.)platform: Gaming platformstag: Tags and categoriesgame_tags_tag: Many-to-many relationship between games and tagsplaylist: User playlistsplaylist_game: Playlist entries
Important: This database is managed by the Flashpoint Launcher. The backend watches for file changes and automatically reloads connections when updates occur.
User Database (user.db)
Application-specific SQLite database. Schema managed via migrations in backend/src/migrations/. Tables include:
users: User accounts with hashed passwords (bcrypt)roles: RBAC roles (admin, moderator, user, guest)role_permissions: Permissions assigned to rolesuser_roles: User role assignmentsuser_playlists: User-created playlistsuser_favorites: Favorited games per userplay_sessions: Game play tracking with duration, completion statussystem_settings: Global system-wide configuration (auth, app, metadata, features, game, storage, rate limiting)
Migrations run automatically on server startup via UserDatabaseService.
Authentication & Authorization
The application uses JWT-based authentication with RBAC:
Auth flow:
- User logs in via
/api/auth/loginwith username/password - Backend validates credentials, returns JWT access token + refresh token
- Frontend stores tokens in localStorage and Zustand auth store
- All subsequent API requests include JWT in Authorization header
- Backend middleware validates JWT and checks permissions
Permission system:
- Permissions are strings like "games.play", "playlists.create", "users.manage"
- Roles bundle permissions together
- ProtectedRoute component in frontend enforces permission requirements
- Backend RBAC middleware validates permissions on protected endpoints
Guest mode:
- Users can browse games without authentication
- Playing games requires authentication and "games.play" permission
Maintenance mode:
- Controlled via
app.maintenance_modesetting insystem_settingstable - When enabled, blocks all non-admin users from accessing endpoints (returns 503)
- Admin users identified by
settings.updatepermission bypass maintenance mode - Minimal public paths always accessible:
/health,/api/auth/login,/api/auth/refresh,/api/settings/public,/proxy/* - Implemented in
backend/src/middleware/maintenanceMode.ts - Depends on
softAuthmiddleware to populatereq.userbefore checking permissions
Permission caching:
- User permissions cached for 5 minutes via
PermissionCache(in-memory) - Cache automatically invalidated when user roles or role permissions change
- Cache cleared on server restart
- Debug issues by checking backend logs for
[Maintenance]entries
Testing
Backend includes vitest configuration. Run tests with:
cd backend
npm test
Frontend testing setup TBD.
Important Implementation Notes
Database Access Patterns
- Never use write operations on flashpoint.sqlite - it's read-only
- User database writes use prepared statements with transactions for data consistency
- DatabaseService includes file watcher to detect external changes from Flashpoint Launcher
- Use BetterSqlite3 synchronous API (not async wrapper) for better performance
Game File Serving
Game files are not served by the backend. The flow is:
- Frontend requests game launch data from backend
- Backend returns launch configuration pointing to game-service URLs
- Frontend creates player iframe pointing to game-service (port 22500)
- Game-service handles all file serving, proxying, and ZIP mounting
This separation prevents backend from blocking on large file transfers and allows independent scaling.
Ruffle Integration
Ruffle emulator files are copied from npm package to frontend/public/ruffle/ during npm install (postinstall hook). The player component loads Ruffle from /ruffle/ruffle.js and creates instances for Flash content.
Scale modes supported: exactfit, noborder, showall (default), noscale
Play Session Tracking
Play sessions have three states:
- Active: User is playing
- Completed: User finished normally
- Abandoned: Session older than 24 hours without completion
Background job runs every 6 hours to clean up abandoned sessions.
CORS Configuration
- Backend CORS origin must match frontend URL
- Game-service enables CORS for all origins (cross-domain game content)
- Vite proxy handles development routing to avoid CORS issues
Common Pitfalls
- Flashpoint path not found: Verify
FLASHPOINT_PATHin .env points to valid Flashpoint installation - Database locked errors: Flashpoint Launcher may lock flashpoint.sqlite - close it before migrations
- Port conflicts: Ports 3100, 5173, 22500, 22501 must be available
- Game files not loading: Ensure game-service is running before backend
- JWT secret in production: Change default JWT_SECRET in production environments
- Ruffle files missing: Run
npm run copy-rufflein frontend if Ruffle doesn't load - 503 errors in maintenance mode: If admin users get 503 errors, check if components are using raw
fetch()instead of the authenticatedapiinstance fromlib/api.ts. The axios interceptors automatically add authentication headers. - Unauthenticated API calls: Never use raw
fetch()for backend API endpoints. Always add new endpoints tofrontend/src/lib/api.tsand use the typed API methods. See "Frontend API Guidelines" section above.
Recent Changes & Lessons Learned
2026-01-28: Maintenance Mode Admin Bypass Fix
Issue: Admin users were getting 503 errors when accessing /api/github/stars while maintenance mode was enabled.
Root Cause: The GitHubButton component was using raw fetch() instead of the authenticated axios instance, so requests had no Authorization header. The maintenance mode middleware blocked these unauthenticated requests.
Fix:
- Added
githubApi.getStarCount()method tofrontend/src/lib/api.ts - Updated
GitHubButton.tsxto use the authenticated API method - The axios instance automatically adds JWT token via request interceptor
Key Lesson: NEVER use raw fetch() for backend API calls. Always use the centralized api instance from lib/api.ts which handles authentication, token refresh, and error handling automatically.
Verification Steps Taken:
- Checked database - confirmed admin role has
settings.updatepermission ✅ - Tested backend directly - confirmed maintenance mode allows admin access ✅
- Identified frontend was making unauthenticated requests ❌
- Fixed component to use authenticated axios instance ✅
Prevention: See "Frontend API Guidelines" section for the correct pattern. When adding new API endpoints, always:
- Add typed method to appropriate API object in
lib/api.ts - Import and use the API method in components
- Test with maintenance mode enabled
2026-01-29: Game Service Directory Traversal Protection
Issue: Game service lacked comprehensive path validation, potentially allowing directory traversal attacks through crafted URLs.
Implementation: Created centralized security utilities in game-service/src/utils/pathSecurity.ts:
-
Path Sanitization & Validation:
sanitizeAndValidatePath()- Ensures paths stay within allowed directories- Platform-aware (Windows case-insensitive, Unix case-sensitive)
- Validates resolved paths against base directories
-
URL Path Sanitization:
sanitizeUrlPath()- Detects null bytes, URL-encoded traversal, backslash attacks- Runs before path resolution for early detection
-
Applied to both servers:
- Legacy Server: URL sanitization + path validation before all file access
- GameZip Server: URL sanitization in file requests
Test Coverage: 17 comprehensive test cases (all passing)
Security Guarantees:
- ✅ Blocks
../../../etc/passwdattempts - ✅ Blocks absolute path escapes (
/etc/passwd) - ✅ Blocks URL-encoded traversal (
..%2F,..%5C) - ✅ Blocks null byte injection (
file.swf\0.txt) - ✅ Blocks backslash traversal (
..\..\..\) - ✅ All blocked attempts logged with full details
Key Lesson: Always validate that resolved file paths stay within allowed base directories. Use path.resolve() and check the resolved path starts with the base directory. Never trust user input, even after URL decoding.
Documentation:
- Detailed guide:
docs/13-security/directory-traversal-protection.md - Updated:
docs/12-reference/security-measures.md
Phase: Phase 1 (Security) - Critical priority fix from comprehensive project review
2026-01-28: Database Migration Consolidation
Change: Consolidated 16 separate migration files into 2 files:
001_initialize_schema.sql: All table definitions002_seed_default_data.sql: All seed data (roles, permissions, settings)
Added: Migration registry system in bootstrap.sql to track applied migrations with checksums and execution times.
Benefits:
- Cleaner migration directory
- Industry-standard migration tracking (similar to Flyway, Knex)
- Backward compatibility with existing databases
- All archived migrations saved in
migrations/archived/for reference
Source Code References
- Flashpoint Launcher source code: D:\Repositories\Community\launcher
- Flashpoint Game Server: D:\Repositories\Community\FlashpointGameServer
Flashpoint App Reference: D:\FP_Data\Flashpoint
Documentation Maintenance
After ANY code change, always:
- Verify no build errors:
npm run typecheckandnpm run build - Check if documentation needs updates
- Ask the user if relevant documentation should be updated
- Update the following docs as needed:
- Architecture changes →
docs/02-architecture/ - New/modified API endpoints →
docs/06-api-reference/ - Database schema changes →
docs/12-reference/database-schema-reference.md - New features →
docs/10-features/ - Component changes →
docs/04-frontend/components/ - Configuration changes →
docs/03-backend/configuration.mdordocs/09-deployment/environment-variables.md
- Architecture changes →
Documentation is a first-class concern - keeping it current ensures the project remains maintainable.