diff --git a/.env.example b/.env.example index 019d641..9083283 100644 --- a/.env.example +++ b/.env.example @@ -105,7 +105,6 @@ ENABLE_CACHE_PREWARM=true # --------------------------------------------------------------------------- # Application # --------------------------------------------------------------------------- -# HOME_RECENT_HOURS=24 # ENABLE_CGI=false # Streamed play for games that can't run in a browser (Java, Shockwave, the # original Flash Player, Windows games). Set a token (e.g. `openssl rand -hex 32`) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2109efa..e539373 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1,5 @@ blank_issues_enabled: true contact_links: - - name: Ask a Question - url: https://github.com/darkraise/flashpoint-web/discussions - about: Use Discussions for questions, help, and general conversation - name: Flashpoint Archive url: https://github.com/FlashpointProject about: For issues with Flashpoint itself (not this web interface) diff --git a/CLAUDE.md b/CLAUDE.md index b08cec0..7232ae0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1193,13 +1193,8 @@ Before submitting code, verify: ## Environment ```bash -FLASHPOINT_PATH=D:/Flashpoint # Required - path to Flashpoint installation +FLASHPOINT_PATH=/path/to/flashpoint # Required - path to Flashpoint installation JWT_SECRET=your-secret # Required - random string for JWT signing DOMAIN=http://localhost:5173 # Optional - frontend URL for CORS LOG_LEVEL=info # Optional - debug, info, warn, error ``` - -## Source References - -- Flashpoint Launcher: `D:\Repositories\Community\launcher` -- Flashpoint Game Server: `D:\Repositories\Community\FlashpointGameServer` diff --git a/backend/.env.example b/backend/.env.example index de0e0e3..6e79ec5 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -22,7 +22,6 @@ BCRYPT_SALT_ROUNDS=10 # ENABLE_CGI=false # Misc -HOME_RECENT_HOURS=24 # --------------------------------------------------------------------------- # Database Performance Optimization (for network storage) diff --git a/backend/src/config.ts b/backend/src/config.ts index 71822bd..b3fc24e 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -167,8 +167,6 @@ export const config = { bcryptSaltRounds: parseInt(process.env.BCRYPT_SALT_ROUNDS || '', 10) || 10, - homeRecentHours: parseInt(process.env.HOME_RECENT_HOURS || '', 10) || 24, - // Session manager that streams games which can't run in a browser, such as // Java applets (flashpoint-ultimate-server). Streaming is off unless both are set. streamingApiUrl: process.env.STREAMING_API_URL || null, diff --git a/backend/src/routes/github.ts b/backend/src/routes/github.ts deleted file mode 100644 index a426283..0000000 --- a/backend/src/routes/github.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Router, Request, Response } from 'express'; -import { logger } from '../utils/logger'; -import { asyncHandler } from '../middleware/asyncHandler'; - -const router = Router(); - -// In-memory cache for GitHub star count (avoid hitting 60/hr unauthenticated rate limit) -let cachedStars: { count: number; fetchedAt: number } | null = null; -const CACHE_TTL_MS = 15 * 60 * 1000; - -// Proxy to avoid browser CORS issues with GitHub API -router.get( - '/stars', - asyncHandler(async (_req: Request, res: Response) => { - if (cachedStars && Date.now() - cachedStars.fetchedAt < CACHE_TTL_MS) { - return res.json({ - success: true, - data: { stars: cachedStars.count }, - }); - } - - const repoUrl = 'https://api.github.com/repos/darkraise/flashpoint-web'; - - const response = await fetch(repoUrl, { - headers: { - Accept: 'application/vnd.github.v3+json', - 'User-Agent': 'Flashpoint-Web-App', - }, - }); - - if (!response.ok) { - // If rate-limited and we have a stale cache, return it - if (cachedStars) { - logger.debug('[GitHub] Rate limited, returning stale cache'); - return res.json({ - success: true, - data: { stars: cachedStars.count }, - }); - } - throw new Error(`GitHub API error: ${response.status}`); - } - - const data = (await response.json()) as { stargazers_count: number }; - - cachedStars = { count: data.stargazers_count, fetchedAt: Date.now() }; - - res.json({ - success: true, - data: { - stars: data.stargazers_count, - }, - }); - }) -); - -export default router; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index d8c242c..32524e9 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -25,7 +25,6 @@ import ratingsRouter from './ratings'; import gameSavesRouter from './game-saves'; import discoveryRouter from './discovery'; import ruffleRouter from './ruffle'; -import githubRouter from './github'; import domainsRouter from './domains'; import healthRouter from './health'; import cacheRouter from './cache'; @@ -40,7 +39,6 @@ export function setupRoutes(app: Express): void { app.use('/api/settings/auth', authSettingsRouter); app.use('/api/settings', systemSettingsRouter); app.use('/api/ruffle', ruffleRouter); - app.use('/api/github', githubRouter); app.use('/api/domains', domainsRouter); app.use('/api/jobs', jobsRouter); app.use('/api/users', usersRouter); diff --git a/backend/src/routes/system-settings.ts b/backend/src/routes/system-settings.ts index b3afd0e..1044173 100644 --- a/backend/src/routes/system-settings.ts +++ b/backend/src/routes/system-settings.ts @@ -80,11 +80,6 @@ router.get( Record > = systemSettings.getPublicSettings(); - if (!publicSettings.app) { - publicSettings.app = {}; - } - publicSettings.app.homeRecentHours = config.homeRecentHours; - // Inject edition/version from config (auto-detected from version.txt, not stored in DB) if (!publicSettings.metadata) { publicSettings.metadata = {}; diff --git a/backend/src/routes/updates.test.ts b/backend/src/routes/updates.test.ts index c51e819..1c47ed2 100644 --- a/backend/src/routes/updates.test.ts +++ b/backend/src/routes/updates.test.ts @@ -51,7 +51,7 @@ const INFO = { updateAvailable: true, isUnreleasedBuild: false, publishedAt: '2026-08-12T07:11:45Z', - releaseUrl: 'https://github.com/darkraise/flashpoint-web/releases/tag/v1.0.42', + releaseUrl: 'https://git.example.com/owner/dotcom-arcade/releases/tag/v1.0.42', changelog: '## Changes', checkedAt: '2026-08-12T08:00:00.000Z', lastCheckFailed: false, diff --git a/backend/src/services/AppUpdateService.ts b/backend/src/services/AppUpdateService.ts index 37efe74..b1cc3ad 100644 --- a/backend/src/services/AppUpdateService.ts +++ b/backend/src/services/AppUpdateService.ts @@ -56,12 +56,12 @@ export class AppUpdateService { return this.buildInfo(); } // A forced check still respects a 60s floor. Express rate limiting allows - // 100 requests a minute, which is no protection for a GitHub quota of 60 an - // hour that this shares with the star count and the Ruffle update check. + // 100 requests a minute, which is no protection for a release API's quota + // (GitHub allows 60 an hour, shared with the Ruffle update check). const maxAge = force ? FORCED_REFRESH_FLOOR_MS : CACHE_TTL_MS; const age = this.cached === null ? Infinity : Date.now() - this.cached.fetchedAt; // A failure also respects the 60s floor, even when force is true, so a - // GitHub outage or rate limit can't be hammered every request while it + // source's outage or rate limit can't be hammered every request while it // still shares the API's blocking timeout with each caller. const failureAge = this.lastFailedAt === null ? Infinity : Date.now() - this.lastFailedAt; const withinFailureFloor = failureAge < FORCED_REFRESH_FLOOR_MS; diff --git a/docker-compose.yml b/docker-compose.yml index f1abd41..003ecfc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,7 +56,6 @@ services: - RATE_LIMIT_WINDOW_MS=${RATE_LIMIT_WINDOW_MS:-60000} - RATE_LIMIT_MAX_REQUESTS=${RATE_LIMIT_MAX_REQUESTS:-100} # Application - - HOME_RECENT_HOURS=${HOME_RECENT_HOURS:-24} # "Latest release" API URL (GitHub or Gitea) for update checks; off when unset - APP_UPDATE_URL=${APP_UPDATE_URL:-} - ENABLE_CGI=${ENABLE_CGI:-false} diff --git a/docs/04-frontend/architecture.md b/docs/04-frontend/architecture.md index 5c41721..07b2603 100644 --- a/docs/04-frontend/architecture.md +++ b/docs/04-frontend/architecture.md @@ -205,9 +205,9 @@ Routes are defined in `App.tsx` with nested routing structure: {/* Public routes */} - - + } /> diff --git a/docs/04-frontend/components/layout-components.md b/docs/04-frontend/components/layout-components.md index f13e039..945ff9e 100644 --- a/docs/04-frontend/components/layout-components.md +++ b/docs/04-frontend/components/layout-components.md @@ -286,16 +286,7 @@ interface SidebarProps { ```typescript const gameNavItems: NavItem[] = [ - { - path: '/flash-games', - iconImage: '/images/Flash.png', - label: 'Flash Games', - }, - { - path: '/html5-games', - iconImage: '/images/HTML5.png', - label: 'HTML5 Games', - }, + { path: '/', icon: Home, label: 'Home' }, { path: '/animations', icon: Film, label: 'Animations' }, { path: '/browse', icon: Gamepad2, label: 'Browse' }, ]; @@ -360,11 +351,6 @@ const isNavItemActive = (item: NavItem) => { ); } - // Special case: Home (/) should highlight Flash Games - if (location.pathname === '/' && itemPath === '/flash-games') { - return true; - } - return location.pathname === itemPath; }; ``` diff --git a/docs/04-frontend/views-routing.md b/docs/04-frontend/views-routing.md index 1314492..71e2423 100644 --- a/docs/04-frontend/views-routing.md +++ b/docs/04-frontend/views-routing.md @@ -6,24 +6,24 @@ Dotcom Arcade frontend. ## Route Structure ``` -/ → Redirects to /flash-games +/ → HomeView (discovery homepage) /login → LoginView (no auth required) /register → RegisterView (no auth required) /unauthorized → UnauthorizedView # Game Views (Public/Guest Accessible) -/flash-games → FlashGamesView -/html5-games → HTML5GamesView /browse → BrowseView /animations → AnimationsView -/games/:id → GameDetailView +/browse/:id → GamePage (also /animations/:id, /games/:id) +/flash/*, /html5/* → Redirect to the same path under /browse # Protected Routes (Auth Required) -/games/:id/play → GamePlayerView (requires "games.play" permission) +/browse/:id/play → GamePage, game started (requires "games.play") /dashboard → DashboardView /favorites → FavoritesView -/playlists → PlaylistsView -/playlists/:id → PlaylistDetailView +/playlists → UserPlaylistsView +/playlists/:id → UserPlaylistDetailView +/flashpoint-playlists → PlaylistsView /settings → SettingsView # Admin Routes (Permission-Based) @@ -91,17 +91,6 @@ interface ProtectedRouteProps { ## Key Views -### FlashGamesView / HTML5GamesView - -Pre-filtered game browsing views. - -**Features:** - -- Platform-specific filtering -- Grid/List view toggle -- Pagination -- Add to favorites/playlists - ### BrowseView Main game browsing view with full filtering. @@ -191,9 +180,9 @@ Routes are configured in `App.tsx`: {/* Public routes */} - - + } /> diff --git a/docs/06-api-reference/README.md b/docs/06-api-reference/README.md index 372058a..0780412 100644 --- a/docs/06-api-reference/README.md +++ b/docs/06-api-reference/README.md @@ -202,7 +202,6 @@ Default: 100 requests per 15 minutes. Headers: `X-RateLimit-Limit`, - [GET /api/metrics/summary](./admin-api.md#get-metrics-summary) - [GET /api/metrics/endpoints](./admin-api.md#get-endpoint-metrics) - [GET /api/jobs](./admin-api.md#list-all-jobs) -- [GET /api/github/stars](./admin-api.md#get-github-star-count) - [GET /api/settings/auth](./admin-api.md#get-auth-settings) - [PATCH /api/settings/auth](./admin-api.md#update-auth-settings) - [GET /game-files/\*](./admin-api.md#get-game-file) diff --git a/docs/06-api-reference/admin-api.md b/docs/06-api-reference/admin-api.md index 70d9110..e862adf 100644 --- a/docs/06-api-reference/admin-api.md +++ b/docs/06-api-reference/admin-api.md @@ -213,19 +213,6 @@ Returns `202 Accepted` Returns progress percentage, stage, estimated completion time. -## GitHub Endpoints - -### Get GitHub Star Count - -`GET /api/github/stars` - No auth required - -```json -{ - "success": true, - "data": { "stars": 2847 } -} -``` - ## Statistics Endpoints ### Get Archive Statistics diff --git a/docs/09-deployment/environment-variables.md b/docs/09-deployment/environment-variables.md index b8a080f..2ef3e62 100644 --- a/docs/09-deployment/environment-variables.md +++ b/docs/09-deployment/environment-variables.md @@ -103,7 +103,6 @@ Two notes that save debugging time: | Variable | Default | Description | | -------------------- | ------- | ------------------------------------------------ | | `ENABLE_CGI` | false | Enable PHP CGI execution for legacy game content | -| `HOME_RECENT_HOURS` | 24 | Hours to look back for "recently added" games | **Frontend Serving:** diff --git a/docs/13-code-reviews/games-filter-panel-review.md b/docs/13-code-reviews/games-filter-panel-review.md deleted file mode 100644 index 127c09b..0000000 --- a/docs/13-code-reviews/games-filter-panel-review.md +++ /dev/null @@ -1,328 +0,0 @@ -# Games Filter Panel & Filter Logic - Code Review - -**Date:** 2026-02-15 -**Reviewer:** Claude -**Status:** Review Complete - -## Overview - -The filter system spans frontend components, hooks, API layer, and backend services. Overall, the implementation is solid with good patterns like config-driven rendering and proper caching. However, there are several issues and improvement opportunities. - -## Files Reviewed - -### Frontend Components -- `frontend/src/components/search/FilterPanel.tsx` -- `frontend/src/components/search/FilterDropdown.tsx` -- `frontend/src/components/search/FilterChips.tsx` -- `frontend/src/components/search/filterConfig.ts` -- `frontend/src/components/search/SearchBar.tsx` -- `frontend/src/components/search/SortControls.tsx` -- `frontend/src/components/library/GameBrowseLayout.tsx` - -### Frontend Hooks & Types -- `frontend/src/hooks/useGames.ts` -- `frontend/src/hooks/useFilterOptions.ts` -- `frontend/src/hooks/useFilterDropdowns.ts` -- `frontend/src/types/game.ts` -- `frontend/src/lib/api/games.ts` - -### Backend -- `backend/src/routes/games.ts` -- `backend/src/services/GameService.ts` - ---- - -## Issues Found - -### 1. Type Safety Issues - -| Location | Issue | Severity | -|----------|-------|----------| -| `FilterPanel.tsx:22-23` | Unsafe `split()` on potentially undefined value | Medium | -| `GameBrowseLayout.tsx:72` | Type assertion `as 'asc' | 'desc'` without validation | Medium | -| `FilterDropdown.tsx:42` | No validation that `selectedValues` array items exist in `options` | Low | - -```typescript -// FilterPanel.tsx:22-23 - currentValue could be number/undefined for yearFrom/yearTo -const current = typeof currentValue === 'string' ? currentValue.split(',').filter(Boolean) : []; -// This works but paramKey includes non-string fields (yearFrom, yearTo, page, limit) -``` - -### 2. Missing Year Range Filter in UI - -| Location | Issue | Severity | -|----------|-------|----------| -| `filterConfig.ts` | `yearFrom`/`yearTo` filters defined in `GameFilters` but not exposed in UI | Medium | -| `FilterPanel.tsx` | No year range slider or inputs despite backend support | Medium | - -The `FilterOptions.yearRange` is fetched but never used - users cannot filter by release year. - -### 3. Inconsistent Filter Chips Implementation - -| Location | Issue | Severity | -|----------|-------|----------| -| `FilterPanel.tsx:105-140` | `renderFilterBadges()` duplicates logic already in `FilterChips.tsx` | Low | -| `GameBrowseLayout.tsx:110-198` | Separate filter chip generation vs `FilterChips` component | Low | - -Two different chip rendering systems exist: -- `FilterPanel.tsx` renders inline badges -- `GameBrowseLayout.tsx` creates `FilterChip[]` array for `FilterChips` component - -### 4. Performance: Redundant Handlers Recreation - -| Location | Issue | Severity | -|----------|-------|----------| -| `FilterPanel.tsx:68-74` | `createFilterHandlers()` called on every render for every filter | Low | -| `FilterPanel.tsx:113-119` | Same handlers recreated in `renderFilterBadges()` | Low | - -```typescript -// Called inside map() on every render -const handlers = createFilterHandlers( - config.paramKey, - config.id, - filters, - updateFilter, - dropdowns -); -``` - -### 5. Missing Search Debouncing in SearchBar - -| Location | Issue | Severity | -|----------|-------|----------| -| `SearchBar.tsx:10-15` | No debounce - navigates immediately on form submit | Low | -| N/A | No "search as you type" with debounce option | Enhancement | - -### 6. Backend: Filter Options Limit Inconsistency - -| Location | Issue | Severity | -|----------|-------|----------| -| `GameService.ts:706-707` | Series limited to 100 | Low | -| `GameService.ts:730-731` | Developers limited to 100 | Low | -| `GameService.ts:780-781` | Play modes NOT limited | Inconsistent | -| `GameService.ts:849` | Tags limited to 100 | Low | - -Play modes query lacks `LIMIT 100` that other filter queries have. - -### 7. Missing Input Validation on Frontend - -| Location | Issue | Severity | -|----------|-------|----------| -| `GameBrowseLayout.tsx:72` | `page` parsed without `isNaN()` check | Medium | -| `GameBrowseLayout.tsx:58-68` | Year parsing validates `isNaN()` - good! | N/A | - -```typescript -// Line 72 - could pass NaN to API -page: parseInt(searchParams.get('page') || '1', 10), -``` - -### 8. URL Encoding Issues - -| Location | Issue | Severity | -|----------|-------|----------| -| `GameBrowseLayout.tsx:134` | `encodeURIComponent()` used for chip IDs but inconsistent | Low | -| `FilterPanel.tsx:125` | Badge key uses raw `value` - may have special chars | Low | - -```typescript -// Line 125 - key should be encoded for consistency - -``` - -### 9. Missing Error Boundary for Filter Options - -| Location | Issue | Severity | -|----------|-------|----------| -| `useFilterOptions.ts` | No `onError` handler | Low | -| `FilterPanel.tsx:79` | Falls back to empty array `|| []` but no loading state | Low | - -If filter options fail to load, dropdowns show "No options available" with no retry mechanism. - -### 10. Accessibility Issues - -| Location | Issue | Severity | -|----------|-------|----------| -| `SortControls.tsx:26` | Native `