Add CLAUDE.md

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Simon GruberandClaude Sonnet 5.5 committed 2026-11-04 09:33:46 +01:00
1 parent 1b1b0898ed
commit 6a896304de
1 file changed
+41
+41
View File
@@ -0,0 +1,41 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Overview
Lunchtime is a self-hosted group lunch ordering app: FastAPI + SQLite backend (Python 3.12), React 18 + TypeScript + Vite + Ant Design frontend, nginx in front. All app code lives under `src/`. There are no tests and no linter configured.
## Commands
Full dev stack (backend with `--reload`, Vite HMR, nginx, Mailpit); also what the devcontainer starts:
```bash
cd src && docker compose up -d # app at http://localhost:8020, Mailpit at http://localhost:8025
```
Without Docker:
```bash
cd src/backend && pip install -r requirements.txt && DB_PATH=./.data uvicorn app.main:app --reload --port 5000
cd src/frontend && npm install && npm run dev # Vite on :3000
cd src/frontend && npm run build # tsc is not run by build; use `npx tsc --noEmit` for type checking
```
`nginx.conf` (repo root) expects `backend:5000` and `frontend:8000`, so running outside Compose needs your own proxy for `/api`.
CI (`.gitea/workflows/build-and-push-images.yaml`) builds and pushes backend/frontend images with Kaniko on changes to `src/**`.
## Architecture
**Backend** (`src/backend/app/`): almost every route is in the single `main.py` (~1300 lines), with Pydantic request models at the top. `services/` holds helpers (accounts/tokens, email, per-order file storage, submissions, config loading); `db.py` creates the schema inline in `init_db()` (`CREATE TABLE IF NOT EXISTS` plus ad-hoc `ALTER TABLE` migrations — there is no migration tool) and maps rows to dicts.
**Auth model:** there are no passwords or sessions. The client generates a user ID and sends it in the `X-User-Id` header (`get_existing_user_id` dependency). Per-order access is via tokens stored in `user_order_tokens` (`admin_token` = order owner, `submission_token` = participant); routes under `/api/orders/{id}/admin/...` check the caller's admin token. Email confirmation, user-ID change, email change and account migration flows use `account_confirmation_tokens` and send mail through `email_service`.
**Storage is split:** SQLite (`burger_orders.db` in `DB_PATH`) holds orders, submissions, user profiles and tokens. Per-order content is stored as files under `data/` next to the app, **keyed by the order's `admin_token`**: `configs/<token>.json` (menu/form definition), `descriptions/<token>.md`, `templates/`, `payments/`, and uploaded `images/` (served at `/api/images`). Deleting an order must also delete these files (see `delete_order` in `main.py`).
**App config:** `config.yaml` (mounted read-only at `/app/config.yaml`) controls `public_base_url`, SMTP settings and UI announcements; loaded by `services/config_service.py` and exposed via `GET /api/config`.
**Frontend** (`src/frontend/src/`): no router library — `lib/routing.ts` parses `window.location` into `home | create | order | admin` routes and `App.tsx` switches between the four `views/`. `lib/api.ts` wraps `fetch` (adds `X-User-Id`, clears the stored ID on "invalid user id" 401s); `lib/services/*` are typed per-domain API clients; `hooks/useApiRequest.ts` and `components/utils/AsyncContent.tsx` handle loading/error state. `lib/orderTemplating.ts` and `lib/orderFormatting.ts` render submissions into the text that is shared/exported from the form config. `TODO.md` tracks planned admin-view work.
Dev note: Compose mounts only `backend/app` and selected frontend source into the containers, so changes to `requirements.txt`/`package.json` need a rebuild. `src/frontend/dist/` is committed build output.