# TryMe Gaming App — System Architecture (`SYSTEM.md`) ## 1. High-Level Overview **TryMe** is a web platform designed for students to author, share, vote on, favourite, and play multiple-choice and write-in quizzes (0–40 questions on the public free tier). It packages a **React 19 + TypeScript** single-page frontend and a **FastAPI + SQLAlchemy 2.0** backend into a single multi-stage container image, backed by either **PostgreSQL** or **SQLite**. ```mermaid graph TB Student["Student / Moderator Browser (SPA)"] CLI["Automation / Study Script (X-API-Key)"] Auth0["Auth0 Federated IdP (OIDC / JWKS)"] subgraph Container["TryMe Container (:8000)"] FastAPI["FastAPI HTTP & Static Layer (app/main.py)"] AuthLayer["Session, Auth0 JWKS & API Key Guard (app/auth.py)"] QuizSvc["Quiz Grading & Leaderboard Service (app/services/quiz_service.py)"] SPA["Compiled React + Vite Assets (frontend/dist)"] end DB[("PostgreSQL 16 or SQLite + PVC")] Student -->|"HTTPS / Cookie or Bearer JWT"| FastAPI CLI -->|"HTTPS + X-API-Key"| FastAPI FastAPI --> SPA FastAPI --> AuthLayer AuthLayer -->|"Verify OIDC / JWKS"| Auth0 AuthLayer --> QuizSvc QuizSvc --> DB ``` --- ## 2. CoreSubsystems ### 2.1 Authentication, Consent & API Keys (`app/auth.py`, `app/routers/auth_routes.py`, `app/routers/users.py`) - **Auth0 Federated Login**: `/api/v1/auth/login` initiates an OAuth2/OIDC Authorization Code flow with signed anti-CSRF state (`itsdangerous`). `/api/v1/auth/callback` exchanges the code, fetches `/userinfo`, provisions or updates the `User` row, and issues an `HttpOnly` session JWT cookie (`tryme_session`). Direct Auth0 RS256 Bearer tokens are also verified via cached `/.well-known/jwks.json`. - **Player Onboarding & Privacy**: Users select a stylized animal icon (`fox`, `owl`, `bear`, `otter`, `koala`, `tiger`, `frog`, `penguin`) and colorway (`avatar-1..8`), set a unique `nickname`, `first_name`, `last_name`, optional integer `age` (**never** storing a birthday), inclusive `gender` (`woman`, `man`, `non_binary`, `agender`, `genderqueer`, `two_spirit`, `other`, `prefer_not_to_say`), and explicitly confirm permission to use public websites (`public_website_consent`). - **Personal API Keys**: Users generate `tm_live_` keys stored strictly as SHA-256 hashes (`key_hash`) with configurable expiration and one-click revocation (`POST /api/v1/users/me/api-keys/{id}/expire`). ### 2.2 Quiz Engine, Leaderboards & Voting (`app/routers/quizzes.py`, `app/services/quiz_service.py`) - **Free Tier Guardrails**: Validated via Pydantic (`max_length=40`) and route checks (`0 <= len(questions) <= 40`). - **Question Formats**: Each question supports either `multiple_choice` (2–6 options) or `write_in` (whitespace-normalized, case-insensitive matching against `correct_answer` plus optional `accepted_answers` synonyms). - **Score Aggregation**: Every quiz card and detail response computes three distinct metrics in bulk SQL queries: 1. **Global Best Score**: Highest `score_percent` across all attempts, paired with the top holder's `nickname` and animal icon (ties broken by earliest completion). 2. **User Latest Score**: The signed-in player's most chronological recent `QuizAttempt`. 3. **User All-Time Best Score**: The signed-in player's highest `QuizAttempt` on that quiz. - **Thumbs Up / Down Voting**: Stored in `quiz_votes` with a `UNIQUE(quiz_id, user_id)` constraint (`+1` or `-1`, toggleable back to `0`). - **Shareable URLs**: Every quiz receives a unique `share_slug` (e.g., `/quiz/solar-system-quick-ch-a1b2c3`) routable directly in the SPA and backend. ### 2.3 Right-Hand Favourites Pane & Recycle Bin (`app/routers/favourites.py`, `FavouritesPane.tsx`) ```mermaid sequenceDiagram participant Main as Explore Feed Card participant Rail as Right Favourites Pane participant Bin as Pop-up Recycle Bin participant API as FastAPI /api/v1/favourites Main->>Rail: Drag quiz card (application/x-tryme-quiz-id) Rail->>API: POST /api/v1/favourites {quiz_id, position} API-->>Rail: Ordered favourites list Rail->>Rail: Drag existing favourite (summons Recycle Bin) alt Reorder inside rail Rail->>API: PUT /api/v1/favourites/reorder {quiz_ids} else Drop onto Recycle Bin Rail->>Bin: Drop favourite item Bin->>API: DELETE /api/v1/favourites/{quiz_id} Bin-->>Rail: Show 7-second Undo bar end ``` ### 2.4 Moderation (`app/routers/admin.py`, `ModeratorView.tsx`) - Moderators can be designated via `ADMIN_USER` container env var (matched against `auth_sub`, `email`, or `nickname`) or claimed once at initial launch (`POST /api/v1/admin/claim`) when no moderator exists. - Moderators can suspend/restore accounts (`is_banned = True` blocks quiz creation, submissions, and voting with `403 Forbidden`) and remove quizzes (`is_removed = True` hides the quiz from public feeds and cleans up dangling favourites). --- ## 3. UI Design System The frontend uses the Hallmark **`Hum`** (`playful` genre, `--scope app`) design system in `frontend/src/styles/tokens.css` and `frontend/src/styles/app.css`: - **Primary Brand Colors**: Warm festive orange `#fb723a` (`oklch(70.6% 0.178 41)`) paired with deep slate-navy `#253450` (`oklch(32.5% 0.058 258)`) over warm cream card stock (`oklch(97.2% 0.012 72)`). - **Typography**: `Chillax` (`500`, `600`) for headings, body, and controls; `Geist Mono` exclusively for API key tokens; `tabular-nums` on all scores, counts, and votes. - **Accessibility & Motion**: Instant `:focus-visible` rings, 44px touch targets, native `` modals, keyboard reorder/delete fallbacks alongside HTML5 drag-and-drop, and `prefers-reduced-motion: reduce` support.