Files
the_order/scripts/deploy/sync-portal-public-to-sankofa-phoenix.sh
T

184 lines
5.0 KiB
Bash
Executable File

#!/bin/bash
#
# Sync the portal-public Next.js standalone build to the Sankofa / Proxmox runtime.
# Target topology:
# NPMplus -> order-haproxy (VMID 10210) -> order-portal-public (VMID 10090)
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/config.sh"
APP_NAME="portal-public"
APP_DIR="${PROJECT_ROOT}/apps/${APP_NAME}"
LOCAL_BUILD_TARBALL="${ARTIFACTS_DIR}/${APP_NAME}-${ENVIRONMENT}.tgz"
LOCAL_STAGE_DIR="${ARTIFACTS_DIR}/${APP_NAME}-${ENVIRONMENT}-stage"
REMOTE_TGZ="/tmp/${APP_NAME}-${ENVIRONMENT}-$$.tgz"
CT_TGZ="/tmp/${APP_NAME}.tgz"
DRY_RUN=false
SKIP_BUILD=false
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run)
DRY_RUN=true
shift
;;
--skip-build)
SKIP_BUILD=true
shift
;;
*)
error_exit "Unknown option: $1"
;;
esac
done
if [ ! -d "${APP_DIR}" ]; then
error_exit "Portal app not found at ${APP_DIR}"
fi
check_prerequisites
check_proxmox_access
if [ "${SKIP_BUILD}" != "true" ]; then
log_step "Building ${APP_NAME} standalone bundle..."
rm -rf "${APP_DIR}/.next"
pnpm --dir "${APP_DIR}" build || error_exit "Failed to build ${APP_NAME}"
else
log_info "Skipping portal build because --skip-build was provided"
fi
if [ ! -f "${APP_DIR}/.next/standalone/apps/${APP_NAME}/server.js" ]; then
error_exit "Expected standalone output missing for ${APP_NAME}"
fi
log_step "Preparing deployment artifact..."
rm -rf "${LOCAL_STAGE_DIR}"
mkdir -p "${LOCAL_STAGE_DIR}/standalone/apps/${APP_NAME}/.next"
cp -R "${APP_DIR}/.next/standalone/." "${LOCAL_STAGE_DIR}/standalone/"
cp -R "${APP_DIR}/.next/static" "${LOCAL_STAGE_DIR}/standalone/apps/${APP_NAME}/.next/static"
cp -R "${APP_DIR}/public" "${LOCAL_STAGE_DIR}/standalone/apps/${APP_NAME}/public"
tar czf "${LOCAL_BUILD_TARBALL}" -C "${LOCAL_STAGE_DIR}" standalone
log_success "Created deployment artifact ${LOCAL_BUILD_TARBALL}"
if [ "${DRY_RUN}" = "true" ]; then
log_info "Dry run only. Would push ${LOCAL_BUILD_TARBALL} to CT ${ORDER_PORTAL_PUBLIC_VMID} on ${ORDER_PORTAL_PUBLIC_HOST}"
exit 0
fi
log_step "Uploading artifact to Proxmox host..."
scp ${SSH_OPTS} "${LOCAL_BUILD_TARBALL}" "${PROXMOX_SSH_USER}@${ORDER_PORTAL_PUBLIC_HOST}:${REMOTE_TGZ}"
log_step "Deploying ${APP_NAME} to CT ${ORDER_PORTAL_PUBLIC_VMID}..."
ssh ${SSH_OPTS} "${PROXMOX_SSH_USER}@${ORDER_PORTAL_PUBLIC_HOST}" bash -s -- \
"${ORDER_PORTAL_PUBLIC_VMID}" \
"${REMOTE_TGZ}" \
"${CT_TGZ}" \
"${ORDER_PORTAL_PUBLIC_APP_DIR}" \
"${ORDER_PORTAL_PUBLIC_SERVICE}" \
"${ORDER_PORTAL_PUBLIC_PORT}" \
"${THE_ORDER_PUBLIC_URL}" \
"${SANKOFA_PHOENIX_URL}" \
"${SANKOFA_PORTAL_URL}" \
"${ORDER_PORTAL_PUBLIC_IP}" <<'REMOTE_EOF'
set -euo pipefail
VMID="$1"
REMOTE_TGZ="$2"
CT_TGZ="$3"
APP_DIR="$4"
SERVICE_NAME="$5"
PORT="$6"
ORDER_URL="$7"
PHOENIX_URL="$8"
PORTAL_URL="$9"
PORTAL_IP="${10}"
pct push "${VMID}" "${REMOTE_TGZ}" "${CT_TGZ}"
rm -f "${REMOTE_TGZ}"
pct exec "${VMID}" -- bash -s -- \
"${CT_TGZ}" \
"${APP_DIR}" \
"${SERVICE_NAME}" \
"${PORT}" \
"${ORDER_URL}" \
"${PHOENIX_URL}" \
"${PORTAL_URL}" <<'CT_EOF'
set -euo pipefail
CT_TGZ="$1"
APP_DIR="$2"
SERVICE_NAME="$3"
PORT="$4"
ORDER_URL="$5"
PHOENIX_URL="$6"
PORTAL_URL="$7"
command -v node >/dev/null || { echo "ERROR: node is required inside the target CT"; exit 1; }
RELEASES_DIR="${APP_DIR}/releases"
SHARED_DIR="${APP_DIR}/shared"
CURRENT_LINK="${APP_DIR}/current"
RELEASE_DIR="${RELEASES_DIR}/$(date +%Y%m%d-%H%M%S)"
mkdir -p "${RELEASES_DIR}" "${SHARED_DIR}" "${RELEASE_DIR}"
tar xzf "${CT_TGZ}" -C "${RELEASE_DIR}"
rm -f "${CT_TGZ}"
ln -sfn "${RELEASE_DIR}/standalone" "${CURRENT_LINK}"
cat > "${SHARED_DIR}/runtime.env" <<ENV_EOF
NODE_ENV=production
PORT=${PORT}
HOSTNAME=0.0.0.0
NEXT_PUBLIC_ORDER_PUBLIC_URL=${ORDER_URL}
NEXT_PUBLIC_SANKOFA_PHOENIX_URL=${PHOENIX_URL}
NEXT_PUBLIC_SANKOFA_PORTAL_URL=${PORTAL_URL}
NEXT_PUBLIC_IDENTITY_SERVICE_URL=${PHOENIX_URL}
NEXT_PUBLIC_ERESIDENCY_SERVICE_URL=${PHOENIX_URL}
NEXT_PUBLIC_FINANCE_SERVICE_URL=${PHOENIX_URL}
NEXT_PUBLIC_DATAROOM_SERVICE_URL=${PHOENIX_URL}
ENV_EOF
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<UNIT_EOF
[Unit]
Description=The Order portal-public (Sankofa Phoenix runtime)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=${CURRENT_LINK}/apps/portal-public
EnvironmentFile=${SHARED_DIR}/runtime.env
ExecStart=/usr/bin/env node server.js
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
UNIT_EOF
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}" >/dev/null
systemctl restart "${SERVICE_NAME}"
systemctl is-active --quiet "${SERVICE_NAME}"
curl -fsS "http://127.0.0.1:${PORT}/api/health" >/dev/null
# Keep the five newest releases.
if [ -d "${RELEASES_DIR}" ]; then
ls -1dt "${RELEASES_DIR}"/* 2>/dev/null | tail -n +6 | xargs -r rm -rf --
fi
CT_EOF
echo "Deployed ${SERVICE_NAME} to CT ${VMID}"
echo "Direct health: http://${PORTAL_IP}:${PORT}/api/health"
REMOTE_EOF
log_success "${APP_NAME} deployed to ${ORDER_PORTAL_PUBLIC_IP}:${ORDER_PORTAL_PUBLIC_PORT}"