trymeapp/SYSTEM.md

5.6 KiB
Raw Permalink Blame History

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.

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

  • 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_<token> 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)

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 <dialog> modals, keyboard reorder/delete fallbacks alongside HTML5 drag-and-drop, and prefers-reduced-motion: reduce support.