Files
tutortool/CLAUDE.md
s0wlz (Matthias Puchstein) f3f570a63e docs: update CLAUDE.md, GEMINI.md, add README
- Add test-* and seed-demo Make targets to command reference
- Document TT_TEST_MODE, /health route, test_reset route module
- Expand admin subroutes list, add Testing and CI sections
- Fix SQLx contradiction (runtime queries, no DATABASE_URL needed)
- Rewrite GEMINI.md with full Claude parity
- Add root README.md with quickstart, stack, and doc links
2026-04-29 04:35:51 +02:00

4.8 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commands

# Development
make dev              # start backend + frontend in parallel
make dev-backend      # cargo run (port 3000)
make dev-frontend     # pnpm dev (port 5173, /api proxied to :3000)

# Build
make build            # pnpm build, then cargo build --release
make compose-up       # docker compose build + start

# Backend
cargo test            # run all backend unit tests
cargo check           # fast type check without linking

# Frontend
pnpm check            # TypeScript + Svelte type check
pnpm check:watch      # watch mode
pnpm build            # Vite build to dist/

# Demo data
make seed-demo        # wipe dev.db and reseed from backend/demo/demo_seed.sql

# E2E test pipeline (see docs/testing.md for full detail)
make test-up          # build test DB if missing, start backend on test port, wait for /health
make test-down        # stop the test backend
make test-reset       # fast DB reset via POST /__test__/reset (~1050 ms)
make test-rebuild     # wipe and rebuild test DB from migrations + seed
make test-e2e         # test-up + pnpm test:e2e in one step

Architecture

TutorTool is a Rust + SvelteKit attendance tracker for tutoring sessions.

Backend (backend/)

  • Framework: Axum (async) on Tokio, port 3000
  • Database: SQLite via SQLx — all queries use the runtime sqlx::query() / sqlx::query_as::<_, T>() (not compile-time macros); no DATABASE_URL needed for cargo build/cargo check
  • Auth: JWT (7-day expiry, jsonwebtoken crate) + bcrypt passwords; TutorClaims extractor in auth.rs
  • Static serving: tower_http::ServeDir serves compiled frontend from frontend/build/ with SPA fallback to index.html
  • Migrations: auto-run via sqlx::migrate! at startup from backend/migrations/
  • PRAGMA foreign_keys = ON is enforced on every connection in db.rs

Route handlers live in backend/src/routes/ and are merged in routes/mod.rs. Each handler receives State<SqlitePool> and extracts TutorClaims from the JWT on protected routes.

Route modules: auth_routes, checkin, courses, rooms, sessions, attendance, notes, export, tutors, and test_reset (mounted only when TT_TEST_MODE=1).

The /health route always returns "ok" and is used by the test pipeline to wait for startup.

Frontend (frontend/)

  • Framework: SvelteKit 5 (Svelte runes, $state/$derived) with TypeScript
  • Adapter: adapter-static → single-page app, fallback: 'index.html'
  • API client: src/lib/api.ts — all fetch calls go through here; JWT injected from src/lib/auth.ts (localStorage-backed store)
  • Types: src/lib/types.ts mirrors the Rust models exactly — keep them in sync when changing the API

Routes:

  • routes/admin/login/ — public login
  • routes/admin/ — all tutor-facing pages (guarded by +layout.svelte auth check)
    • attendance/, courses/, export/, live/, rooms/, sessions/, students/, tutors/
  • routes/s/[code]/ — public student check-in page

Data Model (SQLite, 9 tables)

tutors ──< tutor_courses >── courses ──< students
                                  └──< sessions ──< slots ──< attendances
                                                        └──< notes
rooms (layout_json) ←── slots.room_id

Key slot status transitions: closedopenlocked. The code field on slots is the public check-in token students use at /s/[code].

Testing

E2E tests run Playwright against a dedicated test backend daemon. The test backend uses a separate DB and is started with TT_TEST_MODE=1, which enables POST /__test__/reset for fast state resets (~1050 ms) without restarting the process. Each git worktree gets its own deterministic port and DB path (no collisions when testing across branches).

See docs/testing.md for the full guide including seed data, MCP-driven verification, and worktree isolation details.

Container / K8s / CI

  • Dockerfile: 3-stage build (Node 20 frontend → Rust 1.95 backend → Debian slim runtime)
  • k8s/: Deployment, Service, PVC for SQLite, CronJob for nightly vacuum + backup rotation
  • Live at tutor.puchstein.dev (tenant-5, ITSH Cloud)
  • CI: Gitea Actions at .gitea/workflows/test.yml — runs cargo check, pnpm check, cargo test, pnpm build, and make test-e2e on every push to main and on PRs

Conventions

  • Rust toolchain is pinned to 1.95.0 via rust-toolchain.toml.
  • Frontend indentation: 2 spaces (Svelte/TS files). Backend: standard rustfmt defaults.
  • All SQLx queries are runtime (sqlx::query_as::<_, T>()); no compile-time macros are used, so DATABASE_URL is not required for cargo build or cargo check.