No description
  • Go 52.7%
  • TypeScript 39.7%
  • CSS 7%
  • Shell 0.3%
  • JavaScript 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
roberto 8823bea8bf
Some checks failed
CI / backend (push) Failing after 11m33s
CI / frontend (push) Successful in 3m1s
CI / production-compose (push) Successful in 5s
CI / publish-images (./backend, tidy-cook-backend) (push) Has been skipped
CI / publish-images (./deploy/backup, tidy-cook-backup) (push) Has been skipped
CI / publish-images (./frontend, tidy-cook-frontend) (push) Has been skipped
Add visual UX and interaction audit
2026-10-12 20:55:02 +00:00
.github/workflows Improve import fact diagnostics and report build commits 2026-10-12 10:21:30 +00:00
backend Improve import fact diagnostics and report build commits 2026-10-12 10:21:30 +00:00
deploy Use HTTPS Forgejo container registry 2026-08-22 22:39:10 +01:00
docs Add visual UX and interaction audit 2026-10-12 20:55:02 +00:00
frontend Improve import fact diagnostics and report build commits 2026-10-12 10:21:30 +00:00
.dockerignore Build recipe app stack 2026-02-08 00:48:16 +00:00
.env.production.example Improve recipe import AI resilience 2026-08-11 21:18:59 +01:00
.gitignore test(import-ui): cover source, decision and editor workflows 2026-08-02 00:34:45 +01:00
CLAUDE.md Show manual and carried-over items, with a rollover banner and undo 2026-09-29 11:09:33 +00:00
docker-compose.production.yml Show administrators the technical details of a failed import 2026-09-29 19:19:03 +00:00
docker-compose.yml Publish The Tidy Cook images from CI 2026-07-22 22:47:59 +01:00
IMPORTER_V3_FOUNDATION_ARCHIVE.md Accept conventional oven-temperature rounding in the rewrite's fact check 2026-09-29 21:11:40 +00:00
PRODUCTION_CONFIG.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_DATA_MODEL.md Publish The Tidy Cook images from CI 2026-07-22 22:47:59 +01:00
PRODUCTION_DEPLOYMENT_PANGOLIN.md Publish The Tidy Cook images from CI 2026-07-22 22:47:59 +01:00
PRODUCTION_FRONTEND_AUDIT.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_JOBS.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_OPERATIONS_KOMODO.md Publish The Tidy Cook images from CI 2026-07-22 22:47:59 +01:00
PRODUCTION_RELEASE_CHECKLIST.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_RELEASE_RUNBOOK.md Release 1.0.2 2026-08-11 00:52:34 +01:00
PRODUCTION_RESTORE_DRILL.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_ROUTE_MATRIX.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_SECRET_POLICY.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_SECRET_ROTATION.md Update docs for the new auth, backup and test behaviour 2026-09-28 22:01:20 +00:00
PRODUCTION_SMOKE_TESTS.md Publish The Tidy Cook images from CI 2026-07-22 22:47:59 +01:00
PRODUCTION_TASKS.md Patch reachable vulnerabilities and add security scans to CI 2026-09-28 21:59:33 +00:00
README.md Show manual and carried-over items, with a rollover banner and undo 2026-09-29 11:09:33 +00:00

The Tidy Cook (React + Go)

A full-stack recipe database application with:

  • React frontend (modern recipe-book UI) exposed on http://localhost:3500
  • Go backend API with SQLite persistence
  • Normalized ingredient catalog with metric-first unit normalization
  • Searchable recipes and Gousto-style recipe detail view
  • Optional CoFID-powered nutrition calculations per recipe

Stack

  • Frontend: React + Vite + TypeScript + CSS
  • Backend: Go + Chi + SQLite
  • Containerization: Docker + Docker Compose

Data model

The backend creates normalized tables automatically:

  • units: conversion metadata (g, kg, ml, l, tsp, tbsp, pc)
  • ingredients: normalized ingredient catalog with base unit
  • recipes: recipe metadata
  • recipe_steps: ordered instructions
  • recipe_ingredients: display quantity + normalized/base quantity
  • cofid_foods: imported CoFID food-code catalog
  • cofid_nutrients: nutrient values per 100g food
  • ingredient_nutrition_map: ingredient to CoFID match cache

If a recipe uses an ingredient not in the catalog, it can be typed manually and is auto-added. Ingredient categories are normalized against a backend-owned canonical vocabulary so seeded ingredients, manual edits, and recipe imports stay aligned.

Run with Docker

docker compose up --build

Then open:

  • Frontend: http://localhost:3500

API endpoints

  • Session authentication is required for application endpoints unless an endpoint is explicitly described as public.
  • Administrative endpoints require an authenticated user with the admin role.
  • GET /api/health
  • GET /api/ready
  • GET /api/auth/me
  • POST /api/auth/login
  • POST /api/auth/logout
  • GET /api/ingredients?query=
  • GET /api/ingredients/categories
  • POST /api/ingredients
  • GET /api/recipes?query= (single search across title, tags, ingredients)
  • GET /api/recipes/{id}
  • GET /api/recipes/{id}/nutrition?servings=
  • POST /api/recipes
  • PUT /api/recipes/{id}
  • DELETE /api/recipes/{id}
  • POST /api/images (upload local recipe image, returns /uploads/... URL)
  • POST /api/pantry/process (session-authenticated; accepts JSON like {"prompt":"add 2 milk","use_ai":true}, form fields like input=..., or raw text/plain input)
  • POST /api/shopping/voice (dedicated shopping:add bearer token; accepts JSON like {"utterance":"two litres of milk and six eggs"})
  • GET|POST|DELETE /api/settings/siri-token (session-authenticated status, generate/regenerate, and revoke operations; the raw token is returned only when generated)
  • GET /api/backup/export (admin session, or the ADMIN_API_TOKEN machine token used by the scheduled backup; download backup archive)
  • POST /api/backup/import (admin session only; restore from backup archive. Writes a copy of the live database to data/pre-restore/ first)
  • POST /api/week-rollover/undo ({"kind":"shopping|pantry","start_date":"YYYY-MM-DD"}: take back what was carried into the current week)
  • POST /api/carryover/keep ({"kind":"shopping|pantry","id":123}: restart the stale clock on an item you still want)
  • POST /api/import-sessions/{id}/retry (restart a failed import from its saved source; body {"use_ai": false} retries without AI)
  • GET /api/auth/sessions, DELETE /api/auth/sessions/{id}, POST /api/auth/sessions/revoke-others (list and sign out the caller's own devices; sessions also expire after SESSION_IDLE_TTL, default 7 days idle)
  • POST /api/nutrition/cofid/import (upload CoFID .xlsx)
  • GET /api/nutrition/status
  • POST /api/admin/review-recipes (normalize all or selected recipes, optional nutrition refresh)
  • POST /api/admin/openfoodfacts/sync (import OFF ingredient/category/label references)
  • GET /api/admin/openfoodfacts/status

Backup format

The Tidy Cook backups use .recipobak (ZIP archive) with:

  • manifest.json:
    • format: "recipopedia-backup"
    • version: "2.0"
    • exported_at (RFC3339 UTC)
  • database.v2.json: complete allowlisted durable-table dump, including accounts, settings, recipes, planning, shopping, pantry, imports, nutrition, and barcode data
  • uploads/: all locally stored recipe images

Rebuildable reference data is not exported: the Open Food Facts taxonomy (taxonomy_terms), the CoFID tables and the ingredient nutrition match cache. After restoring onto a fresh instance, re-upload the CoFID .xlsx and run the Open Food Facts sync if you use them. Archives made by older releases that include those tables still restore.

Active sessions and one-time password-reset tokens are intentionally excluded. Restoring a backup invalidates existing sessions while preserving user accounts and password hashes. Version 1 full-backup archives are rejected because they do not contain all durable application domains.

Local development (without Docker)

Backend

cd backend
go mod tidy
go run .

Frontend

cd frontend
npm install
npm run dev

Frontend dev server runs on port 3500.

Generate Ingredient Seed Candidates

You can generate a ranked ingredient seed candidate file from the current SQLite database:

cd backend
go run ./scripts/generate_ingredient_seed.go \
  --db ./data/recipopedia.db \
  --output ./seeds/ingredients.generated.json \
  --version ingredients.generated.v1 \
  --limit 500

Useful flags:

  • --min-recipes 2: only include ingredients used in at least 2 distinct recipes
  • --include-unused: include ingredients even if they have no recipe usage yet
  • --include-custom=false: exclude ingredients currently categorized as custom
  • --include-supermarket: include preferred_supermarket in the generated seed

The output schema matches the bundled ingredient seed format used at startup.

Ingredient Seed Releases

Installation-time ingredient population is driven by bundled seed files in backend/seeds.

Startup behavior:

  • the backend scans bundled ingredients.vN.json files
  • the highest bundled version is selected automatically
  • that seed is applied once per database via seed_versions
  • later startups are idempotent for the same seed version

Recommended release workflow for a larger default catalog:

  1. Generate a candidate file, for example ingredients.v2.json.
  2. Review and curate canonical names, categories, units, aisles, and aliases.
  3. Commit the new seed file into backend/seeds/.
  4. Release the app. Fresh installs will pick the highest bundled seed automatically.
  5. Existing installs will apply the new version once on next startup because seed_versions tracks applied versions.

This keeps installation offline and deterministic: the app does not need to fetch USDA or Open Food Facts during install.

Production deployment

The product name is The Tidy Cook; recipopedia remains the repository and legacy internal data-format identifier. The first production target is a single Linux host using Docker Compose with Pangolin as the authenticated HTTPS entrypoint. See PRODUCTION_DEPLOYMENT_PANGOLIN.md for the Newt-site and same-host local-site layouts.

Successful main CI runs publish attested linux/amd64 and linux/arm64 backend, frontend, and backup images under ghcr.io/wantxapas/tidy-cook-*. Production selects an immutable sha-<full-commit> image tag for deployment and rollback.

For the complete version bump, validation, image verification, GitHub release, deployment, and rollback sequence, see PRODUCTION_RELEASE_RUNBOOK.md.

Komodo scheduling, encrypted SSH/NAS backups, and Uptime Kuma monitoring are covered in PRODUCTION_OPERATIONS_KOMODO.md.

The v1 authorization and ownership boundary is documented in PRODUCTION_DATA_MODEL.md.

Shopping list and pantry: carrying items into a new week

Shopping items and pantry stock belong to a planning week. The first time the current week is opened, what was not finished is carried into it once:

  • Shopping: manual items that were not bought (added in the app, through the API, the Siri shortcut or a suggestion) move on unchanged. Bought and removed items stay behind. Manual items show a Manual label and, when they came from elsewhere, via Siri / API / Telegram; added_via is set by the server and an API client may send "added_via":"api" when adding an item.
  • Pantry: stock moves on minus whatever that week's planned recipes use, so 500 g of flour with 200 g planned carries 300 g; stock nothing planned uses carries whole; stock the plan used up does not carry.
  • Only the current week is ever rolled into: a future week cannot pull an item forward while it can still be bought this week, and a past week is history. Each source item is stamped once carried, so re-opening never duplicates it and a deleted copy never comes back.
  • Carried items show Carried from 12 Oct. After 4 weeks an item is flagged Still needed? (it keeps rolling) with a Keep them action, and editing an item also restarts the clock. A banner above the list summarises the carry and offers Undo. Undo removes the copies (taking back any quantity merged into an existing line) and leaves the originals in their old week.