Files
YesChef/CLAUDE.md
T
Josh Rogers 7c1cfd62e6 Introduce Family entity and bootstrap default family on startup
Foundation for the multi-tenant migration: adds the Family table with a
unique InviteCode, and a startup hook that bootstraps a single default
family from the FamilyCode config when the table is empty. No behavior
change yet — the table exists and is seeded but nothing reads it.

Also fixes the backend test command in CLAUDE.md: dotnet test on the
.NET 10 SDK with MTP rejects the --solution switch and positional
project args, so we now use Push-Location + --project.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 23:00:00 -05:00

92 lines
6.4 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Overview
YesChef is a family shopping list and recipe app. Single-tenant per deployment, gated by a shared `FAMILY_CODE` invite phrase at registration. Real-time list collaboration is delivered over SignalR.
The repo is a small monorepo with two deployables plus an e2e harness at the root:
- `src/backend/YesChef.Api/` — ASP.NET Core (net10.0) minimal API + EF Core (Npgsql) + SignalR
- `src/frontend/` — SvelteKit 2 (Svelte 5 runes) + TypeScript + Tailwind v4, deployed via `@sveltejs/adapter-node`
- `docker-compose.yml` (root) — production-style stack: Postgres 17 + backend + frontend, fronted by Traefik (TLS via Let's Encrypt). Frontend and backend share the same `DOMAIN`; Traefik routes `/api`, `/hubs`, and `/health` to the backend, everything else to the frontend.
- `test-e2e.mjs` / `test-e2e-multiuser.mjs` — Playwright scripts run against `http://localhost:5173` (Vite dev) hitting a local backend.
## Common commands
Frontend (run from `src/frontend`, use `npm --prefix src/frontend <cmd>` to avoid `cd`):
```powershell
npm --prefix src/frontend install
npm --prefix src/frontend run dev # Vite dev server on :5173
npm --prefix src/frontend run build # adapter-node build → src/frontend/build
npm --prefix src/frontend run check # svelte-kit sync + svelte-check (type/lint check)
```
Backend (use `dotnet --project` to avoid `cd`):
```powershell
dotnet run --project src/backend/YesChef.Api # listens per launchSettings.json
dotnet build src/backend/YesChef.Api/YesChef.Api.csproj
dotnet ef migrations add <Name> --project src/backend/YesChef.Api
dotnet ef database update --project src/backend/YesChef.Api
```
Note: `Program.cs` calls `db.Database.MigrateAsync()` on startup, so running the API auto-applies pending migrations against the configured `ConnectionStrings:DefaultConnection`.
Backend tests (TUnit on Microsoft.Testing.Platform; integration tests use Testcontainers + Postgres). The `src/backend/global.json` opts `dotnet test` into MTP mode, which means project args must be passed via `--project` (positional project paths are rejected) and `--solution` is NOT a supported switch on the .NET 10 SDK. Run from `src/backend` so `global.json` is in scope — use `Push-Location` to avoid the blocked `cd`:
```powershell
Push-Location src/backend; try { dotnet test --project YesChef.Api.UnitTests/YesChef.Api.UnitTests.csproj } finally { Pop-Location }
Push-Location src/backend; try { dotnet test --project YesChef.Api.IntegrationTests/YesChef.Api.IntegrationTests.csproj } finally { Pop-Location }
```
Integration tests start a single Postgres 17 container per test session, create a migrated `yeschef_template` database, then clone a fresh DB per test via `CREATE DATABASE … TEMPLATE …`. Tests run in parallel (capped by `IntegrationTestParallelLimit`) and require Docker (Rancher Desktop or Docker Desktop).
Full stack via Docker (requires `.env` populated from `.env.example`):
```powershell
docker compose up -d --build
docker compose logs -f backend
```
E2E (requires frontend running on :5173 and backend reachable):
```powershell
node test-e2e.mjs
node test-e2e-multiuser.mjs
```
There is no backend test project and no test runner configured in the root `package.json``npm test` is a no-op stub.
## Architecture
### Backend — feature-folder minimal API
`Program.cs` is the composition root. It wires EF Core (`YesChefDb`), JWT bearer auth, SignalR, runs migrations, then maps endpoints by feature folder:
- `Auth/``/api/auth` register/login/me. Registration requires the `FamilyCode` config value (env `FamilyCode`). Passwords use `PasswordHasher<User>`. JWTs are signed with HS256 from `Jwt:Secret`; issuer/audience are NOT validated. The `OnMessageReceived` event also accepts `?access_token=` for the `/hubs/*` SignalR paths.
- `Features/Stores/`, `Features/ShoppingLists/`, `Features/Recipes/` — each exposes a static `Map…Endpoints(this RouteGroupBuilder)` extension. All are mapped under `RequireAuthorization()`.
- `Features/ShoppingLists/ShoppingListHub.cs` — SignalR hub at `/hubs/shopping-list` for live list updates.
- `Data/YesChefDb.cs` — single `DbContext`. Notable model rules: `User.Name` and `Store.Name` are unique; `ShoppingList → Items` cascade-deletes; `ShoppingListItem.CheckedByUser` and `ShoppingListItem.Recipe` set null on delete; `Recipe → Ingredients` cascades.
- `Auth/ClaimsPrincipalExtensions.cs``GetUserId()` / `GetUserName()` helpers used by endpoints to scope writes.
- `Migrations/` — EF Core migrations. Add new ones with `dotnet ef migrations add` (see commands above).
When adding a feature: create `Features/<Name>/<Name>Endpoints.cs` with a `Map<Name>Endpoints` extension, register the group in `Program.cs`, and add any new entities to `Entities/` plus configuration in `YesChefDb.OnModelCreating`.
### Frontend — SvelteKit with runes
- `svelte.config.js` forces `runes: true` for all non-`node_modules` files. Use Svelte 5 runes (`$state`, `$derived`, `$effect`); do not use legacy reactive `$:` syntax.
- `src/lib/api.ts``api<T>(path, opts)` helper. Stores the JWT in `localStorage` under `token`, attaches `Authorization: Bearer …`, and on 401 it clears the token and `goto('/login')`.
- `src/lib/auth.svelte.ts` — runes-based auth state.
- `src/lib/signalr.ts` — SignalR client wiring (passes the JWT via the `?access_token=` query string the backend's `OnMessageReceived` handler expects).
- `src/routes/` — file-based routes: `/login`, `/lists`, `/lists/[id]`, `/recipes`, `/recipes/new`, `/recipes/[id]`, `/stores`.
- `src/service-worker.ts` — PWA service worker.
- `$lib` is aliased to `src/lib` (see `svelte.config.js`).
- The frontend calls relative paths like `/api/...`; in production Traefik routes those to the backend on the same host. For local dev against a backend on a different port, configure a Vite proxy or run the backend behind the same origin.
### Configuration & secrets
`.env.example` documents the four required environment variables for the Docker stack: `POSTGRES_PASSWORD`, `JWT_SECRET`, `FAMILY_CODE`, `DOMAIN`. The backend reads `Jwt:Secret`, `FamilyCode`, and `ConnectionStrings:DefaultConnection` via `IConfiguration` — these can come from `appsettings.Development.json` for local runs or from environment variables (double-underscore form, e.g. `ConnectionStrings__DefaultConnection`) in containers.