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

5.0 KiB
Raw Permalink 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 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.