- Switched to secure httpOnly, SameSite=Strict cookies for JWT authentication. - Refactored backend to use AppState for shared secrets and database pool caching. - Modernized frontend with Svelte 5 runes ($state) and removed localStorage reliance. - Gated destructive test endpoints behind debug_assertions and fixed unsafe test patterns. - Enhanced CI pipeline with cargo clippy, cargo fmt, and pinned pnpm version. - Updated documentation and implementation plans to match the hardened architecture.
4.9 KiB
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)
# Linting & Quality
make lint # runs cargo fmt, clippy (-D warnings), and svelte-check
# Build
make build # runs lint, then pnpm build and cargo build --release
make compose-up # docker compose build + start
# Testing
make test # runs lint, then cargo test (backend unit tests)
make test-e2e # test-up + pnpm test:e2e in one step
# Demo data
make seed-demo # wipe dev.db and reseed from backend/demo/demo_seed.sql
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); noDATABASE_URLneeded forcargo build/cargo check - Auth: Secure JWT-based authentication. The backend sets an
httpOnly,SameSite=Strictcookie namedtoken. TheTutorClaimsextractor inauth.rsenforces authentication by reading this cookie. - Shared State: Axum handlers use
State<AppState>(orState<SqlitePool>viaFromRef) which caches theJWT_SECRETand DB pool. - Static serving:
tower_http::ServeDirserves compiled frontend fromfrontend/build/with SPA fallback toindex.html - Migrations: auto-run via
sqlx::migrate!at startup frombackend/migrations/ PRAGMA foreign_keys = ONis enforced on every connection indb.rs
Route handlers live in backend/src/routes/ and are merged in routes/mod.rs.
Route modules: auth_routes, checkin, courses, rooms, sessions, attendance, notes, export, tutors, and test_reset (mounted only when TT_TEST_MODE=1 AND in debug builds).
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. - Auth state: Managed by the
authobject in$lib/auth.svelte.ts. - Adapter:
adapter-static→ single-page app,fallback: 'index.html' - API client:
src/lib/api.ts— all fetch calls go through here; relies on browser automatic cookie handling. - Types:
src/lib/types.tsmirrors the Rust models exactly — keep them in sync when changing the API
Routes:
routes/admin/login/— public loginroutes/admin/— all tutor-facing pages (guarded by+layout.svelteauth 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: closed → open → locked. 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 (~10–50 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 22/pnpm frontend → Rust 1.95 backend → Debian slim runtime, non-root)deploy/: Helm chart — Deployment, Service, HTTPRoute (Gateway API), PVC, CronJob for nightly vacuum + backup rotation- Live at
tutor.puchstein.dev(tenant-5, ITSH Cloud); image atregistry.itsh.dev/s0wlz/tutortool - CI: Gitea Actions at
.gitea/workflows/ci.yml— runsmake lint,cargo test,pnpm build,make test-e2e, and a no-push Docker build on every non-main push and PR - Release:
.gitea/workflows/release.yml— triggered byv*.*.*tags; builds + pushes image, thenhelm upgrade
Conventions
- Rust toolchain is pinned to 1.95.0 via
rust-toolchain.toml. - Frontend indentation: 2 spaces (Svelte/TS files). Backend: standard
rustfmtdefaults. - All SQLx queries are runtime (
sqlx::query_as::<_, T>()); no compile-time macros are used, soDATABASE_URLis not required forcargo buildorcargo check. - Zero Warnings Policy: All code must pass
make lint(clippy, fmt, svelte-check) without warnings before committing.