Files
tutortool/CLAUDE.md
Claude e01f7808a0
Some checks failed
CI / test (push) Failing after 1m35s
CI / test (pull_request) Failing after 1m34s
Overhaul build pipeline: pnpm, non-root image, Helm chart, CI+release workflows
- db.rs: fix fresh-PVC startup crash by using SqliteConnectOptions with
  create_if_missing(true) and foreign_keys(true); drops after_connect
- Dockerfile: switch to Node 22 + pnpm (corepack), run pnpm check before
  build, copy backend/demo/ for TT_TEST_MODE support, non-root app user,
  add HEALTHCHECK, remove baked-in JWT_SECRET
- .dockerignore: exclude node_modules, build artifacts, data/, logs
- deploy/: new Helm chart replacing k8s/ — Deployment, Service, HTTPRoute
  (Gateway API), PVC (hcloud-volumes), CronJob backup, ServiceAccount, VPA;
  JWT_SECRET sourced from pre-provisioned K8s Secret
- k8s/: removed (superseded by deploy/)
- ci.yml: replaces test.yml — Node 20->22, same test steps, adds no-push
  Docker build; triggers on non-main pushes and PRs
- release.yml: new tag-driven workflow (v*.*.*) — runs tests, pushes image
  to registry.itsh.dev/s0wlz/tutortool, deploys via helm upgrade

https://claude.ai/code/session_01N1kWaQJkz1fC7mUippdQR5
2026-04-29 19:11:29 +00:00

99 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
```bash
# 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: `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 (~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 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 at `registry.itsh.dev/s0wlz/tutortool`
- CI: Gitea Actions at `.gitea/workflows/ci.yml` — runs `cargo check`, `pnpm check` (tsgo + svelte-check), `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 by `v*.*.*` tags; builds + pushes image, then `helm upgrade`
## 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`.