Add BeanFlows MVP: coffee analytics dashboard, API, and web app

- Fix pipeline granularity: add market_year to cleaned/serving SQL models
- Add DuckDB data access layer with async query functions (analytics.py)
- Build Chart.js dashboard: supply/demand, STU ratio, top producers, YoY table
- Add country comparison page with multi-select picker
- Replace items CRUD with read-only commodity API (list, metrics, countries, CSV)
- Configure BeanFlows plan tiers (Free/Starter/Pro) with feature gating
- Rewrite public pages for coffee market intelligence positioning
- Remove boilerplate items schema, update health check for DuckDB
- Add test suite: 139 tests passing (dashboard, API, billing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Deeman
2026-02-18 16:11:50 +01:00
parent b222c01828
commit 2748c606e9
59 changed files with 6272 additions and 2 deletions

128
web/docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,128 @@
services:
# ── Always-on infrastructure ──────────────────────────────
router:
image: nginx:alpine
restart: unless-stopped
ports:
- "5000:80"
volumes:
- ./router/default.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- net
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 5s
litestream:
image: litestream/litestream:latest
restart: unless-stopped
command: replicate -config /etc/litestream.yml
volumes:
- app-data:/app/data
- ./beanflows/litestream.yml:/etc/litestream.yml:ro
# ── Blue slot ─────────────────────────────────────────────
blue-app:
profiles: ["blue"]
build:
context: ./beanflows
restart: unless-stopped
env_file: ./beanflows/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
blue-worker:
profiles: ["blue"]
build:
context: ./beanflows
restart: unless-stopped
command: python -m beanflows.worker
env_file: ./beanflows/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
blue-scheduler:
profiles: ["blue"]
build:
context: ./beanflows
restart: unless-stopped
command: python -m beanflows.worker scheduler
env_file: ./beanflows/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
# ── Green slot ────────────────────────────────────────────
green-app:
profiles: ["green"]
build:
context: ./beanflows
restart: unless-stopped
env_file: ./beanflows/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:5000/health')"]
interval: 10s
timeout: 5s
retries: 3
start_period: 15s
green-worker:
profiles: ["green"]
build:
context: ./beanflows
restart: unless-stopped
command: python -m beanflows.worker
env_file: ./beanflows/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
green-scheduler:
profiles: ["green"]
build:
context: ./beanflows
restart: unless-stopped
command: python -m beanflows.worker scheduler
env_file: ./beanflows/.env
environment:
- DATABASE_PATH=/app/data/app.db
volumes:
- app-data:/app/data
networks:
- net
volumes:
app-data:
networks:
net: