Skip to content

Repository files navigation

Macro Tracker

Note

This application was built largely as an exercise in using an LLM to code. Nearly all of the code is AI-generated. This should also explain some of the peculiarities of the project, including the minimal file structure (which was chose to make code changes more efficient for rapid development). Everything from here on out in this README is AI-generated.

Personal macro tracking web app: React + TypeScript + Vite frontend, FastAPI + sqlite backend, served from a single port. Designed to run locally on a Mac and be exposed on the public internet via Cloudflare Tunnel + Cloudflare Access (gated by Google SSO + passkey) using the lab-server orchestrator.

Architecture history: originally built on Supabase (cloud Postgres + auth). Migrated to local sqlite + FastAPI on 2026-05-09 — auth removed (Cloudflare Access handles it at the edge), data lives in backend/data/macro.db.

Features

  • Real-time macro tracking with progress bars (protein, carbs, fat).
  • Workout vs. rest day macro targets.
  • Food database with frequency tracking.
  • Historical preservation: edits to a food's macros only affect future entries; past food_entries snapshot the values at log time.
  • Mobile-first responsive layout.

Tech stack

  • Frontend: React 18 + TypeScript + Vite. Built once via npm run build → static assets in dist/.
  • Backend: FastAPI + sqlite (Python 3.11+). Serves both /api/* JSON endpoints and the built frontend's static files from a single uvicorn process — bound to the Unix socket ~/.lab-sockets/macro.sock when lab-served, or a local TCP port for manual dev.
  • Auth: none in-app. Cloudflare Access at the edge gates macro.emmilco.com behind the operator's Google account + passkey.
  • Hosting: registered as the macro sub-app in lab-server's apps.toml. Lab-server is launchd-managed, always-on while the Mac is awake; cloudflared routes macro.emmilco.com to the lab auth gateway, which authenticates the request and forwards it to the app's Unix socket ~/.lab-sockets/macro.sock.

Layout

macro-tracker/
├── src/                  # React frontend
│   ├── App.tsx           # Main component (no auth — Cloudflare Access handles it)
│   ├── App.module.css    # Styles
│   ├── api.ts            # fetch-based wrapper for /api/* (replaces former supabase.ts)
│   ├── types.ts          # TypeScript interfaces
│   └── main.tsx          # React entry point
├── backend/              # Python backend (NEW since Path B migration)
│   ├── server.py         # FastAPI app: /api/* endpoints + static-file serving
│   ├── init_db.py        # Migration: parse Supabase SQL exports → local sqlite
│   ├── requirements.txt  # fastapi, uvicorn
│   ├── .venv/            # virtualenv (gitignored)
│   └── data/
│       └── macro.db      # sqlite (gitignored)
├── database/             # Legacy Supabase schema (reference only; not used at runtime)
│   ├── database-schema.sql
│   ├── add-auth-schema.sql
│   └── update-foods.sql
├── dist/                 # Built React assets (gitignored, generated by `npm run build`)
├── index.html            # Vite entry
├── package.json
├── vite.config.ts
└── tsconfig.json

Running locally (manual)

# 1. Backend deps + venv
cd backend
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

# 2. Frontend deps + build
cd ..
npm install
npm run build      # writes to dist/

# 3. Start the server
backend/.venv/bin/uvicorn backend.server:app --host 127.0.0.1 --port 7100

To hit the lab-served instance directly (no auth needed for same-user local access): curl --unix-socket ~/.lab-sockets/macro.sock http://localhost/ (or the helper ~/Documents/GitHub/lab-server/labcurl macro /). For active hot-reload development, run a throwaway TCP dev server — add --reload to the uvicorn command above and visit http://127.0.0.1:7100/. The FastAPI app serves the React app at / and JSON endpoints at /api/*.

Running via lab-server (production)

The macro sub-app is registered in lab-server/apps.toml:

[apps.macro]
working_dir = "/Users/elliotmilco/Documents/GitHub/macro-tracker"
start_command = "backend/.venv/bin/uvicorn backend.server:app --uds /Users/elliotmilco/.lab-sockets/macro.sock"
enabled = true

When the lab-server LaunchAgent is loaded, this starts automatically and the app is reachable at https://macro.emmilco.com (after Google SSO via Cloudflare Access).

To pick up frontend changes after editing React code: npm run build, then launchctl kickstart -k gui/$(id -u)/com.emmilco.lab-server to restart the daemon (which restarts uvicorn with the new dist/).

Database

Single sqlite file at backend/data/macro.db. Tables:

  • foods — master food database with frequency tracking.
  • daily_entries — daily logs with day_type (workout / rest).
  • food_entries — per-day food consumption with snapshotted food data for historical preservation.
  • user_settings — workout / rest macro targets (single row, single user).

Schema file (sqlite-flavored) is created by backend/init_db.py at first run.

Initial data migration (one-time, already done)

backend/init_db.py parses PostgreSQL INSERT exports from the legacy Supabase project and imports them into local sqlite. The migration filtered to a single user (multi-user fields were dropped since auth is now external). Re-running with --reset drops the local db and re-imports.

API surface

JSON endpoints exposed by backend/server.py:

Method Path Purpose
GET /api/foods list, ordered by frequency desc
POST /api/foods create new food
PATCH /api/foods/{id} update food
DELETE /api/foods/{id} delete food
POST /api/foods/{id}/increment-frequency bump usage counter
GET /api/daily-entries/by-date/{date} get day's entry (or null)
POST /api/daily-entries create day entry
PATCH /api/daily-entries/{id} update day type
GET /api/daily-entries/historical?exclude={date} list history
GET /api/food-entries?daily_entry_id={id} list day's foods
POST /api/food-entries log a food
PATCH /api/food-entries/{id} update multiplier
DELETE /api/food-entries/{id} delete a food log
GET /api/settings get macro targets
PUT /api/settings update macro targets

The src/api.ts module mirrors these as typed TypeScript functions.

Default macro targets

  • Workout Day: 180g protein, 250g carbs, 80g fat (~2,040 cal)
  • Rest Day: 180g protein, 150g carbs, 100g fat (~1,940 cal)

Both editable in the settings page (writes via PUT /api/settings).

Why the migration

The legacy Supabase project was paused (free-tier inactivity) and increasingly fragile (third-party auth in front of a personal app, vendor lock-in, slow cold starts). Migrating to local sqlite + Cloudflare Access:

  • Single Mac is the source of truth — no cloud dependency.
  • Cloudflare Access provides stronger auth (Google + passkey) than Supabase's password-based flow, and is shared across every other lab-server app.
  • Free (no Supabase tier, no Netlify deploy).
  • Faster (no cold start, sqlite is microseconds).

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages