Initial project scaffold: FastAPI backend + Svelte 5 frontend

Backend (uv): FastAPI app with routers (foods, log, targets, OFF proxy),
SQLAlchemy models, Pydantic schemas, migration runner + 0001 initial schema,
example pytest suite (7 tests).
Frontend (npm): Vite 7 + Svelte 5 runes, lib/ (api, stores, scanner, format)
per spec §7, placeholder components, example vitest suite (4 tests).
SPEC.md §1: registered uvicorn and Vitest (rule §8.3.3).
This commit is contained in:
Craig
2026-07-26 10:25:59 +01:00
commit e047d884b6
47 changed files with 3994 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
"""OpenFoodFacts proxy (spec §3.5). All OFF calls go through this router.
OFF etiquette (spec §8.1 rule 10): descriptive User-Agent, timeouts, and
kcal-vs-kJ normalization in exactly one module. Tests mock at the httpx
boundary — never hit the real OFF API (spec §8.4).
"""
import httpx
from fastapi import APIRouter
OFF_BASE_URL = "https://world.openfoodfacts.org"
OFF_USER_AGENT = "CalCount/0.1 (personal self-hosted calorie counter)"
OFF_TIMEOUT = 10.0
router = APIRouter(prefix="/api/off", tags=["off"])
@router.get("/product/{barcode}")
def get_product(barcode: str):
"""Proxy a product lookup by barcode. Returns raw OFF JSON for now.
TODO: normalize to our foods schema (spec §3.5) in one shared module.
"""
resp = httpx.get(
f"{OFF_BASE_URL}/api/v2/product/{barcode}",
headers={"User-Agent": OFF_USER_AGENT},
timeout=OFF_TIMEOUT,
)
resp.raise_for_status()
return resp.json()