tracksolid_timescale_grafan.../scripts/bootstrap_dashboard_ro.sh
david kiania cbbe3dab87 feat(db): dedicated read-only dashboard_ro role + repoint staging
Replaces the grafana_ro reuse with a purpose-built least-privilege login role
that can serve the FULL dashboard_api read surface — so it backs the staging
instance now and can take over the live prod connection later (stage 2).

scripts/dashboard_ro_role.sql (run as postgres, password-free in repo):
  - CREATE ROLE dashboard_ro LOGIN, read-only
  - SELECT on reporting.* + tracksolid.*; explicit SELECT on the
    reporting.v_trips MATERIALIZED VIEW (not covered by GRANT ON ALL TABLES)
  - EXECUTE on reporting.fn_* map functions
  - ALTER DEFAULT PRIVILEGES so future objects are auto-readable ("dynamic")

scripts/bootstrap_dashboard_ro.sh:
  - generates the password into ~/.dashboard_ro.pw (0600), never printed
  - applies the DDL via docker exec psql -U postgres -v ro_pw=...

deploy_dashboard_api_staging.sh: build DATABASE_URL from dashboard_ro +
~/.dashboard_ro.pw instead of grafana_ro.

Migrations 17/18 (already applied) are left intact. Not yet executed on host.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 12:33:53 +03:00

38 lines
2.1 KiB
Bash
Executable file

#!/usr/bin/env bash
# bootstrap_dashboard_ro.sh — create/refresh the dashboard_ro read-only role.
# ─────────────────────────────────────────────────────────────────────────────
# Run ON THE HOST. Generates a strong password into ~/.dashboard_ro.pw (0600) on
# first run (reused thereafter), then applies scripts/dashboard_ro_role.sql to the
# prod DB as the postgres superuser. The password is NEVER printed and never
# leaves the host — the staging deploy script reads the same ~/.dashboard_ro.pw.
#
# Deploy:
# scp scripts/dashboard_ro_role.sql scripts/bootstrap_dashboard_ro.sh \
# kianiadee@twala.rahamafresh.com:~/
# ssh kianiadee@twala.rahamafresh.com 'bash ~/bootstrap_dashboard_ro.sh'
#
# Idempotent: re-running rotates nothing unless ~/.dashboard_ro.pw is deleted
# first (then it generates + sets a fresh password and you must redeploy the API).
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
PW_FILE="${DASHBOARD_RO_PW_FILE:-$HOME/.dashboard_ro.pw}"
SQL_FILE="${1:-$HOME/dashboard_ro_role.sql}"
test -f "$SQL_FILE" || { echo "ERROR: role SQL not found at $SQL_FILE (scp scripts/dashboard_ro_role.sql to ~ first)"; exit 1; }
if [ ! -s "$PW_FILE" ]; then
( umask 077; openssl rand -hex 24 > "$PW_FILE" )
chmod 600 "$PW_FILE"
echo "Generated new dashboard_ro password -> $PW_FILE (0600)"
else
echo "Reusing existing dashboard_ro password from $PW_FILE"
fi
PW=$(cat "$PW_FILE")
DB=$(docker ps --filter name=timescale_db --format "{{.Names}}" | head -1)
[ -n "$DB" ] || { echo "ERROR: timescale_db container not found"; exit 1; }
echo "Applying dashboard_ro role DDL to $DB as postgres ..."
docker exec -i "$DB" psql -U postgres -d tracksolid_db -v ON_ERROR_STOP=1 -v ro_pw="$PW" < "$SQL_FILE"
echo "dashboard_ro ready (password not printed). Now (re)run deploy_dashboard_api_staging.sh."