Topology clarification: - portal.gigafibre.ca = standalone nginx container serving /opt/client-app/ (the actual Vue SPA). This is the real customer portal. - client.gigafibre.ca = ERPNext frontend (exposes Frappe's password login form — dead-end UX, legacy MD5 attack surface). Changes: - apps/client/deploy.sh: target /opt/client-app/ directly with DEPLOY_BASE=/ (was uploading into ERPNext's /assets/client-app/, which nothing serves). Atomic stage-and-swap + docker restart so the nginx bind-mount picks up the new inode. - apps/portal/traefik-client-portal.yml: replace per-path /login and /desk blocks on client.gigafibre.ca with a catch-all 307 to portal.gigafibre.ca. Old bookmarks, old invoice links, and in-flight SMS all end up on the Vue SPA instead of Frappe's password page. - apps/ops/package-lock.json: sync to include html5-qrcode transitive deps so `npm ci` in deploy.sh works from a clean checkout. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
76 lines
3.2 KiB
Bash
Executable File
76 lines
3.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# deploy.sh — Build Gigafibre Customer Portal and ship to portal.gigafibre.ca
|
|
#
|
|
# Topology (2026-04-22):
|
|
# portal.gigafibre.ca → standalone nginx:alpine container `client-portal`
|
|
# serving /opt/client-app/ on erp.gigafibre.ca.
|
|
# client.gigafibre.ca → Traefik 302 → portal.gigafibre.ca (legacy alias).
|
|
#
|
|
# We build the PWA with base=/ (the portal host serves from root, not from
|
|
# /assets/client-app/ like the old ERPNext-embedded deployment) and rsync
|
|
# the dist/pwa/ output into /opt/client-app/ on the server. The nginx
|
|
# container bind-mounts /opt/client-app/ read-only, so files appear live
|
|
# with no container restart.
|
|
#
|
|
# Usage:
|
|
# ./deploy.sh # build + ship to production (portal.gigafibre.ca)
|
|
# ./deploy.sh local # build only (dist/pwa/) — no deploy
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
cd "$SCRIPT_DIR"
|
|
|
|
SERVER="root@96.125.196.67"
|
|
SSH_KEY="$HOME/.ssh/proxmox_vm"
|
|
DEST="/opt/client-app"
|
|
|
|
echo "==> Installing dependencies..."
|
|
npm ci --silent
|
|
|
|
echo "==> Building PWA (base=/ for portal.gigafibre.ca)..."
|
|
# VITE_ERP_TOKEN is still needed by a few API calls that hit ERPNext
|
|
# directly (catalog, invoice PDFs). TODO: migrate these behind the hub.
|
|
VITE_ERP_TOKEN="b273a666c86d2d0:06120709db5e414" DEPLOY_BASE=/ npx quasar build -m pwa
|
|
|
|
if [ "${1:-}" = "local" ]; then
|
|
echo ""
|
|
echo "Local build done. Output: dist/pwa/"
|
|
exit 0
|
|
fi
|
|
|
|
echo "==> Packaging..."
|
|
tar czf /tmp/client-pwa.tar.gz -C dist/pwa .
|
|
|
|
echo "==> Shipping to $SERVER:$DEST ..."
|
|
# We deploy to a staging dir and flip atomically — this avoids serving
|
|
# a half-written index.html referencing new hashed assets that haven't
|
|
# finished uploading (would 404 the SPA for a second or two).
|
|
cat /tmp/client-pwa.tar.gz | ssh -i "$SSH_KEY" "$SERVER" "bash -s" <<'REMOTE'
|
|
set -euo pipefail
|
|
cat > /tmp/client.tar.gz
|
|
STAGE=$(mktemp -d /opt/client-app.new.XXXXXX)
|
|
tar xzf /tmp/client.tar.gz -C "$STAGE"
|
|
# Preserve docker-compose.yml + nginx.conf from live dir (they live alongside the SPA)
|
|
cp /opt/client-app/docker-compose.yml "$STAGE/" 2>/dev/null || true
|
|
cp /opt/client-app/nginx.conf "$STAGE/" 2>/dev/null || true
|
|
# Atomic flip
|
|
BACKUP=/opt/client-app.bak.$(date +%s)
|
|
mv /opt/client-app "$BACKUP"
|
|
mv "$STAGE" /opt/client-app
|
|
# Keep last 3 backups, prune the rest
|
|
ls -dt /opt/client-app.bak.* 2>/dev/null | tail -n +4 | xargs -r rm -rf
|
|
rm -f /tmp/client.tar.gz
|
|
# nginx bind-mount follows the original inode, so the `mv` swap above
|
|
# leaves the container pointed at the backup dir. Restart to re-bind.
|
|
docker restart client-portal >/dev/null
|
|
echo " Deployed. Backup: $BACKUP"
|
|
REMOTE
|
|
|
|
rm -f /tmp/client-pwa.tar.gz
|
|
|
|
echo ""
|
|
echo "Done! Customer Portal: https://portal.gigafibre.ca/"
|
|
echo "Legacy alias (302): https://client.gigafibre.ca/"
|