lomda-hub/.github/copilot-instructions.md
2026-02-04 16:50:33 +02:00

5.2 KiB

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): SQLAlchemy 2.0 with type hints (Mapped[T]). Enums: UserRole, ModuleType, ProgressStatus, QuestionType.
  • Routers: Three main routers in backend/app/routers: auth.py (login/register/OAuth), admin.py (course/module CRUD), learner.py (enrollments/progress).
  • Dependencies (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): Settings loaded from env vars using Pydantic BaseSettings.

Frontend Structure

  • Pages (frontend/src/pages): Each major view (login, courses, player, admin dashboard/editor) as separate page component.
  • API Client (frontend/src/api): Centralized API calls. Token management in client.ts with localStorage.
  • Routing: React Router v6 in 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: 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) 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)
  • 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 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.
  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