Initial commit: YesChef family shopping list and recipe app

Backend (.NET 10 minimal API):
- Vertical slice architecture with feature folders
- Postgres via EF Core with initial migration
- JWT auth with family invite code registration
- REST endpoints for stores, shopping lists, items, recipes
- SignalR hub for real-time list collaboration (per-list groups
  and lists-overview group for live list creation/archival/progress)
- Multi-stage Dockerfile

Frontend (SvelteKit + Svelte 5 runes, Tailwind v4):
- Mobile-first PWA with web manifest and service worker
- Bottom-nav layout, login/register, lists overview, list detail,
  stores management, recipes (list/create/detail with add-to-list)
- SignalR client with reference-counted connection
- Real-time updates on both lists overview and list detail pages

Infrastructure:
- docker-compose.yml with postgres, backend, frontend services
  and Traefik labels for path-based routing (/api, /hubs to backend)
- .env.example with required config

End-to-end tests (Playwright):
- test-e2e.mjs: single-user flow (auth, stores, lists, items, recipes)
- test-e2e-multiuser.mjs: two-user real-time sync coverage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Josh Rogers
2026-05-06 19:32:39 -05:00
commit 48d30df07b
64 changed files with 5873 additions and 0 deletions
+60
View File
@@ -0,0 +1,60 @@
services:
postgres:
image: postgres:17
environment:
POSTGRES_DB: yeschef
POSTGRES_USER: yeschef
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- yeschef-pgdata:/var/lib/postgresql/data
expose:
- "5432"
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U yeschef"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./src/backend/YesChef.Api
dockerfile: Dockerfile
environment:
ConnectionStrings__DefaultConnection: "Host=postgres;Database=yeschef;Username=yeschef;Password=${POSTGRES_PASSWORD}"
Jwt__Secret: ${JWT_SECRET}
FamilyCode: ${FAMILY_CODE}
expose:
- "5000"
depends_on:
postgres:
condition: service_healthy
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.yeschef-api.rule=Host(`${DOMAIN}`) && (PathPrefix(`/api`) || PathPrefix(`/hubs`) || Path(`/health`))"
- "traefik.http.routers.yeschef-api.entrypoints=websecure"
- "traefik.http.routers.yeschef-api.tls.certresolver=letsencrypt"
- "traefik.http.services.yeschef-api.loadbalancer.server.port=5000"
frontend:
build:
context: ./src/frontend
dockerfile: Dockerfile
environment:
ORIGIN: https://${DOMAIN}
expose:
- "3000"
depends_on:
- backend
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.yeschef-web.rule=Host(`${DOMAIN}`)"
- "traefik.http.routers.yeschef-web.entrypoints=websecure"
- "traefik.http.routers.yeschef-web.tls.certresolver=letsencrypt"
- "traefik.http.routers.yeschef-web.priority=1"
- "traefik.http.services.yeschef-web.loadbalancer.server.port=3000"
volumes:
yeschef-pgdata: