Files
Shane MaynardandClaude Fable 5 38c7d01e03
Test and deploy / test-and-deploy (push) Successful in 1m6s
Add brewing section, plan chat, roaster learning, API tokens, Swagger docs, and full backup
Brewing:
- roasted_beans + brews tables; /beans (bean management with LLM URL
  prefill) and /brews (silhouette brewer picker across immersion/
  percolation/espresso, recipe fields, auto ratio, 0-10 rating, tasting
  notes); bean remaining weight derived from logged brew doses
- Green inventory lot form also prefills from a product URL

Navigation/UX:
- Side nav is now generated from one definition in nav.js, grouped
  Roasting / Brewing / account, consistent on every page

LLM:
- 'Ask the LLM' chat drawer on the planner (stateless /api/plan-chat)
  grounded in the plan, computed ledger, learned pace, and a new
  roaster-behavior profile aggregated from uploaded .alogs
  (/api/roaster-profile: TP lag, phase RoR, median milestone temps)
- The profile also feeds roast reviews and the planner curve's fallback
  milestone temps

API platform:
- User-generated bearer tokens (rpt_…) with account-page management;
  token requests skip CSRF; hand-authored OpenAPI 3 spec at
  /api/openapi.json rendered by self-hosted Swagger UI at /api-docs
- Full-database backup export/import (admin) + per-user data export

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-08 22:37:42 -04:00

54 lines
2.0 KiB
SQL

-- Additive only. Brewing side of the app: roasted/purchased bean management, brew logs, and
-- user-generated API tokens. Bean remaining weight is computed (initial - sum of brew doses),
-- never stored, so it can't drift.
CREATE TABLE roasted_beans (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name text NOT NULL,
roaster text NOT NULL DEFAULT '',
origin text NOT NULL DEFAULT '',
process text NOT NULL DEFAULT '',
variety text NOT NULL DEFAULT '',
roast_level text NOT NULL DEFAULT '',
roast_date date,
initial_weight_g numeric,
url text NOT NULL DEFAULT '',
tasting_notes text NOT NULL DEFAULT '',
notes text NOT NULL DEFAULT '',
roast_plan_id uuid REFERENCES roast_plans(id) ON DELETE SET NULL,
archived boolean NOT NULL DEFAULT false,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE brews (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
bean_id uuid REFERENCES roasted_beans(id) ON DELETE SET NULL,
method text NOT NULL,
dose_g numeric,
water_g numeric,
yield_g numeric,
grinder text NOT NULL DEFAULT '',
grind_setting text NOT NULL DEFAULT '',
water_temp_c numeric,
brew_time_s integer,
bloom_time_s integer,
rating numeric,
tasting_notes text NOT NULL DEFAULT '',
notes text NOT NULL DEFAULT '',
brewed_at timestamptz NOT NULL DEFAULT now(),
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE api_tokens (
token_hash text PRIMARY KEY,
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name text NOT NULL DEFAULT '',
created_at timestamptz NOT NULL DEFAULT now(),
last_used_at timestamptz
);
CREATE INDEX roasted_beans_user ON roasted_beans(user_id, archived, created_at DESC);
CREATE INDEX brews_user ON brews(user_id, brewed_at DESC);
CREATE INDEX brews_bean ON brews(bean_id);
CREATE INDEX api_tokens_user ON api_tokens(user_id);