munch-ease-backend/README.md
2025-11-05 20:24:47 +11:00

146 lines
6.1 KiB
Markdown
Raw Permalink 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.

## Doof Backend (aka Munch Ease) 🍽️
FastAPI backend for collaborative meal planning, recipe wrangling, and grocery shopping. Household-scoped, JWT-secured, SQLite-fast. Bring your recipes, well do the rest.
## Why youll love it
- 🚀 Fast and modern API with FastAPI + Pydantic v2
- 🏠 Household-scoped everything (recipes, meals, shopping) for clean multi-user isolation
- 🔐 JWT access tokens + HttpOnly refresh cookie (Argon2 password hashing)
- 🧪 100% test-friendly: ephemeral SQLite, deterministic APIs, RFC7807 errors
- 🧠 Ingredient NLP parsing with product matching
- 🛒 One-click shopping lists: request meals or individual ingredients, dedup done for you
- 🧾 OpenAPI on tap for your frontend and SDKs
- 🔗 Built-in recipe parsing from URLs (with BeautifulSoup + httpx)
## Stack
- Runtime: Python 3.11+
- Web: FastAPI, Starlette
- Data models: Pydantic v2 (camelCase JSON via custom `ApiModel`)
- Database: SQLite (aiosqlite), schema bootstrapped in each `repository.py`
- Auth: Custom HMAC-SHA256 JWTs + Argon2 password hashing
- Parsing/Scraping: `ingredient-parser-nlp`, `beautifulsoup4`, `httpx`
- Tooling: ruff (lint+format), mypy (typecheck), pytest, pre-commit, uvicorn
## Features at a glance
- 🔑 Auth: Register, login, refresh, logout; access bearer token + HttpOnly refresh cookie
- 🏡 Households: Create, list, invite members; accept invitations via shareable links
- 🧾 Recipes: Create, list, paginate, delete (hide) with actor attribution; parse-from-url helper
- 🧪 Ingredients: NLP parse one or many lines; product matching baked in
- 🍽️ Meals: Plan, get, update, delete, mark consumed; validate participants and recipes
- 🛍️ Shopping: Request meals and ingredients, view current list, purchase to a list, fetch past lists
- 🩺 Health: `GET /healthz` returns a tiny “ok” model for probes
- 📜 Errors: RFC7807 Problem Details everywhere, with tidy camelCase payloads
## Project structure
- `main.py` — FastAPI app factory, routers, exception handlers, health, frontend proxy/static
- `settings.py` — Environment-driven runtime settings (no external deps)
- `security.py` — Minimal JWT utilities (HS256) + helpers
- `db.py` — aiosqlite connect + `create()` bootstraps all domain tables
- `api/` — HTTP surface (versioned under `/api/v1`) - STRICTLY NO SQL AT THIS LAYER!
- `auth.py` — register, login, refresh, logout
- `households.py` — create/list, members, invitations, scoped routes
- `ingredients.py` — household-scoped NLP parsing
- `recipes.py` — household-scoped list/get/create/delete, public utilities
- `meals.py` — household-scoped CRUD + mark consumed
- `shopping.py` — household-scoped current list, purchase, request/unrequest
- `openapi.py` — OpenAPI augmentation (cookie auth, problem+json)
- `deps.py` — DB/session, auth, household scoping, error helpers
- Domain packages (models + repository + sql mutation + helpers):
- `persons/`
- `users/`, `households/`, `ingredients/`, `recipes/` (incl. `scraping.py`), `meals/`, `products/` (Coles/Woolworths helpers), `shopping/`
- Strive to be useful as a fairly portal package independant of the http layer
- `scripts/` — Utility scripts
- `export_openapi.py` — writes `openapi.json` from the live app
- `manual_parse_recipes.py` — Test parsing against a variety of sources
- `tests/` — API and domain tests with fixtures and sample files
## Quickstart
First time setup:
```bash
make install
```
Run the dev server:
```bash
make dev
```
Run tests:
```bash
make test
```
Run all checks (lint, typecheck, tests, format check, OpenAPI export):
```bash
make all-checks
```
## Environment
These are read from the environment (see `settings.py`):
- `DOOF_DB` — SQLite file path (default `./data/doof.sqlite`)
- `DOOF_PROD``true/false` controls frontend proxy vs. static serving (default `false`)
- `FRONTEND_DEV_URL` — dev server to reverse-proxy in non-prod (default `http://localhost:8000/`)
- `DOOF_JWT_ISSUER`, `DOOF_JWT_AUDIENCE` — JWT claims
- `DOOF_JWT_ACCESS_TTL`, `DOOF_JWT_REFRESH_TTL` — TTLs in seconds (default 900/2592000)
- `DOOF_JWT_ACCESS_SECRET_B64`, `DOOF_JWT_REFRESH_SECRET_B64` — base64 secrets (use in prod!)
Dev convenience: if secrets arent provided, deterministic dev secrets are used. Dont ship those.
## API surface (v1)
- Base: `/api/v1`
- Auth: `/auth/register`, `/auth/login`, `/auth/refresh`, `/auth/logout`
- Households: `/households`, `/users/me/households`, `/households/{slug}/members`, `/households/{slug}/whoami`, invitations create/accept
- Ingredients: `/households/{slug}/ingredients/parse` (single or batch parsing)
- Recipes: `/households/{slug}/recipes` (list/paged, create), `/{id}` (get/delete), `/parse-from-url`
- Meals: `/households/{slug}/meals` (create/update/delete/get/upcoming/mark-consumed)
- Shopping: `/households/{slug}/shopping/current`, `/{listId}`, request/unrequest meals and ingredients, purchase lists
- Health: `/healthz`
Errors are consistent Problem Details (`application/problem+json`). Models serialize in camelCase.
## Make targets
- `make install` — Create venv and install dependencies
- `make dev` — Run dev server (`uvicorn main:app --reload`)
- `make test` — Run tests (pytest -q)
- `make format` — Format with ruff
- `make lint` — Lint with ruff
- `make typecheck` — Type check with mypy
- `make openapi` — Export OpenAPI to `openapi.json`
- `make all-checks` — Lint + typecheck + tests + format check + OpenAPI export
- `make clean` — Remove venv and caches
## Development notes
- Schema bootstrap: `db.create(conn)` calls each features `repository.create` to make tables
- Frontend integration:
- Dev: requests for non-`/api/*` are reverse-proxied to `FRONTEND_DEV_URL`
- Prod: static files served from `./front-dist`
- Security: Argon2 password hashing; HS256 JWTs signed with your secrets; refresh token stored as HttpOnly cookie
- DX niceties: camelCase JSON by default, strict validation, helpful error messages
## OpenAPI
Generate the spec file used by the frontend and CI:
```bash
make openapi
```
This writes `openapi.json` at the repo root.
---
Built with love and leftovers. Hungry for issues and PRs. 🧑‍🍳