New feature: photograph a recipe card, cookbook page, or handwritten note and have it extracted into a structured, editable recipe. Backend: - POST /recipes/scan: accept 1-4 photos, run VLM extraction, return structured JSON for review (not auto-saved) - POST /recipes/scan/save: persist a reviewed/edited recipe - GET/DELETE /recipes/user: user-created recipe CRUD - Vision backend priority: cf-orch -> local Qwen2.5-VL -> Anthropic BYOK - 503 with clear config hint when no vision backend available - Multi-photo support: facing pages (ingredients/directions) sent together - Pantry cross-reference: marks which ingredients are already on hand - migration 041: user_recipes table (title, servings, cook_time, steps, ingredients JSON, source, pantry_match_pct) - Tier gate: recipe_scan -> paid, BYOK-unlockable Frontend: - "Scan" button in the Recipes tab bar (camera icon) - RecipeScanModal: upload step (drag-drop + file picker, up to 4 photos, live previews), processing step (spinner), review/edit step (all fields inline-editable before save), pantry match badge, warning banner for low-confidence or incomplete scans Tests: 35 new tests (23 unit + 12 API), 404 total passing
23 lines
1.2 KiB
SQL
23 lines
1.2 KiB
SQL
-- Migration 041: user_recipes table for user-scanned and manually-entered recipes.
|
|
--
|
|
-- Separate from the food.com corpus (recipes table) -- user recipes are personal,
|
|
-- not curated, and need different fields (servings as string, cook_time as string).
|
|
|
|
CREATE TABLE IF NOT EXISTS user_recipes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
title TEXT NOT NULL,
|
|
subtitle TEXT,
|
|
servings TEXT, -- kept as string: "2", "4-6", "serves 8"
|
|
cook_time TEXT, -- kept as string: "25 min", "1 hour"
|
|
source_note TEXT, -- e.g. "Purple Carrot", "Betty Crocker"
|
|
ingredients TEXT NOT NULL DEFAULT '[]', -- JSON: [{name, qty, unit, raw}]
|
|
steps TEXT NOT NULL DEFAULT '[]', -- JSON: ["step 1", "step 2", ...]
|
|
notes TEXT,
|
|
tags TEXT DEFAULT '[]', -- JSON: ["vegan", "quick"]
|
|
source TEXT NOT NULL DEFAULT 'manual', -- 'scan' | 'manual'
|
|
pantry_match_pct INTEGER, -- 0-100, computed at scan time; null for manual
|
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_user_recipes_created ON user_recipes (created_at DESC);
|