tracksolid_timescale_grafan.../deploy_dashboard_api.sh
david kiania aea226cc74
Some checks failed
Static Analysis / static (push) Waiting to run
Tests / test (push) Waiting to run
Static Analysis / static (pull_request) Has been cancelled
Tests / test (pull_request) Has been cancelled
feat(db): split refresher onto REFRESH_DATABASE_URL; prod reads via dashboard_ro
Enables stage-2: the prod dashboard_api request pool connects as the READ-ONLY
dashboard_ro role (DATABASE_URL) while the v_trips refresher keeps a privileged
connection via REFRESH_DATABASE_URL (falls back to DATABASE_URL when unset, so
single-role/staging deploys are unchanged). Avoids the FIX-D02 trap (a read-only
role cannot REFRESH).

Adds deploy_dashboard_api.sh (the prod bridge deploy, now version-controlled):
strips inherited DATABASE_URL, sets REFRESH_DATABASE_URL=<app role> +
DATABASE_URL=dashboard_ro, CORS incl. fleetops.rahamafresh.com.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 20:19:40 +03:00

70 lines
4.5 KiB
Bash
Executable file

#!/usr/bin/env bash
# deploy_dashboard_api.sh — PROD dashboard_api bridge (fleetapi.rahamafresh.com).
# Standalone Traefik-labelled bridge (NOT Coolify-managed): reuses the
# webhook_receiver image + app network, bind-mounts the WIP API file. An env/CORS
# change needs a container RECREATE (this script does that).
#
# Stage-2 least privilege: the request pool connects as the READ-ONLY dashboard_ro
# role (DATABASE_URL), while the v_trips refresher keeps the privileged app role
# (REFRESH_DATABASE_URL) — REFRESH needs write perms that dashboard_ro lacks.
set -euo pipefail
WH=$(docker ps --filter name=webhook_receiver --format "{{.Names}}" | head -1)
IMG=$(docker inspect "$WH" --format "{{.Image}}")
APPNET=$(docker inspect "$WH" --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}}{{end}}')
echo "Reusing image $IMG on network $APPNET (from $WH)"
mkdir -p /home/kianiadee/dashboard_api
# Stage a fresh copy only if one was scp'd to ~; otherwise keep the existing mount.
if [ -f /home/kianiadee/dashboard_api_rev.py ]; then
mv -f /home/kianiadee/dashboard_api_rev.py /home/kianiadee/dashboard_api/dashboard_api_rev.py
fi
test -f /home/kianiadee/dashboard_api/dashboard_api_rev.py \
|| { echo "ERROR: dashboard_api_rev.py missing in mount dir; scp it to ~ first"; exit 1; }
# Reuse the webhook container's env, stripping runtime noise AND any inherited
# DATABASE_URL + DASHBOARD_CORS_ORIGINS (both set explicitly below).
docker inspect "$WH" --format '{{range .Config.Env}}{{println .}}{{end}}' \
| grep -vE '^(PATH=|HOSTNAME=|HOME=|PWD=|TERM=|SHLVL=|_=|LANG=|GPG_KEY=|PYTHON_VERSION=|PYTHON_PIP_VERSION=|PYTHONUNBUFFERED=|DATABASE_URL=|DASHBOARD_CORS_ORIGINS=)' \
> /home/kianiadee/dashboard_api/dapi.env
echo 'DASHBOARD_CORS_ORIGINS=https://liveposition.rahamafresh.com,https://fleetintelligence.rahamafresh.com,https://fleetnow.rahamafresh.com,https://fleetops.rahamafresh.com' \
>> /home/kianiadee/dashboard_api/dapi.env
# Split roles: REFRESH_DATABASE_URL = the inherited privileged URL (for the
# refresher); DATABASE_URL = read-only dashboard_ro (for request handling).
SRC_DB_URL=$(docker inspect "$WH" --format '{{range .Config.Env}}{{println .}}{{end}}' | sed -n 's/^DATABASE_URL=//p' | head -1)
RO_PW=$(cat /home/kianiadee/.dashboard_ro.pw 2>/dev/null || true)
[ -n "$SRC_DB_URL" ] || { echo "ERROR: DATABASE_URL not found in $WH env"; exit 1; }
[ -n "$RO_PW" ] || { echo "ERROR: ~/.dashboard_ro.pw missing — run bootstrap_dashboard_ro.sh first"; exit 1; }
HOSTPART="${SRC_DB_URL#*@}"
{
echo "REFRESH_DATABASE_URL=${SRC_DB_URL}"
echo "DATABASE_URL=postgresql://dashboard_ro:${RO_PW}@${HOSTPART}"
} >> /home/kianiadee/dashboard_api/dapi.env
chmod 600 /home/kianiadee/dashboard_api/dapi.env
docker rm -f dashboard_api 2>/dev/null || true
docker run -d --name dashboard_api --restart unless-stopped \
--network "$APPNET" \
--env-file /home/kianiadee/dashboard_api/dapi.env \
-v /home/kianiadee/dashboard_api/dashboard_api_rev.py:/app/dashboard_api_rev.py:ro \
--label 'traefik.enable=true' \
--label 'traefik.docker.network=coolify' \
--label 'traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https' \
--label 'traefik.http.routers.http-0-fleetapi.entryPoints=http' \
--label 'traefik.http.routers.http-0-fleetapi.middlewares=redirect-to-https' \
--label 'traefik.http.routers.http-0-fleetapi.rule=Host(`fleetapi.rahamafresh.com`)' \
--label 'traefik.http.routers.https-0-fleetapi.entryPoints=https' \
--label 'traefik.http.routers.https-0-fleetapi.rule=Host(`fleetapi.rahamafresh.com`)' \
--label 'traefik.http.routers.https-0-fleetapi.tls=true' \
--label 'traefik.http.routers.https-0-fleetapi.tls.certresolver=letsencrypt' \
--label 'traefik.http.services.fleetapi.loadbalancer.server.port=8890' \
"$IMG" sh -c 'uvicorn dashboard_api_rev:app --host 0.0.0.0 --port 8890 --workers 2'
docker network connect coolify dashboard_api 2>/dev/null || true
sleep 8
echo "== container =="; docker ps --filter name=dashboard_api --format "{{.Names}} | {{.Status}}"
echo "== CORS origins in effect =="; docker exec dashboard_api printenv DASHBOARD_CORS_ORIGINS
echo "== request role (expect dashboard_ro) =="; docker exec dashboard_api sh -lc 'printenv DATABASE_URL | sed -E "s#://([^:]+):[^@]+@#://\1:<pw>@#"'
echo "== refresh role set? =="; docker exec dashboard_api sh -lc 'printenv REFRESH_DATABASE_URL | sed -E "s#://([^:]+):[^@]+@#://\1:<pw>@#"'
echo "== internal health =="; docker exec dashboard_api sh -lc 'curl -s http://localhost:8890/health' 2>&1 | head || true