From 70ffd218a1b873d75c17ada1611d91393450c089 Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Thu, 2 Jul 2026 14:21:01 +0000 Subject: [PATCH] NPM: locate proxy host via python3 JSON parse, not brittle grep The proxy-host lookup grepped for '"id":N,"domain_names":[...]' in a fixed field order that NPM's JSON doesn't guarantee, so the host was never found ("proxy host not found"). Replace both the cashup and hk-planner inline blocks with a shared npm_add_location() helper that parses the proxy-hosts list with python3 and matches DOMAIN inside domain_names. Co-Authored-By: Claude Sonnet 4.6 --- install-stack.sh | 155 +++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 86 deletions(-) diff --git a/install-stack.sh b/install-stack.sh index 3314d9d..334080f 100755 --- a/install-stack.sh +++ b/install-stack.sh @@ -926,53 +926,7 @@ ${build_out}" || msg_warn "Cashup may need extra time — check LXC 117" # Add /cashup/ location to the existing NPM proxy host (idempotent) - msg_info "Adding /cashup/ to NPM proxy" - local npm_token - npm_token=$(npm_get_token) - - local npm_ip="10.10.10.3" # stable internal NPM IP (vmbr1) — API only needs internal reach - if [[ -n "$npm_token" ]]; then - # Find the proxy host for our domain - local host_id - host_id=$(curl -sf "http://${npm_ip}:81/api/nginx/proxy-hosts" \ - -H "Authorization: Bearer ${npm_token}" 2>/dev/null \ - | grep -o '"id":[0-9]*,"domain_names":\["'"${DOMAIN}"'"\]' \ - | grep -o '"id":[0-9]*' | cut -d: -f2) || true - - if [[ -n "$host_id" ]]; then - # Fetch existing host, merge /cashup/ location, PUT back - local existing - existing=$(curl -sf "http://${npm_ip}:81/api/nginx/proxy-hosts/${host_id}" \ - -H "Authorization: Bearer ${npm_token}" 2>/dev/null) || true - - if [[ -n "$existing" ]] && ! echo "$existing" | grep -q '"/cashup/"'; then - # Build merged locations by appending cashup entry - local merged_locations - merged_locations=$(echo "$existing" | python3 -c " -import sys, json -d = json.load(sys.stdin) -locs = d.get('locations') or [] -locs.append({'path':'/cashup/','forward_scheme':'http','forward_host':'10.10.10.117','forward_port':3083,'advanced_config':''}) -print(json.dumps(locs)) -" 2>/dev/null) || true - - if [[ -n "$merged_locations" ]]; then - curl -sf -X PUT "http://${npm_ip}:81/api/nginx/proxy-hosts/${host_id}" \ - -H "Authorization: Bearer ${npm_token}" \ - -H "Content-Type: application/json" \ - -d "{\"locations\":${merged_locations}}" &>/dev/null \ - && msg_ok "NPM location /cashup/ added" \ - || msg_warn "NPM update failed — add /cashup/ → 10.10.10.117:3083 manually in NPM admin" - fi - else - msg_ok "NPM location /cashup/ already present" - fi - else - msg_warn "NPM proxy host for ${DOMAIN} not found — add /cashup/ → 10.10.10.117:3083 manually" - fi - else - msg_warn "NPM API unavailable — add /cashup/ → 10.10.10.117:3083 manually in NPM admin" - fi + npm_add_location "/cashup/" "10.10.10.117" 3083 } # ════════════════════════════════════════════════════════════════════════════ @@ -1034,45 +988,7 @@ ${build_out}" || msg_warn "HK Planner may need extra time — check LXC 118" # Add /hk-planner/ location to NPM proxy host - msg_info "Adding /hk-planner/ to NPM proxy" - local npm_token; npm_token=$(npm_get_token) - local npm_ip="10.10.10.3" # stable internal NPM IP (vmbr1) — API only needs internal reach - if [[ -n "$npm_token" ]]; then - local host_id - host_id=$(curl -sf "http://${npm_ip}:81/api/nginx/proxy-hosts" \ - -H "Authorization: Bearer ${npm_token}" 2>/dev/null \ - | grep -o '"id":[0-9]*,"domain_names":\["'"${DOMAIN}"'"\]' \ - | grep -o '"id":[0-9]*' | cut -d: -f2) || true - if [[ -n "$host_id" ]]; then - local existing - existing=$(curl -sf "http://${npm_ip}:81/api/nginx/proxy-hosts/${host_id}" \ - -H "Authorization: Bearer ${npm_token}" 2>/dev/null) || true - if [[ -n "$existing" ]] && ! echo "$existing" | grep -q '"/hk-planner/"'; then - local merged_locations - merged_locations=$(echo "$existing" | python3 -c " -import sys, json -d = json.load(sys.stdin) -locs = d.get('locations') or [] -locs.append({'path':'/hk-planner/','forward_scheme':'http','forward_host':'10.10.10.118','forward_port':3080,'advanced_config':''}) -print(json.dumps(locs)) -" 2>/dev/null) || true - if [[ -n "$merged_locations" ]]; then - curl -sf -X PUT "http://${npm_ip}:81/api/nginx/proxy-hosts/${host_id}" \ - -H "Authorization: Bearer ${npm_token}" \ - -H "Content-Type: application/json" \ - -d "{\"locations\":${merged_locations}}" &>/dev/null \ - && msg_ok "NPM location /hk-planner/ added" \ - || msg_warn "NPM update failed — add /hk-planner/ → 10.10.10.118:3080 manually" - fi - else - msg_ok "NPM location /hk-planner/ already present" - fi - else - msg_warn "NPM proxy host for ${DOMAIN} not found — add /hk-planner/ → 10.10.10.118:3080 manually" - fi - else - msg_warn "NPM API unavailable — add /hk-planner/ → 10.10.10.118:3080 manually in NPM admin" - fi + npm_add_location "/hk-planner/" "10.10.10.118" 3080 } # ════════════════════════════════════════════════════════════════════════════ @@ -1118,6 +1034,73 @@ npm_get_token() { echo "" } +# Idempotently add a custom location (path → forward_host:port) to the NPM +# proxy host that serves $DOMAIN. Robust JSON handling via python3 — the proxy +# host is located by matching $DOMAIN inside its domain_names array (field +# order in NPM's JSON is not guaranteed, so grep-based matching is unreliable). +npm_add_location() { + local path=$1 fwd_host=$2 fwd_port=$3 + local npm_ip="10.10.10.3" + msg_info "Adding ${path} to NPM proxy" + + local token; token=$(npm_get_token) + if [[ -z "$token" ]]; then + msg_warn "NPM API unavailable — add ${path} → ${fwd_host}:${fwd_port} manually in NPM admin" + return + fi + + local hosts_json host_id + hosts_json=$(curl -sf "http://${npm_ip}:81/api/nginx/proxy-hosts" \ + -H "Authorization: Bearer ${token}" 2>/dev/null) || true + host_id=$(echo "$hosts_json" | python3 -c " +import sys, json +try: hosts = json.load(sys.stdin) +except Exception: sys.exit(0) +dom = '${DOMAIN}' +for h in hosts: + if dom in (h.get('domain_names') or []): + print(h['id']); break +" 2>/dev/null) || true + + if [[ -z "$host_id" ]]; then + msg_warn "NPM proxy host for ${DOMAIN} not found — add ${path} → ${fwd_host}:${fwd_port} manually" + return + fi + + local existing merged + existing=$(curl -sf "http://${npm_ip}:81/api/nginx/proxy-hosts/${host_id}" \ + -H "Authorization: Bearer ${token}" 2>/dev/null) || true + merged=$(echo "$existing" | python3 -c " +import sys, json +try: h = json.load(sys.stdin) +except Exception: sys.exit(0) +path, fh, fp = '${path}', '${fwd_host}', ${fwd_port} +locs = h.get('locations') or [] +if any(l.get('path') == path for l in locs): + print('EXISTS'); sys.exit(0) +locs.append({'path':path,'forward_scheme':'http','forward_host':fh,'forward_port':fp,'advanced_config':''}) +print(json.dumps(locs)) +" 2>/dev/null) || true + + if [[ "$merged" == "EXISTS" ]]; then + msg_ok "NPM location ${path} already present" + return + fi + if [[ -z "$merged" ]]; then + msg_warn "NPM merge failed — add ${path} → ${fwd_host}:${fwd_port} manually" + return + fi + + if curl -sf -X PUT "http://${npm_ip}:81/api/nginx/proxy-hosts/${host_id}" \ + -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/json" \ + -d "{\"locations\":${merged}}" &>/dev/null; then + msg_ok "NPM location ${path} added" + else + msg_warn "NPM update failed — add ${path} → ${fwd_host}:${fwd_port} manually" + fi +} + configure_npm_proxy_hosts() { msg_step "Configuring NPM proxy hosts" msg_info "Waiting for NPM API to be ready"