Personal nutrition tracker — Next.js + Drizzle + d3 over PostgreSQL, sized to run (app + postgres + nginx) inside a 1 GB GCP e2-micro.
- Dashboard — three d3 charts over
daily_summaries: calories vs weight (dual axis, 30-day rolling average, morning/evening weight band), macro breakdown (stacked kcal: protein / other carbs / sugar / other fat / saturated fat), and nutrients normalized to % of RDI. - Daily Entry — searchable food + meal selector with live nutrient preview, inline-editable log entries, collapsible meal entries, calories remaining, 8 macro progress bars vs per-nutrient target/limit modes, sticky daily targets, and independent morning/evening weight tracking.
- Food Database — master-detail CRUD with per-100g / per-item unit types.
- Weekly Planner — plan a week of meals (paint one meal across many days), see weekly macros + per-day verdicts, apply today's plan into the log (today-only), and compare plan vs actual. Plan is intent, kept separate from the log.
- Auth — Google sign-in (Auth.js, JWT sessions) with an email allowlist.
- Next.js (App Router, standalone output) + TypeScript
- Drizzle ORM + postgres.js (pool max 4 — tuned for the 1 GB VM)
- d3 submodules only (
d3-scale,d3-shape,d3-array) - vitest + PGlite (data-layer tests run against the real schema)
- PostgreSQL 15 — schema in
database/init.sql(unchanged from v1;updated_atis trigger-maintained, never set by the app)
# 1. local postgres (applies database/init.sql on first run)
docker compose up -d
# 2. env
cp .env.example .env # fill in Google OAuth credentials + allowlist
# 3. run
npm install
npm run dev # http://localhost:3000Tests and checks:
npm test # vitest: domain logic + PGlite data layer
npm run typecheck
npm run build # standalone production buildThese mirror the original Python app and are guarded by tests in
tests/data/storage.test.ts and the parity scripts in scripts/parity/:
- An empty day writes NULL nutrients to
daily_summaries, never 0. saveDailyEntrynever touches the weight columns; weights are written only byupdateMeasurements.- Saving a day is delete-all-for-date + reinsert; meal entries are
food_entriesrows sharing ameal_id. - Daily targets are sticky: existing row → most recent earlier row → defaults.
Production is a GCP e2-micro (1 GB RAM) running postgres + nginx + the app.
next build never runs on the VM — CI (or scripts/deploy.sh) builds the
standalone output and rsyncs it to ~/apps/nutritional/releases/<sha>,
then repoints the current symlink and restarts the systemd unit
(deploy/nutritional-next.service, memory-capped at 448 MB).
- Deploy & operate (routine):
deploy/README.md - First-time VM setup:
deploy/SETUP.md - DB ops (backup/restore/tuning):
database/—db.sh,README.md,postgresql.conf.template
0 comments
log in to comment.