Flashback Challenges Architecture
User-facing surfaces
Section titled “User-facing surfaces”- Challenge list page:
src/app/challenges/page.jsx - Challenge detail page:
src/app/challenges/[challengeId]/page.jsx - FACEIT connect button:
src/components/auth/FaceitConnectButton.tsx - Rules modal:
src/components/modals/ChallengeRulesModal.tsx - Sidebar link:
src/components/navigation/ChallengeSidebarSection.jsx
Frontend data layer
Section titled “Frontend data layer”Current hooks live in src/hooks/useApi.ts:
useChallengesuseChallengeuseChallengeRegistrationuseRegisterChallengeuseUnregisterChallengeuseChallengeScoreboarduseChallengeAnalyticsuseMyChallengeAnalyticsSummariesuseChallengeParticipantsuseChallengeParticipantCountuseUserChallengeRegistrations
Deprecated but still present:
src/hooks/useChallenges.tssrc/lib/api/challenges.client.ts
When touching client/shared code, prefer the existing API helpers and OpenAPI client:
apiClientfromsrc/lib/api/client.tsapiGet,apiPost,apiPut,apiDeletefromsrc/lib/api-utils
Primary API routes
Section titled “Primary API routes”Challenge CRUD/list/detail:
src/app/api/v1/challenges/route.tssrc/app/api/v1/challenges/[challengeId]/route.ts
Registration:
src/app/api/v1/challenges/[challengeId]/registrations/route.tssrc/app/api/v1/users/me/challenges/route.ts
Live scoreboard and participants:
src/app/api/v1/challenges/[challengeId]/scoreboard/route.tssrc/app/api/v1/challenges/[challengeId]/participants/route.ts
Analytics / demo processing status:
src/app/api/v1/challenges/[challengeId]/analytics/route.tssrc/app/api/v1/users/me/challenge-analytics-summaries/route.ts- Core server logic:
src/lib/challenges/challenge-analytics.server.ts
FACEIT and sync:
- FACEIT OAuth connect:
src/app/api/v1/auth/faceit/connect/route.ts - FACEIT callback page:
src/app/api/v1/auth/callback/faceit/page.jsx - Active challenge sync cron:
src/app/api/v1/cron/faceit-match-sync/route.ts - Older manual refresh routes:
src/app/api/v1/challenges/[challengeId]/refresh-games/route.tssrc/app/api/v1/challenges/[challengeId]/refresh-games/[userId]/route.ts
Demo queue endpoint:
src/app/api/v1/users/me/demos/queue/route.ts
Important libraries
Section titled “Important libraries”- Access control:
src/lib/challenges/challenge-access.server.ts - Password challenges:
src/lib/challenges/challenge-password.server.ts - Analytics derivation:
src/lib/challenges/challenge-analytics.server.ts - Outcome backfill:
src/lib/challenges/challenge-match-outcome-backfill.ts - FACEIT OAuth helpers:
src/lib/faceit-oauth.ts - FACEIT Elo:
src/lib/faceit/elo.server.ts - FACEIT placements:
src/lib/faceit/placements.server.ts - Challenge DTO mapper:
src/lib/mappers/challenges.ts - Challenge schemas/OpenAPI:
src/lib/schemas/challenges.ts
Supabase tables
Section titled “Supabase tables”Core challenge tables:
challengesuser_challengeschallenge_faceit_matchesfaceit_seasons
Demo pipeline / analysis:
demosdemo_pipelineanalyzed_demosdemo_player_match_statsdemo_player_round_statsdemo_roundsdemo_killsdemo_damagesdemo_utilities
Legacy match summary table (not used for current challenge scoring):
user_games
Users/auth:
- Clerk user public metadata stores FACEIT identity.
- Supabase
userstable stores app user row keyed byclerk_user_id.
Data flow
Section titled “Data flow”1. FACEIT account connection
Section titled “1. FACEIT account connection”FaceitConnectButton -> FACEIT OAuth browser flow -> POST /api/v1/auth/faceit/connect -> FACEIT token exchange + userinfo -> Clerk publicMetadata.faceit = { guid, nickname, avatar, country, connectedAt }Stable identity is FACEIT guid/player id. Nickname is display/helper data and can change.
2. Challenge registration
Section titled “2. Challenge registration”/challenges or /challenges/[id] -> useRegisterChallenge -> POST /api/v1/challenges/{id}/registrations -> validates Clerk user -> requires FACEIT metadata -> checks challenge visibility/password/window -> ensures Supabase users row -> inserts user_challenges -> snapshots starting Elo if challenge already starteduser_challenges.faceit stores the nickname at registration time.
3. Live match sync
Section titled “3. Live match sync”Cloud Scheduler / cron secret -> POST /api/v1/cron/faceit-match-sync -> active challenges from challenges table -> participants from user_challenges -> Clerk metadata for FACEIT player ids -> FACEIT Data API match history -> discover eligible challenge matches -> resolve demo URLs if available -> create_pipeline_job RPC for new demos -> upsert challenge_faceit_matches -> update Elo and placement metadata -> backfill outcomes from analyzed demosThis route is the main production mechanism for live challenge matching and demo queuing.
4. Demo pipeline
Section titled “4. Demo pipeline”See ../demo-pipeline/contracts/queues.md.
create_pipeline_job RPC -> demos row -> demo_pipeline row -> pgmq demo_download message -> faceit-downloader downloads/uploads demo -> advance_demo_pipeline_stage -> pgmq demo_analyze message -> analyst parses demo and writes analyzed_demos + demo_* statsdemo_generate exists, but current showrunner integration is not the primary path for challenge scoring.
5. Scoreboard
Section titled “5. Scoreboard”GET /api/v1/challenges/{id}/scoreboard -> challenge + scoring_config -> user_challenges participants/Elo -> challenge_faceit_matches -> demos + analyzed_demos + demo_player_match_stats -> ScoreboardRowDTO[]Scoreboard stats come from analyzed demos only. user_games is legacy match-summary data and is intentionally not used as a challenge scoring fallback, because switching from legacy summaries to partial analyzed-demo data can make displayed match counts decrease.
6. User analytics
Section titled “6. User analytics”GET /api/v1/challenges/{id}/analytics -> validates registration -> challenge_faceit_matches for current user -> demo_pipeline status -> analyzed_demos -> demo_player_match_stats matched by SteamID or normalized FACEIT name -> processing summary + totals + per-demo statusPossible analysisStatus values:
not_queuedqueuedprocessingfailedanalyzedno_player_stats