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:
76
web/deploy.sh
Normal file
76
web/deploy.sh
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
COMPOSE="docker compose -f docker-compose.prod.yml"
|
||||
LIVE_FILE=".live-slot"
|
||||
ROUTER_CONF="router/default.conf"
|
||||
|
||||
# ── Determine slots ─────────────────────────────────────────
|
||||
|
||||
CURRENT=$(cat "$LIVE_FILE" 2>/dev/null || echo "none")
|
||||
|
||||
if [ "$CURRENT" = "blue" ]; then
|
||||
TARGET="green"
|
||||
else
|
||||
TARGET="blue"
|
||||
fi
|
||||
|
||||
echo "==> Current: $CURRENT → Deploying: $TARGET"
|
||||
|
||||
# ── Build ───────────────────────────────────────────────────
|
||||
|
||||
echo "==> Building $TARGET..."
|
||||
$COMPOSE --profile "$TARGET" build
|
||||
|
||||
# ── Migrate ─────────────────────────────────────────────────
|
||||
|
||||
echo "==> Running migrations..."
|
||||
$COMPOSE --profile "$TARGET" run --rm "${TARGET}-app" \
|
||||
python -m beanflows.migrations.migrate
|
||||
|
||||
# ── Start & health check ───────────────────────────────────
|
||||
|
||||
echo "==> Starting $TARGET (waiting for health check)..."
|
||||
if ! $COMPOSE --profile "$TARGET" up -d --wait; then
|
||||
echo "!!! Health check failed — rolling back"
|
||||
$COMPOSE stop "${TARGET}-app" "${TARGET}-worker" "${TARGET}-scheduler"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Switch router ───────────────────────────────────────────
|
||||
|
||||
echo "==> Switching router to $TARGET..."
|
||||
mkdir -p "$(dirname "$ROUTER_CONF")"
|
||||
cat > "$ROUTER_CONF" <<NGINX
|
||||
upstream app {
|
||||
server ${TARGET}-app:5000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
proxy_pass http://app;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
}
|
||||
}
|
||||
NGINX
|
||||
|
||||
# Ensure router is running, then reload
|
||||
$COMPOSE up -d router
|
||||
$COMPOSE exec router nginx -s reload
|
||||
|
||||
# ── Stop old slot ───────────────────────────────────────────
|
||||
|
||||
if [ "$CURRENT" != "none" ]; then
|
||||
echo "==> Stopping $CURRENT..."
|
||||
$COMPOSE stop "${CURRENT}-app" "${CURRENT}-worker" "${CURRENT}-scheduler"
|
||||
fi
|
||||
|
||||
# ── Record live slot ────────────────────────────────────────
|
||||
|
||||
echo "$TARGET" > "$LIVE_FILE"
|
||||
echo "==> Deployed $TARGET successfully!"
|
||||
Reference in New Issue
Block a user