From 236e71f0f025979a5943c76f7cb5e8866b96bafd Mon Sep 17 00:00:00 2001 From: defiQUG Date: Thu, 9 Apr 2026 01:23:19 -0700 Subject: [PATCH] feat(portal): merge IT_READ_API_* from repo .env to CT 7801 - Add sankofa-portal-merge-it-read-api-env-from-repo.sh (base64-safe upsert + restart) - Document in SANKOFA_IT_OPS_KEYCLOAK_PORTAL_NEXT_STEPS.md Made-with: Cursor --- ...NKOFA_IT_OPS_KEYCLOAK_PORTAL_NEXT_STEPS.md | 2 +- ...-portal-merge-it-read-api-env-from-repo.sh | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100755 scripts/deployment/sankofa-portal-merge-it-read-api-env-from-repo.sh diff --git a/docs/03-deployment/SANKOFA_IT_OPS_KEYCLOAK_PORTAL_NEXT_STEPS.md b/docs/03-deployment/SANKOFA_IT_OPS_KEYCLOAK_PORTAL_NEXT_STEPS.md index d175c2b..e028ba2 100644 --- a/docs/03-deployment/SANKOFA_IT_OPS_KEYCLOAK_PORTAL_NEXT_STEPS.md +++ b/docs/03-deployment/SANKOFA_IT_OPS_KEYCLOAK_PORTAL_NEXT_STEPS.md @@ -22,7 +22,7 @@ 3. **Do not** expose `IT_READ_API_KEY` or Proxmox credentials to the browser bundle. 4. Display **`collected_at`** from JSON; show a stale warning if older than your SLO (e.g. 24h). -**Deploy:** `scripts/deployment/sync-sankofa-portal-7801.sh` after portal changes. +**Deploy:** `scripts/deployment/sync-sankofa-portal-7801.sh` after portal changes. Merge env without editing the CT by hand: `scripts/deployment/sankofa-portal-merge-it-read-api-env-from-repo.sh` (requires `IT_READ_API_URL` in repo `.env`). --- diff --git a/scripts/deployment/sankofa-portal-merge-it-read-api-env-from-repo.sh b/scripts/deployment/sankofa-portal-merge-it-read-api-env-from-repo.sh new file mode 100755 index 0000000..ee6d985 --- /dev/null +++ b/scripts/deployment/sankofa-portal-merge-it-read-api-env-from-repo.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# Upsert IT_READ_API_URL and optional IT_READ_API_KEY into LXC 7801 +# (/opt/sankofa-portal/.env and .env.local) for portal /it API routes. +# Values come from repo .env (via load-project-env.sh). Uses base64 on the SSH +# line so special characters in URL or key do not break the shell. +# +# Usage: +# ./scripts/deployment/sankofa-portal-merge-it-read-api-env-from-repo.sh [--dry-run] [--no-restart] +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +# shellcheck source=/dev/null +source "${PROJECT_ROOT}/scripts/lib/load-project-env.sh" 2>/dev/null || true +# shellcheck source=/dev/null +source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true + +PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}" +VMID="${SANKOFA_PORTAL_VMID:-7801}" +CT_DIR="${SANKOFA_PORTAL_CT_DIR:-/opt/sankofa-portal}" +SERVICE_NAME="${SANKOFA_PORTAL_SERVICE:-sankofa-portal}" +SSH_OPTS=(-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new) + +DRY_RUN=false +NO_RESTART=false +for arg in "$@"; do + case "$arg" in + --dry-run) DRY_RUN=true ;; + --no-restart) NO_RESTART=true ;; + esac +done + +if [[ -z "${IT_READ_API_URL:-}" ]]; then + echo "ERROR: IT_READ_API_URL is not set. Add to repo .env (see .env.master.example), e.g." >&2 + echo " IT_READ_API_URL=http://192.168.11.11:8787" >&2 + echo " IT_READ_API_KEY=... # optional, if read API uses X-API-Key" >&2 + exit 1 +fi + +if $DRY_RUN; then + echo "[DRY-RUN] Would upsert IT_READ_API_* on CT ${VMID} ${CT_DIR}/.env and .env.local" + echo "[DRY-RUN] IT_READ_API_URL=${IT_READ_API_URL}" + [[ -n "${IT_READ_API_KEY:-}" ]] && echo "[DRY-RUN] IT_READ_API_KEY=(set)" || echo "[DRY-RUN] IT_READ_API_KEY=(unset)" + echo "[DRY-RUN] restart: $([[ "$NO_RESTART" == true ]] && echo no || echo yes)" + exit 0 +fi + +URL_B64="$(printf '%s' "$IT_READ_API_URL" | base64 -w0)" +KEY_B64="" +if [[ -n "${IT_READ_API_KEY:-}" ]]; then + KEY_B64="$(printf '%s' "$IT_READ_API_KEY" | base64 -w0)" +fi + +ssh "${SSH_OPTS[@]}" "root@${PROXMOX_HOST}" \ + "pct exec ${VMID} -- env URL_B64='${URL_B64}' KEY_B64='${KEY_B64}' CT_DIR='${CT_DIR}' python3 -" <<'PY' +import base64 +import os +import re +from pathlib import Path + +url = base64.b64decode(os.environ["URL_B64"]).decode("utf-8") +kb = os.environ.get("KEY_B64") or "" +key = base64.b64decode(kb).decode("utf-8") if kb.strip() else "" +ct = Path(os.environ["CT_DIR"]) +keys = {"IT_READ_API_URL": url} +if key: + keys["IT_READ_API_KEY"] = key + + +def upsert(text: str, k: str, v: str) -> str: + line = f"{k}={v}" + if re.search(rf"^{re.escape(k)}=", text, flags=re.M): + return re.sub(rf"^{re.escape(k)}=.*$", line, text, flags=re.M, count=1) + if text and not text.endswith("\n"): + text += "\n" + return text + line + "\n" + + +for fname in (".env", ".env.local"): + p = ct / fname + body = p.read_text() if p.exists() else "" + for k, v in keys.items(): + body = upsert(body, k, v) + p.parent.mkdir(parents=True, exist_ok=True) + p.write_text(body) + print(f"upserted IT read API keys in {p}") +PY + +if [[ "$NO_RESTART" == true ]]; then + echo "[ok] IT read API vars merged on CT ${VMID} (no service restart)." +else + ssh "${SSH_OPTS[@]}" "root@${PROXMOX_HOST}" \ + "pct exec ${VMID} -- systemctl restart ${SERVICE_NAME} && pct exec ${VMID} -- systemctl is-active ${SERVICE_NAME}" + echo "[ok] IT read API vars merged on CT ${VMID}; ${SERVICE_NAME} restarted." +fi