#!/bin/bash # ───────────────────────────────────────────────────────────────────────────── # deploy.sh — Build Targo Ops PWA and deploy to ops-frontend nginx container # # The ops app is served by a standalone nginx container (ops-frontend) at # erp.gigafibre.ca/ops/. Traefik strips /ops prefix before proxying to nginx. # Authentik protection is handled via Traefik forwardAuth middleware. # # Static files go to /opt/ops-app/ on the host, mounted into the container. # # Usage: # ./deploy.sh # deploy to remote server (SPA mode, no service worker cache) # ./deploy.sh local # deploy to local Docker (development) # ./deploy.sh pwa # deploy to remote server (PWA mode, for production) # # Prerequisites (remote): # - SSH key ~/.ssh/proxmox_vm for root@96.125.196.67 # - ops-frontend container running (see infra/docker-compose.yaml) # ───────────────────────────────────────────────────────────────────────────── set -e 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/ops-app" # Default to SPA mode (no service worker = no cache headaches during dev) BUILD_MODE="spa" DIST_DIR="dist/spa" if [ "$1" = "pwa" ]; then BUILD_MODE="pwa" DIST_DIR="dist/pwa" shift fi echo "==> Installing dependencies..." npm ci --silent echo "==> Building $BUILD_MODE (base=/ops/)..." DEPLOY_BASE=/ops/ npx quasar build -m "$BUILD_MODE" if [ "$1" = "local" ]; then # ── Local deploy ── echo "==> Deploying to local $DEST..." rm -rf "$DEST"/* cp -r "$DIST_DIR"/* "$DEST/" echo "" echo "Done! Targo Ops: http://localhost/ops/" else # ── Remote deploy ── echo "==> Packaging..." tar czf /tmp/ops-build.tar.gz -C "$DIST_DIR" . echo "==> Deploying to $SERVER..." cat /tmp/ops-build.tar.gz | ssh -i "$SSH_KEY" "$SERVER" \ "cat > /tmp/ops.tar.gz && \ rm -rf $DEST/*.js $DEST/*.html $DEST/*.json $DEST/assets $DEST/icons $DEST/sw.js $DEST/workbox-*.js && \ cd $DEST && tar xzf /tmp/ops.tar.gz && \ rm -f /tmp/ops.tar.gz" rm -f /tmp/ops-build.tar.gz # ── Render nginx.conf from the versioned template, injecting tokens SERVER-SIDE ── # Tokens live only on the server (/opt/targo-hub/.env): ERP_TOKEN = ERPNext service account # (hub-service@targo.ca, NOT Administrator) · HUB_SERVICE_TOKEN = hub Bearer. They are never # in the repo, never in the JS bundle. This makes the conf reproducible (fixes the past # "nginx.conf deleted from host → ops down on restart" landmine) and rotation a one-command redeploy. echo "==> Rendering nginx.conf (server-side token injection)..." scp -i "$SSH_KEY" infra/nginx.conf "$SERVER":/tmp/ops-nginx.tmpl ssh -i "$SSH_KEY" "$SERVER" ' set -e ERP=$(grep -E "^ERP_TOKEN=" /opt/targo-hub/.env | cut -d= -f2-) HUB=$(grep -E "^HUB_SERVICE_TOKEN=" /opt/targo-hub/.env | cut -d= -f2-) [ -z "$HUB" ] && HUB=$(grep -E "^HUB_TOKEN=" /opt/targo-hub/.env | cut -d= -f2-) if [ -z "$ERP" ] || [ -z "$HUB" ]; then echo "ABORT: ERP_TOKEN/HUB token missing in /opt/targo-hub/.env"; exit 1; fi sed -e "s#__ERP_API_TOKEN__#${ERP}#g" -e "s#__HUB_TOKEN__#${HUB}#g" /tmp/ops-nginx.tmpl > '"$DEST"'/nginx.conf rm -f /tmp/ops-nginx.tmpl # validate against a throwaway nginx before touching the live container docker run --rm -v '"$DEST"'/nginx.conf:/etc/nginx/conf.d/default.conf:ro nginx:alpine nginx -t # restart re-binds the file (avoids stale //deleted inode); ~1s blip docker restart ops-frontend >/dev/null sleep 2 docker exec ops-frontend sh -c "wget -qO- http://127.0.0.1/api/method/frappe.auth.get_logged_user 2>/dev/null" | head -c 80; echo ' echo "" echo "Done! Targo Ops ($BUILD_MODE): https://erp.gigafibre.ca/ops/" fi