infra: deploy.sh renders ops nginx.conf from versioned template w/ server-side token injection

- apps/ops/infra/nginx.conf brought back in sync with the live working conf
  (was missing the /hub proxy + /auth/whoami → deploying the old template
  would have broken OPS). Tokens are placeholders (__ERP_API_TOKEN__ /
  __HUB_TOKEN__), never real values.
- deploy.sh now renders nginx.conf on every deploy: substitutes the tokens
  from the server's /opt/targo-hub/.env (ERP_TOKEN = service account, not
  Administrator; HUB_SERVICE_TOKEN), validates via throwaway nginx -t, then
  restarts ops-frontend. Fixes the "nginx.conf deleted from host → OPS down
  on restart" landmine and makes token rotation a one-command redeploy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-08 15:59:48 -04:00
parent 844eff9a49
commit d15dec5e6c
2 changed files with 58 additions and 7 deletions

View File

@ -62,6 +62,29 @@ else
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

View File

@ -6,23 +6,51 @@ server {
resolver 127.0.0.11 valid=30s;
# ERPNext API proxy token injected server-side (never in JS bundle)
# To rotate: edit this file + docker restart ops-frontend
# Return Authentik user identity (email injected by Traefik forward-auth)
location = /auth/whoami {
default_type application/json;
return 200 '{"email":"$http_x_authentik_email","username":"$http_x_authentik_username","groups":"$http_x_authentik_groups"}';
}
# ERPNext API proxy token injected SERVER-SIDE at deploy (never in the JS bundle, never committed).
# deploy.sh fills the placeholder below from the server's /opt/targo-hub/.env ERP_TOKEN
# (the dedicated service account hub-service@targo.ca NOT Administrator). Rotate = re-run deploy.sh.
location /api/ {
proxy_pass https://erp.gigafibre.ca;
proxy_ssl_verify off;
proxy_set_header Host erp.gigafibre.ca;
proxy_set_header Authorization "token __ERP_API_TOKEN__"; # injected at deploy never commit a real token
proxy_set_header Authorization "token __ERP_API_TOKEN__";
proxy_set_header X-Authentik-Email $http_x_authentik_email;
proxy_set_header X-Authentik-Username $http_x_authentik_username;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
# NOTE: Ollama Vision proxy removed 2026-04-22 invoice OCR and all
# barcode/equipment scans now go directly to targo-hub (Gemini 2.5 Flash).
# See docs/features/vision-ocr.md. The hub handles CORS + rate-limit, so no
# nginx pass-through is needed here.
# Ollama Vision API proxy (kept for legacy vision paths; primary OCR goes through the hub)
location /ollama/ {
set $ollama_upstream http://ollama:11434;
proxy_pass $ollama_upstream/;
proxy_set_header Host $host;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
client_max_body_size 20m;
}
# targo-hub proxy service token injected SERVER-SIDE at deploy (never bundled/committed).
# deploy.sh fills the placeholder below from the server's /opt/targo-hub/.env HUB_SERVICE_TOKEN.
location /hub/ {
proxy_pass https://msg.gigafibre.ca/;
proxy_ssl_verify off;
proxy_set_header Host msg.gigafibre.ca;
proxy_set_header Authorization "Bearer __HUB_TOKEN__";
proxy_set_header X-Authentik-Email $http_x_authentik_email;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_read_timeout 3600s;
}
# SPA fallback all routes serve index.html
location / {