# Lomda Hub - AI Coding Agent Instructions ## Architecture Overview **Stack**: React+Vite (TypeScript) frontend, FastAPI backend, PostgreSQL database with SQLAlchemy 2.0 ORM, Alembic migrations. **Deployment**: Docker Compose with 3 services (db, backend, frontend). Frontend on 5173, backend on 8000, postgres on 5433. **Auth**: JWT access/refresh tokens + Google OAuth. Tokens passed via `Authorization: Bearer` header. Two roles: `admin` and `learner`. ## Key Structural Patterns ### Backend Structure - **Models** ([backend/app/models.py](../backend/app/models.py)): SQLAlchemy 2.0 with type hints (`Mapped[T]`). Enums: `UserRole`, `ModuleType`, `ProgressStatus`, `QuestionType`. - **Routers**: Three main routers in [backend/app/routers](../backend/app/routers): `auth.py` (login/register/OAuth), `admin.py` (course/module CRUD), `learner.py` (enrollments/progress). - **Dependencies** ([backend/app/deps.py](../backend/app/deps.py)): `get_db()` for DB sessions, `get_current_user()` for auth, `require_admin()` for admin-only routes. - **Config** ([backend/app/core/config.py](../backend/app/core/config.py)): Settings loaded from env vars using Pydantic `BaseSettings`. ### Frontend Structure - **Pages** ([frontend/src/pages](../frontend/src/pages)): Each major view (login, courses, player, admin dashboard/editor) as separate page component. - **API Client** ([frontend/src/api](../frontend/src/api)): Centralized API calls. Token management in `client.ts` with localStorage. - **Routing**: React Router v6 in [App.tsx](../frontend/src/App.tsx). Admin routes prefixed with `/admin`. ### Data Flow: Locked Course Progression Courses have ordered modules. Learner must complete module N before unlocking N+1: 1. On enrollment, first module is `unlocked`, rest are `locked` (see `ModuleProgress` model). 2. Content modules: mark completed when viewed. 3. Quiz modules: mark completed only if attempt passes (`score >= pass_score`). 4. Backend logic in [learner.py](../backend/app/routers/learner.py): `submit_quiz_attempt` updates progress and unlocks next module. ## Critical Workflows ### Database Migrations 1. After model changes: `cd backend && alembic revision --autogenerate -m "description"` 2. Apply: `alembic upgrade head` (or `docker compose restart backend` for Docker) 3. Seed data: `python -m app.scripts.seed` (creates admin user + sample data) ### Development Commands **Docker**: `docker compose up --build` (runs migrations + seed automatically via Dockerfile CMD) **Local backend**: `uvicorn app.main:app --reload` (from backend dir with venv active) **Local frontend**: `npm run dev` (from frontend dir) ### Google OAuth Flow - Login: GET `/auth/google/login` → redirects to Google → callback at `/auth/google/callback` - Callback returns `RedirectResponse` to `FRONTEND_URL/auth/callback?access_token=...&refresh_token=...` - Frontend ([GoogleCallbackPage.tsx](../frontend/src/pages/GoogleCallbackPage.tsx)) extracts tokens from URL params and saves to localStorage - **Critical**: FastAPI route must NOT have trailing slash mismatch or it will 307 redirect infinitely ## Project-Specific Conventions ### API Response Patterns - Use Pydantic schemas for request/response validation (see [schemas.py](../backend/app/schemas.py)) - Admin endpoints return extended models with `group_ids` arrays - Learner endpoints filter by current user (never expose other learners' data) ### Error Handling - Raise `HTTPException` with appropriate status codes (400 bad request, 401 unauthorized, 403 forbidden, 404 not found) - No generic try/except unless specific recovery logic needed ### Database Relationships - Use SQLAlchemy relationship cascades: `cascade="all, delete-orphan"` for parent→child - Unique constraints for enrollment, progress, group membership: prevent duplicates via `__table_args__` ### Frontend State Management - No global state library; use React hooks + fetch in components - Token refresh: not auto-implemented; tokens expire after 30min (access) / 7days (refresh) ## Integration Points ### External Dependencies - Google OAuth: requires `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REDIRECT_URI` env vars - SMTP: config present but not actively used (future email features) ### Cross-Component Communication - Frontend calls backend REST API; no WebSockets or real-time updates - CORS: configured in [main.py](../backend/app/main.py) via `CORS_ORIGINS` env var (default: `http://localhost:5173`) ## Common Gotchas 1. **Trailing slashes**: FastAPI auto-redirects `/path/` to `/path` (307). Define routes without trailing slashes consistently. 2. **Password hashing**: bcrypt truncates at 72 bytes; see defensive check in [auth.py](../backend/app/auth.py). 3. **Module ordering**: `order_index` is manually managed. No auto-reordering on deletion. 4. **Admin seed**: Default admin created with email from `ADMIN_SEED_EMAIL` env var (default: `admin@example.com`). ## Key Files to Reference - [models.py](../backend/app/models.py): All database schema - [schemas.py](../backend/app/schemas.py): Request/response validation - [deps.py](../backend/app/deps.py): Auth dependencies - [learner.py](../backend/app/routers/learner.py): Core learning flow logic - [App.tsx](../frontend/src/App.tsx): Frontend routing and navigation