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:
@@ -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 ""
|
||||
|
||||
101
scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh
Executable file
101
scripts/deployment/keycloak-sankofa-ensure-client-redirects.sh
Executable 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)."
|
||||
89
scripts/deployment/provision-sankofa-public-web-lxc-7806.sh
Executable file
89
scripts/deployment/provision-sankofa-public-web-lxc-7806.sh
Executable 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"
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
106
scripts/deployment/sync-sankofa-public-web-to-ct.sh
Executable file
106
scripts/deployment/sync-sankofa-public-web-to-ct.sh
Executable 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"
|
||||
@@ -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++))
|
||||
|
||||
@@ -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 "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 (3–6, 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}|"
|
||||
|
||||
Reference in New Issue
Block a user