heal-auth-schema-all-tenants.sh 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. #!/usr/bin/env bash
  2. # Heal Briven Auth schema drift on EVERY project database.
  3. #
  4. # Why: older "Enable Auth" tenants only got the first tables. Later columns
  5. # (two_factor_enabled) and tables (_briven_auth_email_templates, passkeys, …)
  6. # were missing → magic-link / email-OTP returned HTTP 500 (Mavi 2026-07-21).
  7. #
  8. # Safe to re-run. Skips DBs with no _briven_auth_users (auth never enabled).
  9. # Doltgres: no ADD COLUMN IF NOT EXISTS — we probe then ALTER.
  10. #
  11. # Usage (on the host that runs doltgres, with PGPASSWORD or .pgpass):
  12. # ./scripts/heal-auth-schema-all-tenants.sh
  13. # Or via docker:
  14. # DOLTGRES_CONTAINER=briven-…-doltgres-1 \
  15. # CONTROL_URL_FROM_API=1 \
  16. # ./scripts/heal-auth-schema-all-tenants.sh
  17. set -euo pipefail
  18. C="${DOLTGRES_CONTAINER:-briven-brivenfrance-uilsk6-doltgres-1}"
  19. API_C="${API_CONTAINER:-briven-brivenfrance-uilsk6-api-1}"
  20. if [[ -z "${PGPASSWORD:-}" ]]; then
  21. if docker inspect "$API_C" &>/dev/null; then
  22. URL=$(docker inspect "$API_C" --format '{{range .Config.Env}}{{println .}}{{end}}' | sed -n 's/^BRIVEN_DATABASE_URL=//p' | head -1)
  23. PGPASSWORD=$(printf '%s' "$URL" | python3 -c 'import sys; from urllib.parse import urlparse,unquote; u=urlparse(sys.stdin.read().strip()); print(unquote(u.password or ""))')
  24. export PGPASSWORD
  25. fi
  26. fi
  27. psqlc() {
  28. local db="$1"; shift
  29. docker exec -e PGPASSWORD="$PGPASSWORD" "$C" \
  30. psql -h 127.0.0.1 -U postgres -d "$db" -v ON_ERROR_STOP=0 -t -A -c "$1" 2>&1
  31. }
  32. mapfile -t DBS < <(docker exec -e PGPASSWORD="$PGPASSWORD" "$C" \
  33. psql -h 127.0.0.1 -U postgres -d postgres -t -A \
  34. -c "SELECT datname FROM pg_database WHERE datname LIKE 'proj_%' ORDER BY 1;")
  35. echo "healing ${#DBS[@]} project databases on $C …"
  36. healed=0
  37. skipped=0
  38. partial=0
  39. for DB in "${DBS[@]}"; do
  40. [[ -z "$DB" ]] && continue
  41. has_users=$(psqlc "$DB" "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name='_briven_auth_users';" | tr -d '[:space:]')
  42. if [[ "$has_users" != "1" ]]; then
  43. echo " $DB skip (no auth tables)"
  44. skipped=$((skipped + 1))
  45. continue
  46. fi
  47. has_col=$(psqlc "$DB" "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema='public' AND table_name='_briven_auth_users' AND column_name='two_factor_enabled';" | tr -d '[:space:]')
  48. if [[ "$has_col" != "1" ]]; then
  49. psqlc "$DB" 'ALTER TABLE "_briven_auth_users" ADD COLUMN two_factor_enabled boolean NOT NULL DEFAULT false;' >/dev/null || true
  50. fi
  51. for sql in \
  52. 'CREATE TABLE IF NOT EXISTS "_briven_auth_jwks" (id text PRIMARY KEY, public_key text NOT NULL, private_key text NOT NULL, created_at timestamptz NOT NULL DEFAULT now(), expires_at timestamptz);' \
  53. 'CREATE TABLE IF NOT EXISTS "_briven_auth_email_templates" (id text PRIMARY KEY, name text NOT NULL, subject text NOT NULL, html text NOT NULL, text text, active boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now());' \
  54. 'CREATE UNIQUE INDEX IF NOT EXISTS "_briven_auth_email_templates_name_uniq" ON "_briven_auth_email_templates" (name);' \
  55. 'CREATE TABLE IF NOT EXISTS "_briven_auth_two_factors" (id text PRIMARY KEY, secret text NOT NULL, backup_codes text NOT NULL, user_id text NOT NULL REFERENCES "_briven_auth_users"(id) ON DELETE CASCADE, verified boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now());' \
  56. 'CREATE INDEX IF NOT EXISTS "_briven_auth_two_factors_user_idx" ON "_briven_auth_two_factors" (user_id);' \
  57. 'CREATE TABLE IF NOT EXISTS "_briven_auth_passkeys" (id text PRIMARY KEY, name text, public_key text NOT NULL, user_id text NOT NULL REFERENCES "_briven_auth_users"(id) ON DELETE CASCADE, credential_id text NOT NULL, counter bigint NOT NULL DEFAULT 0, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now());' \
  58. 'CREATE INDEX IF NOT EXISTS "_briven_auth_passkeys_user_idx" ON "_briven_auth_passkeys" (user_id);'
  59. do
  60. psqlc "$DB" "$sql" >/dev/null || true
  61. done
  62. email_t=$(psqlc "$DB" "SELECT COUNT(*) FROM information_schema.tables WHERE table_name='_briven_auth_email_templates';" | tr -d '[:space:]')
  63. t2fa=$(psqlc "$DB" "SELECT COUNT(*) FROM information_schema.columns WHERE table_name='_briven_auth_users' AND column_name='two_factor_enabled';" | tr -d '[:space:]')
  64. if [[ "$email_t" == "1" && "$t2fa" == "1" ]]; then
  65. echo " $DB HEALED_OK"
  66. healed=$((healed + 1))
  67. else
  68. echo " $DB PARTIAL email_templates=$email_t two_factor_enabled=$t2fa"
  69. partial=$((partial + 1))
  70. fi
  71. done
  72. echo "done: healed_ok=$healed skipped=$skipped partial=$partial total=${#DBS[@]}"
  73. [[ "$partial" -eq 0 ]]