feat(sankofa): public web CT 7806, portal NPM/DNS defaults, Keycloak redirect helper

- Provision/sync scripts and systemd for corporate Next on 7806; IP_SANKOFA_PUBLIC_WEB for apex NPM
- Portal stack: NEXTAUTH_URL default portal.sankofa.nexus; NPM fleet + migrate + DNS ordering
- keycloak-sankofa-ensure-client-redirects.sh (KEYCLOAK_ADMIN_PASSWORD); .env.master.example hints
- Docs: task list, inventory, FQDN/E2E/EXPECTED_WEB_CONTENT, AGENTS pointers

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-29 13:41:02 -07:00
parent 50809f75dc
commit 7f3dcf2513
20 changed files with 600 additions and 49 deletions

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Enable working login on https://sankofa.nexus:
# Enable working login on the client portal hostname (https://portal.sankofa.nexus; NEXTAUTH_URL):
# - Fix Keycloak systemd (JAVA_HOME line; hostname + proxy headers for NPM).
# - Remove .env.local on CT 7801; install .env with PORTAL_LOCAL_LOGIN_* + NEXTAUTH_SECRET.
# - Run sync-sankofa-portal-7801.sh (rebuild portal with updated auth.ts).
@@ -32,7 +32,7 @@ cat > "$ENV_TMP" <<EOF
NEXT_PUBLIC_GRAPHQL_ENDPOINT=http://192.168.11.50:4000/graphql
NEXT_PUBLIC_GRAPHQL_WS_ENDPOINT=ws://192.168.11.50:4000/graphql-ws
NEXTAUTH_URL=https://sankofa.nexus
NEXTAUTH_URL=https://portal.sankofa.nexus
NEXTAUTH_SECRET=${NEXTAUTH_SEC}
KEYCLOAK_URL=https://keycloak.sankofa.nexus
KEYCLOAK_REALM=master
@@ -85,7 +85,7 @@ bash "${SCRIPT_DIR}/sync-sankofa-portal-7801.sh"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Sign in at https://sankofa.nexus"
echo "✅ Sign in at https://portal.sankofa.nexus (NEXTAUTH_URL)"
echo " Email: ${LOCAL_EMAIL}"
echo " Password: ${GEN_PASS}"
echo ""

View File

@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# Ensure Keycloak OIDC client has redirect URIs and web origins for portal/admin hostnames.
# Uses Admin REST API (password grant on master realm). Set secrets in .env (not committed).
#
# Required env: KEYCLOAK_ADMIN_PASSWORD
# Optional: KEYCLOAK_URL (default https://keycloak.sankofa.nexus), KEYCLOAK_ADMIN (default admin),
# KEYCLOAK_REALM (default master), KEYCLOAK_CLIENT_ID (default sankofa-portal)
#
# Usage: from repo root with .env loaded:
# ./scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh
# ./scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh --dry-run
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
if [ -f "$PROJECT_ROOT/.env" ]; then
set +u
set -a
# shellcheck source=/dev/null
source "$PROJECT_ROOT/.env" 2>/dev/null || true
set +a
set -u
fi
KEYCLOAK_URL="${KEYCLOAK_URL:-https://keycloak.sankofa.nexus}"
REALM="${KEYCLOAK_REALM:-master}"
CLIENT_ID="${KEYCLOAK_CLIENT_ID:-sankofa-portal}"
ADMIN_USER="${KEYCLOAK_ADMIN:-admin}"
ADMIN_PASS="${KEYCLOAK_ADMIN_PASSWORD:-}"
DRY=0
[[ "${1:-}" == "--dry-run" ]] && DRY=1
if [ -z "$ADMIN_PASS" ]; then
echo "KEYCLOAK_ADMIN_PASSWORD is not set. Add it to .env (or export) and re-run." >&2
echo "Without it, update the client manually: Valid redirect URIs + Web origins for portal/admin." >&2
exit 1
fi
TOKEN_JSON=$(curl -sS -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=admin-cli" \
-d "username=${ADMIN_USER}" \
-d "password=${ADMIN_PASS}")
TOKEN=$(echo "$TOKEN_JSON" | jq -r '.access_token // empty')
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
echo "Failed to obtain admin token (check URL, user, password)." >&2
echo "$TOKEN_JSON" | jq -r '.error_description // .error // .' >&2
exit 1
fi
CLIENT_LIST=$(curl -sS -G "${KEYCLOAK_URL}/admin/realms/${REALM}/clients" \
-H "Authorization: Bearer ${TOKEN}" \
--data-urlencode "clientId=${CLIENT_ID}")
INTERNAL_ID=$(echo "$CLIENT_LIST" | jq -r '.[0].id // empty')
if [ -z "$INTERNAL_ID" ] || [ "$INTERNAL_ID" = "null" ]; then
echo "No client with clientId=${CLIENT_ID} in realm ${REALM}." >&2
exit 1
fi
FULL=$(curl -sS "${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${INTERNAL_ID}" \
-H "Authorization: Bearer ${TOKEN}")
DESIRED_REDIRECTS='[
"https://portal.sankofa.nexus/*",
"https://portal.sankofa.nexus",
"https://admin.sankofa.nexus/*",
"https://admin.sankofa.nexus"
]'
DESIRED_ORIGINS='[
"https://portal.sankofa.nexus",
"https://admin.sankofa.nexus"
]'
MERGED=$(echo "$FULL" | jq --argjson dr "$DESIRED_REDIRECTS" --argjson wo "$DESIRED_ORIGINS" '
.redirectUris = ((.redirectUris // []) + $dr | unique) |
.webOrigins = ((.webOrigins // []) + $wo | unique)
')
if [ "$DRY" = 1 ]; then
echo "[dry-run] Would set client ${CLIENT_ID} (${INTERNAL_ID}) to:"
echo "$MERGED" | jq '{clientId, redirectUris, webOrigins}'
exit 0
fi
HTTP_CODE=$(curl -sS -o /tmp/kc_put_body.txt -w "%{http_code}" -X PUT \
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${INTERNAL_ID}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d "$MERGED")
if [[ "$HTTP_CODE" != "204" && "$HTTP_CODE" != "200" ]]; then
echo "PUT client failed HTTP ${HTTP_CODE}" >&2
cat /tmp/kc_put_body.txt >&2 || true
exit 1
fi
rm -f /tmp/kc_put_body.txt
echo "Updated Keycloak client ${CLIENT_ID}: redirect URIs and web origins merged (portal + admin)."

View File

@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# Create LXC 7806 (sankofa-public-web) for corporate Next.js at repo root → sankofa.nexus via IP_SANKOFA_PUBLIC_WEB.
# Installs Node 20 + pnpm and systemd unit; then run sync-sankofa-public-web-to-ct.sh and NPM fleet update.
#
# Usage (from repo root, SSH to r630-01):
# bash scripts/deployment/provision-sankofa-public-web-lxc-7806.sh [--dry-run]
#
# Env: PROXMOX_HOST, SANKOFA_PUBLIC_WEB_VMID (7806), IP_SANKOFA_PUBLIC_WEB_CT (default 192.168.11.63)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# 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_PUBLIC_WEB_VMID:-7806}"
IP_CT="${IP_SANKOFA_PUBLIC_WEB_CT:-192.168.11.63}"
HOSTNAME_CT="${SANKOFA_PUBLIC_WEB_HOSTNAME:-sankofa-public-web}"
TEMPLATE="${TEMPLATE:-local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst}"
STORAGE="${STORAGE:-local-lvm}"
NETWORK="${NETWORK:-vmbr0}"
GATEWAY="${NETWORK_GATEWAY:-192.168.11.1}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
SERVICE_FILE="${PROJECT_ROOT}/config/systemd/sankofa-public-web.service"
DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
echo "=== Provision Sankofa public web LXC ${VMID} (${IP_CT}) ==="
echo "Proxmox: ${PROXMOX_HOST}"
if [[ ! -f "$SERVICE_FILE" ]]; then
echo "ERROR: Missing $SERVICE_FILE"
exit 1
fi
if $DRY_RUN; then
echo "[DRY-RUN] Would create CT ${VMID} if missing, install Node/pnpm, install systemd unit"
exit 0
fi
if ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct list 2>/dev/null | grep -q '^${VMID} '"; then
echo "CT ${VMID} already exists — skipping pct create"
else
echo "Creating CT ${VMID} (${HOSTNAME_CT}) @ ${IP_CT}/24..."
ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s <<EOF
set -euo pipefail
pct create ${VMID} ${TEMPLATE} \
--hostname ${HOSTNAME_CT} \
--memory 6144 \
--cores 2 \
--rootfs ${STORAGE}:32 \
--net0 name=eth0,bridge=${NETWORK},ip=${IP_CT}/24,gw=${GATEWAY} \
--nameserver ${DNS_PRIMARY:-1.1.1.1} \
--description 'Sankofa corporate public web (Next.js root) — sankofa.nexus via NPM IP_SANKOFA_PUBLIC_WEB' \
--start 1 \
--onboot 1 \
--unprivileged 0
EOF
echo "Waiting for CT to boot..."
sleep 20
fi
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct status ${VMID}" | grep -q running || { echo "ERROR: CT ${VMID} not running"; exit 1; }
echo "Installing Node 20 + pnpm inside CT ${VMID}..."
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- bash -lc '
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl gnupg
if ! command -v node >/dev/null 2>&1 || ! node -v | grep -q \"^v20\"; then
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y -qq nodejs
fi
command -v pnpm >/dev/null 2>&1 || npm install -g pnpm
mkdir -p /opt/sankofa-public-web
'"
echo "Installing systemd unit..."
scp $SSH_OPTS "$SERVICE_FILE" "root@${PROXMOX_HOST}:/tmp/sankofa-public-web.service"
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct push ${VMID} /tmp/sankofa-public-web.service /etc/systemd/system/sankofa-public-web.service && rm -f /tmp/sankofa-public-web.service"
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- systemctl daemon-reload"
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- systemctl enable sankofa-public-web"
echo "✅ CT ${VMID} ready. Next:"
echo " SANKOFA_PUBLIC_WEB_VMID=${VMID} bash scripts/deployment/sync-sankofa-public-web-to-ct.sh"
echo " Then set IP_SANKOFA_PUBLIC_WEB=${IP_CT} and run scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh"

View File

@@ -14,7 +14,7 @@ PROXMOX_HOST="${PROXMOX_HOST:-${PROXMOX_HOST_R630_01:-192.168.11.11}}"
VMID="${SANKOFA_PORTAL_VMID:-7801}"
CT_APP_DIR="${SANKOFA_PORTAL_CT_DIR:-/opt/sankofa-portal}"
SERVICE_NAME="${SANKOFA_PORTAL_SERVICE:-sankofa-portal}"
NEXTAUTH_PUBLIC_URL="${SANKOFA_PORTAL_NEXTAUTH_URL:-https://sankofa.nexus}"
NEXTAUTH_PUBLIC_URL="${SANKOFA_PORTAL_NEXTAUTH_URL:-https://portal.sankofa.nexus}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
ssh $SSH_OPTS "root@${PROXMOX_HOST}" "pct exec ${VMID} -- bash -s" <<EOF

View File

@@ -6,7 +6,7 @@
# ./scripts/deployment/sync-sankofa-portal-7801.sh [--dry-run]
# Env:
# PROXMOX_HOST (default 192.168.11.11), SANKOFA_PORTAL_VMID (7801), SANKOFA_PORTAL_SRC, IP_SANKOFA_PORTAL (for post-check only)
# SANKOFA_PORTAL_NEXTAUTH_URL (default https://sankofa.nexus) — applied on CT after build
# SANKOFA_PORTAL_NEXTAUTH_URL (default https://portal.sankofa.nexus) — applied on CT after build
#
# See: docs/03-deployment/PUBLIC_SECTOR_LIVE_DEPLOYMENT_CHECKLIST.md (Phoenix CT 7801)
@@ -97,14 +97,14 @@ REMOTE_EOF
echo ""
echo "🔐 Ensuring NextAuth URL/secret on CT (see sankofa-portal-ensure-nextauth-on-ct.sh)…"
SANKOFA_PORTAL_NEXTAUTH_URL="${SANKOFA_PORTAL_NEXTAUTH_URL:-https://sankofa.nexus}"
SANKOFA_PORTAL_NEXTAUTH_URL="${SANKOFA_PORTAL_NEXTAUTH_URL:-https://portal.sankofa.nexus}"
export SANKOFA_PORTAL_VMID SANKOFA_PORTAL_CT_DIR SANKOFA_PORTAL_SERVICE SANKOFA_PORTAL_NEXTAUTH_URL PROXMOX_HOST
bash "${SCRIPT_DIR}/sankofa-portal-ensure-nextauth-on-ct.sh"
echo ""
echo "✅ Done. Verify:"
echo " curl -sS http://${IP_SANKOFA_PORTAL:-192.168.11.51}:3000/ | head -c 120"
echo " curl -sSI https://sankofa.nexus/api/auth/signin | head -n 15"
echo " https://sankofa.nexus/ (via NPM)"
echo " curl -sSI https://portal.sankofa.nexus/api/auth/signin | head -n 15"
echo " https://portal.sankofa.nexus/ (via NPM; corporate apex is sankofa.nexus → IP_SANKOFA_PUBLIC_WEB)"
echo ""
echo "Override public auth URL: SANKOFA_PORTAL_NEXTAUTH_URL=https://portal.sankofa.nexus $0"
echo "Legacy apex auth URL only if needed: SANKOFA_PORTAL_NEXTAUTH_URL=https://sankofa.nexus $0"

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env bash
# Sync Sankofa repo-root Next.js app (corporate / marketing site) to a dedicated LXC for apex sankofa.nexus.
# Does not run NextAuth portal setup — use sync-sankofa-portal-7801.sh for client SSO on portal.sankofa.nexus.
#
# Prerequisites: SSH root@PROXMOX_HOST; sibling repo at ../Sankofa (root package.json + src/app + public/).
# On the CT: install systemd unit config/systemd/sankofa-public-web.service → /etc/systemd/system/ and enable.
#
# Usage:
# ./scripts/deployment/sync-sankofa-public-web-to-ct.sh [--dry-run]
# Env:
# PROXMOX_HOST, SANKOFA_PUBLIC_WEB_VMID (default 7806), SANKOFA_PUBLIC_WEB_SRC, SANKOFA_PUBLIC_WEB_CT_DIR, SANKOFA_PUBLIC_WEB_SERVICE
#
# After first deploy: set IP_SANKOFA_PUBLIC_WEB + SANKOFA_PUBLIC_WEB_PORT in config/ip-addresses.conf (or .env) and run
# scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# 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_PUBLIC_WEB_VMID:-7806}"
CT_APP_DIR="${SANKOFA_PUBLIC_WEB_CT_DIR:-/opt/sankofa-public-web}"
SERVICE_NAME="${SANKOFA_PUBLIC_WEB_SERVICE:-sankofa-public-web}"
SSH_OPTS="-o BatchMode=yes -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new"
DEFAULT_SRC="${PROJECT_ROOT}/../Sankofa"
if [[ -d "$DEFAULT_SRC" && -f "$DEFAULT_SRC/package.json" ]]; then
SANKOFA_PUBLIC_WEB_SRC="${SANKOFA_PUBLIC_WEB_SRC:-$DEFAULT_SRC}"
else
SANKOFA_PUBLIC_WEB_SRC="${SANKOFA_PUBLIC_WEB_SRC:-}"
fi
DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
echo "=== Sync Sankofa public (repo root) → CT ${VMID} (${CT_APP_DIR}) ==="
echo "Proxmox: ${PROXMOX_HOST}"
echo "Source: ${SANKOFA_PUBLIC_WEB_SRC:-<unset>}"
echo ""
if [[ -z "$SANKOFA_PUBLIC_WEB_SRC" || ! -d "$SANKOFA_PUBLIC_WEB_SRC" ]]; then
echo "ERROR: Set SANKOFA_PUBLIC_WEB_SRC to the Sankofa monorepo root (parent of portal/)."
echo "Example: SANKOFA_PUBLIC_WEB_SRC=/path/to/Sankofa $0"
exit 1
fi
if ! command -v tar >/dev/null; then
echo "ERROR: tar required"
exit 1
fi
TMP_TGZ="${TMPDIR:-/tmp}/sankofa-public-web-sync-$$.tgz"
REMOTE_TGZ="/tmp/sankofa-public-web-sync-$$.tgz"
CT_TGZ="/tmp/sankofa-public-web-sync.tgz"
cleanup() { rm -f "$TMP_TGZ"; }
trap cleanup EXIT
if $DRY_RUN; then
echo "[DRY-RUN] tar (exclude node_modules,.next,.git) → $TMP_TGZ"
echo "[DRY-RUN] scp → root@${PROXMOX_HOST}:${REMOTE_TGZ}"
echo "[DRY-RUN] pct push ${VMID} … && systemctl stop ${SERVICE_NAME}"
echo "[DRY-RUN] pnpm install && pnpm build && systemctl start ${SERVICE_NAME}"
exit 0
fi
echo "📦 Archiving Sankofa repo root (excluding node_modules, .next, .git, .env / .env.local)…"
tar czf "$TMP_TGZ" \
--exclude=node_modules \
--exclude=.next \
--exclude=portal/node_modules \
--exclude=portal/.next \
--exclude=.git \
--exclude=.env.local \
--exclude=.env \
-C "$SANKOFA_PUBLIC_WEB_SRC" .
echo "📤 Copy to Proxmox host…"
scp $SSH_OPTS "$TMP_TGZ" "root@${PROXMOX_HOST}:${REMOTE_TGZ}"
echo "📥 Push into CT ${VMID} and build…"
ssh $SSH_OPTS "root@${PROXMOX_HOST}" bash -s <<REMOTE_EOF
set -euo pipefail
pct push ${VMID} ${REMOTE_TGZ} ${CT_TGZ}
rm -f ${REMOTE_TGZ}
pct exec ${VMID} -- systemctl stop ${SERVICE_NAME} || true
pct exec ${VMID} -- bash -lc 'set -euo pipefail
mkdir -p ${CT_APP_DIR}
cd ${CT_APP_DIR}
tar xzf ${CT_TGZ}
rm -f ${CT_TGZ}
command -v pnpm >/dev/null || { echo "ERROR: pnpm missing in CT"; exit 1; }
pnpm install
pnpm build
'
pct exec ${VMID} -- systemctl start ${SERVICE_NAME}
pct exec ${VMID} -- systemctl is-active ${SERVICE_NAME}
REMOTE_EOF
echo ""
echo "✅ Done. Point NPM apex with IP_SANKOFA_PUBLIC_WEB / SANKOFA_PUBLIC_WEB_PORT, then:"
echo " bash scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh"
echo " curl -sS http://${IP_SANKOFA_PUBLIC_WEB:-<CT_IP>}:${SANKOFA_PUBLIC_WEB_PORT:-3000}/ | head -c 120"

View File

@@ -284,24 +284,34 @@ create_proxy_host() {
return 0
}
# Configure all 19 domains
echo "🚀 Starting domain configuration (19 domains)..."
# Configure core domains (count varies; sankofa zone includes portal/admin/dash when dash IP set)
echo "🚀 Starting domain configuration..."
echo ""
SUCCESS=0
FAILED=0
# sankofa.nexus (5 domains) — portal :3000 / Phoenix API :4000 (not Blockscout; explorer is IP_BLOCKSCOUT:80)
# sankofa.nexus zone — corporate apex (IP_SANKOFA_PUBLIC_WEB), Phoenix API :4000, client SSO (portal/admin), optional dash (IP_SANKOFA_DASH). Not Blockscout.
IP_SANKOFA_PORTAL="${IP_SANKOFA_PORTAL:-${IP_SERVICE_51:-192.168.11.51}}"
IP_SANKOFA_PHOENIX_API="${IP_SANKOFA_PHOENIX_API:-${IP_SERVICE_50:-192.168.11.50}}"
SANKOFA_PORTAL_PORT="${SANKOFA_PORTAL_PORT:-3000}"
SANKOFA_PHOENIX_API_PORT="${SANKOFA_PHOENIX_API_PORT:-4000}"
IP_SANKOFA_PUBLIC_WEB="${IP_SANKOFA_PUBLIC_WEB:-${IP_SANKOFA_PORTAL}}"
SANKOFA_PUBLIC_WEB_PORT="${SANKOFA_PUBLIC_WEB_PORT:-${SANKOFA_PORTAL_PORT}}"
IP_SANKOFA_CLIENT_SSO="${IP_SANKOFA_CLIENT_SSO:-${IP_SANKOFA_PORTAL}}"
SANKOFA_CLIENT_SSO_PORT="${SANKOFA_CLIENT_SSO_PORT:-${SANKOFA_PORTAL_PORT}}"
IP_ORDER_HAPROXY="${IP_ORDER_HAPROXY:-192.168.11.39}"
create_proxy_host "sankofa.nexus" "http" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.sankofa.nexus" "http" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "sankofa.nexus" "http" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.sankofa.nexus" "http" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "phoenix.sankofa.nexus" "http" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "www.phoenix.sankofa.nexus" "http" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "the-order.sankofa.nexus" "http" "${IP_ORDER_HAPROXY}" "80" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "portal.sankofa.nexus" "http" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
create_proxy_host "admin.sankofa.nexus" "http" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
if [[ -n "${IP_SANKOFA_DASH:-}" ]]; then
SANKOFA_DASH_PORT="${SANKOFA_DASH_PORT:-3000}"
create_proxy_host "dash.sankofa.nexus" "http" "${IP_SANKOFA_DASH}" "${SANKOFA_DASH_PORT}" "false" && ((SUCCESS++)) || ((FAILED++))
fi
# d-bis.org (9 domains)
create_proxy_host "explorer.d-bis.org" "http" "${IP_BLOCKSCOUT:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-${IP_DEVICE_14:-192.168.11.14}}}}}0}" "80" "false" && ((SUCCESS++)) || ((FAILED++))

View File

@@ -373,15 +373,19 @@ update_proxy_host "dbis.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_
update_proxy_host "iccc.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_DEV}:3002" false && updated_count=$((updated_count + 1)) || { add_proxy_host "iccc.xom-dev.phoenix.sankofa.nexus" "${IP_GOV_PORTALS_DEV}" 3002 false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
update_proxy_host "omnl.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_DEV}:3003" false && updated_count=$((updated_count + 1)) || { add_proxy_host "omnl.xom-dev.phoenix.sankofa.nexus" "${IP_GOV_PORTALS_DEV}" 3003 false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
update_proxy_host "xom.xom-dev.phoenix.sankofa.nexus" "http://${IP_GOV_PORTALS_DEV}:3004" false && updated_count=$((updated_count + 1)) || { add_proxy_host "xom.xom-dev.phoenix.sankofa.nexus" "${IP_GOV_PORTALS_DEV}" 3004 false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
# Sankofa portal (Next.js CT 7801) and Phoenix API (Fastify CT 7800) — not Blockscout / SolaceScanScout (that is explorer.d-bis.org / IP_BLOCKSCOUT:80)
# Public web intent: sankofa.nexus = Sankofa Sovereign Technologies; phoenix.sankofa.nexus = Phoenix Cloud Services (division). Client SSO: admin / portal + keycloak IdP. Operator: dash (IP+MFA). See docs/02-architecture/EXPECTED_WEB_CONTENT.md.
# Sankofa / Phoenix — not Blockscout (explorer.d-bis.org / IP_BLOCKSCOUT:80).
# Intent: sankofa.nexus = corporate marketing (IP_SANKOFA_PUBLIC_WEB); portal.sankofa.nexus + admin = client SSO (IP_SANKOFA_CLIENT_SSO); phoenix = division + API. dash = operator (set IP_SANKOFA_DASH to manage). See docs/02-architecture/EXPECTED_WEB_CONTENT.md.
# www.sankofa.nexus → 301 https://sankofa.nexus$request_uri; www.phoenix → phoenix; www.the-order → the-order (NPM advanced_config).
IP_SANKOFA_PORTAL="${IP_SANKOFA_PORTAL:-${IP_SERVICE_51:-192.168.11.51}}"
IP_SANKOFA_PHOENIX_API="${IP_SANKOFA_PHOENIX_API:-${IP_SERVICE_50:-192.168.11.50}}"
SANKOFA_PORTAL_PORT="${SANKOFA_PORTAL_PORT:-3000}"
SANKOFA_PHOENIX_API_PORT="${SANKOFA_PHOENIX_API_PORT:-4000}"
update_proxy_host "sankofa.nexus" "http://${IP_SANKOFA_PORTAL}:${SANKOFA_PORTAL_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "sankofa.nexus" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
update_proxy_host "www.sankofa.nexus" "http://${IP_SANKOFA_PORTAL}:${SANKOFA_PORTAL_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)) || { add_proxy_host "www.sankofa.nexus" "${IP_SANKOFA_PORTAL}" "${SANKOFA_PORTAL_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
IP_SANKOFA_PUBLIC_WEB="${IP_SANKOFA_PUBLIC_WEB:-${IP_SANKOFA_PORTAL}}"
SANKOFA_PUBLIC_WEB_PORT="${SANKOFA_PUBLIC_WEB_PORT:-${SANKOFA_PORTAL_PORT}}"
IP_SANKOFA_CLIENT_SSO="${IP_SANKOFA_CLIENT_SSO:-${IP_SANKOFA_PORTAL}}"
SANKOFA_CLIENT_SSO_PORT="${SANKOFA_CLIENT_SSO_PORT:-${SANKOFA_PORTAL_PORT}}"
update_proxy_host "sankofa.nexus" "http://${IP_SANKOFA_PUBLIC_WEB}:${SANKOFA_PUBLIC_WEB_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "sankofa.nexus" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
update_proxy_host "www.sankofa.nexus" "http://${IP_SANKOFA_PUBLIC_WEB}:${SANKOFA_PUBLIC_WEB_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)) || { add_proxy_host "www.sankofa.nexus" "${IP_SANKOFA_PUBLIC_WEB}" "${SANKOFA_PUBLIC_WEB_PORT}" false false "https://sankofa.nexus" && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
update_proxy_host "phoenix.sankofa.nexus" "http://${IP_SANKOFA_PHOENIX_API}:${SANKOFA_PHOENIX_API_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "phoenix.sankofa.nexus" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
update_proxy_host "www.phoenix.sankofa.nexus" "http://${IP_SANKOFA_PHOENIX_API}:${SANKOFA_PHOENIX_API_PORT}" false false "https://phoenix.sankofa.nexus" && updated_count=$((updated_count + 1)) || { add_proxy_host "www.phoenix.sankofa.nexus" "${IP_SANKOFA_PHOENIX_API}" "${SANKOFA_PHOENIX_API_PORT}" false false "https://phoenix.sankofa.nexus" && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
# Keycloak (CT 7802) — portal SSO; NPM must forward X-Forwarded-* (Keycloak KC_PROXY_HEADERS=xforwarded on upstream)
@@ -403,6 +407,16 @@ IP_SANKOFA_STUDIO="${IP_SANKOFA_STUDIO:-192.168.11.72}"
SANKOFA_STUDIO_PORT="${SANKOFA_STUDIO_PORT:-8000}"
# block_exploits false — studio UI/API may POST; align with portal policy (avoid spurious 405 from NPM WAF)
update_proxy_host "studio.sankofa.nexus" "http://${IP_SANKOFA_STUDIO}:${SANKOFA_STUDIO_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "studio.sankofa.nexus" "${IP_SANKOFA_STUDIO}" "${SANKOFA_STUDIO_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
# Client SSO hostnames (Next.js portal stack on 7801 typical). NEXTAUTH_URL / Keycloak redirects: https://portal.sankofa.nexus (and https://admin.sankofa.nexus).
update_proxy_host "portal.sankofa.nexus" "http://${IP_SANKOFA_CLIENT_SSO}:${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "portal.sankofa.nexus" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
update_proxy_host "admin.sankofa.nexus" "http://${IP_SANKOFA_CLIENT_SSO}:${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "admin.sankofa.nexus" "${IP_SANKOFA_CLIENT_SSO}" "${SANKOFA_CLIENT_SSO_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
# Operator systems dashboard — only when IP_SANKOFA_DASH is set (see config/ip-addresses.conf).
if [[ -n "${IP_SANKOFA_DASH:-}" ]]; then
SANKOFA_DASH_PORT="${SANKOFA_DASH_PORT:-3000}"
update_proxy_host "dash.sankofa.nexus" "http://${IP_SANKOFA_DASH}:${SANKOFA_DASH_PORT}" false false && updated_count=$((updated_count + 1)) || { add_proxy_host "dash.sankofa.nexus" "${IP_SANKOFA_DASH}" "${SANKOFA_DASH_PORT}" false false && updated_count=$((updated_count + 1)); } || failed_count=$((failed_count + 1))
else
echo " Skipping dash.sankofa.nexus (set IP_SANKOFA_DASH and SANKOFA_DASH_PORT to provision upstream)."
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

View File

@@ -330,6 +330,10 @@ main() {
SANKOFA_RECORDS=(
"@" # sankofa.nexus
"www" # www.sankofa.nexus
"portal" # portal.sankofa.nexus (client SSO)
"admin" # admin.sankofa.nexus (client access admin)
"keycloak" # keycloak.sankofa.nexus (IdP)
"studio" # studio.sankofa.nexus (FusionAI Creator)
"phoenix" # phoenix.sankofa.nexus
"www.phoenix" # www.phoenix.sankofa.nexus
"the-order" # the-order.sankofa.nexus

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
# Update Sankofa NPMplus proxy hosts (portal + Phoenix API + the-order) via API by numeric host ID.
# Prefer for production: scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh (domain-based, full fleet).
# Update Sankofa NPMplus proxy hosts (apex + Phoenix API + the-order) via API by numeric host ID.
# Prefer for production: scripts/nginx-proxy-manager/update-npmplus-proxy-hosts-api.sh (domain-based; includes portal.sankofa.nexus, admin.sankofa.nexus, dash when IP_SANKOFA_DASH set).
# NPM proxy host IDs below match backup backup-20260325_183932 (36, 7, 59); override with SANKOFA_NPM_ID_* if your DB differs.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -27,6 +27,8 @@ IP_SANKOFA_PORTAL="${IP_SANKOFA_PORTAL:-${IP_SERVICE_51:-192.168.11.51}}"
IP_SANKOFA_PHOENIX_API="${IP_SANKOFA_PHOENIX_API:-${IP_SERVICE_50:-192.168.11.50}}"
SANKOFA_PORTAL_PORT="${SANKOFA_PORTAL_PORT:-3000}"
SANKOFA_PHOENIX_API_PORT="${SANKOFA_PHOENIX_API_PORT:-4000}"
IP_SANKOFA_PUBLIC_WEB="${IP_SANKOFA_PUBLIC_WEB:-$IP_SANKOFA_PORTAL}"
SANKOFA_PUBLIC_WEB_PORT="${SANKOFA_PUBLIC_WEB_PORT:-$SANKOFA_PORTAL_PORT}"
IP_ORDER_HAPROXY="${IP_ORDER_HAPROXY:-192.168.11.39}"
THE_ORDER_UPSTREAM_IP="${THE_ORDER_UPSTREAM_IP:-${IP_ORDER_HAPROXY}}"
THE_ORDER_UPSTREAM_PORT="${THE_ORDER_UPSTREAM_PORT:-80}"
@@ -41,8 +43,8 @@ SANKOFA_NPM_ID_WWW_THE_ORDER="${SANKOFA_NPM_ID_WWW_THE_ORDER:-59}"
# Optional 4th field: canonical HTTPS apex — NPM advanced_config 301 (www → apex). Matches update-npmplus-proxy-hosts-api.sh.
declare -A PROXY_HOSTS=(
["$SANKOFA_NPM_ID_ROOT"]="sankofa.nexus|${IP_SANKOFA_PORTAL}|${SANKOFA_PORTAL_PORT}|"
["$SANKOFA_NPM_ID_WWW"]="www.sankofa.nexus|${IP_SANKOFA_PORTAL}|${SANKOFA_PORTAL_PORT}|https://sankofa.nexus"
["$SANKOFA_NPM_ID_ROOT"]="sankofa.nexus|${IP_SANKOFA_PUBLIC_WEB}|${SANKOFA_PUBLIC_WEB_PORT}|"
["$SANKOFA_NPM_ID_WWW"]="www.sankofa.nexus|${IP_SANKOFA_PUBLIC_WEB}|${SANKOFA_PUBLIC_WEB_PORT}|https://sankofa.nexus"
["$SANKOFA_NPM_ID_PHOENIX"]="phoenix.sankofa.nexus|${IP_SANKOFA_PHOENIX_API}|${SANKOFA_PHOENIX_API_PORT}|"
["$SANKOFA_NPM_ID_WWW_PHOENIX"]="www.phoenix.sankofa.nexus|${IP_SANKOFA_PHOENIX_API}|${SANKOFA_PHOENIX_API_PORT}|https://phoenix.sankofa.nexus"
["$SANKOFA_NPM_ID_THE_ORDER"]="the-order.sankofa.nexus|${THE_ORDER_UPSTREAM_IP}|${THE_ORDER_UPSTREAM_PORT}|"