From 12345f98166450922030c22a944364de46082b67 Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 8 Mar 2026 23:53:36 -0600 Subject: [PATCH] Fix Pangolin sync siteId resolution, nginx media proxy, and upgrade script - Resolve Pangolin site slug to numeric ID in sync route (fixes target creation 400 errors) - Disable SSO on newly created Pangolin resources for public access - Fix nginx media API proxy: use rewrite + set ordering for proper URI rewriting - Upgrade script: clear skip-worktree flags, fix Docker-owned dir permissions, stash untracked files Co-Authored-By: Claude Opus 4.6 --- api/src/modules/pangolin/pangolin.routes.ts | 21 +++++++++++++++++---- nginx/conf.d/api.conf.template | 3 ++- scripts/upgrade.sh | 18 +++++++++++++++++- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/api/src/modules/pangolin/pangolin.routes.ts b/api/src/modules/pangolin/pangolin.routes.ts index c114f7e6..eb691b2b 100644 --- a/api/src/modules/pangolin/pangolin.routes.ts +++ b/api/src/modules/pangolin/pangolin.routes.ts @@ -708,12 +708,25 @@ router.post('/sync', pangolinSetupLimiter, async (_req: Request, res: Response) return; } - const siteId = env.PANGOLIN_SITE_ID; - if (!siteId) { + const siteSlug = env.PANGOLIN_SITE_ID; + if (!siteSlug) { res.status(400).json({ error: { message: 'PANGOLIN_SITE_ID not set. Run setup first.', code: 'NO_SITE' } }); return; } + // Resolve numeric siteId from slug (Pangolin targets require numeric siteId) + let siteId: string | number = siteSlug; + if (isNaN(Number(siteSlug))) { + const sites = await pangolinClient.listSites(); + const match = sites.find(s => s.niceId === siteSlug || s.name === siteSlug); + if (match) { + siteId = match.siteId; + logger.info(`Resolved site slug "${siteSlug}" to numeric siteId ${siteId}`); + } else { + logger.warn(`Could not resolve site slug "${siteSlug}" to numeric ID, using as-is`); + } + } + const domain = env.DOMAIN; const resourceDefs = loadResourceDefinitions(); if (resourceDefs.length === 0) { @@ -809,9 +822,9 @@ router.post('/sync', pangolinSetupLimiter, async (_req: Request, res: Response) protocol: 'tcp', }); - // Make publicly accessible + // Make publicly accessible (disable SSO auth + blockAccess) try { - await pangolinClient.updateResource(resource.resourceId, { blockAccess: false }); + await pangolinClient.updateResource(resource.resourceId, { sso: false, blockAccess: false }); } catch { logger.warn(`Created ${fullDomain} but failed to set public access`); } diff --git a/nginx/conf.d/api.conf.template b/nginx/conf.d/api.conf.template index 7a6ced18..61e6ac63 100644 --- a/nginx/conf.d/api.conf.template +++ b/nginx/conf.d/api.conf.template @@ -6,7 +6,8 @@ server { # Media API endpoints (must come BEFORE / for longest prefix match) # Uses variable proxy_pass for runtime DNS resolution after container restarts location /media/ { - set $upstream_media http://changemaker-media-api:4100/api/; + set $upstream_media http://changemaker-media-api:4100; + rewrite ^/media/(.*) /api/$1 break; proxy_pass $upstream_media; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index dce364c9..b4e3e43f 100755 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh @@ -584,13 +584,29 @@ fi # Step 0: Save user-modifiable paths before any git operations save_user_paths +# Step 0b: Clear skip-worktree flags that prevent merge (e.g., repo-data JSON files) +SKIP_WORKTREE_FILES="$(git ls-files -v | grep '^S ' | awk '{print $2}' || true)" +if [[ -n "$SKIP_WORKTREE_FILES" ]]; then + info "Clearing skip-worktree flags on $(echo "$SKIP_WORKTREE_FILES" | wc -l | xargs) file(s)..." + echo "$SKIP_WORKTREE_FILES" | xargs git update-index --no-skip-worktree + success "Skip-worktree flags cleared" +fi + +# Step 0c: Fix Docker-owned directories that block git checkout +for owned_dir in api/upgrade api/uploads api/configs; do + if [[ -d "$PROJECT_DIR/$owned_dir" ]] && [[ ! -w "$PROJECT_DIR/$owned_dir" ]]; then + info "Fixing permissions on $owned_dir..." + docker run --rm -v "$PROJECT_DIR/$owned_dir:/fix" alpine chown -R "$(id -u):$(id -g)" /fix 2>/dev/null || true + fi +done + # Step 1: Stash user changes if any exist HAS_CHANGES=false if [[ -n "$(git status --porcelain 2>/dev/null)" ]]; then HAS_CHANGES=true STASH_NAME="upgrade-${TIMESTAMP}" info "Stashing local changes as '$STASH_NAME'..." - git stash push -m "$STASH_NAME" + git stash push --include-untracked -m "$STASH_NAME" success "Local changes stashed" fi