Fix Pangolin setup: root domain support + disable SSO auth on resources

- Omit subdomain field for root domain resources (Pangolin rejects empty
  string but accepts absent field)
- Set sso:false + blockAccess:false after resource creation so resources
  are publicly accessible without Pangolin auth redirects
- Make subdomain optional in CreateHttpResourcePayload type
- Applied to both /setup and /sync endpoints

Bunker Admin
This commit is contained in:
bunker-admin 2026-03-23 15:47:57 -06:00
parent a5a83f2d04
commit a56f8446f7
2 changed files with 8 additions and 6 deletions

View File

@ -571,10 +571,11 @@ router.post('/setup', pangolinSetupLimiter, async (req: Request, res: Response)
try {
// Step 4a: Create HTTP resource
// Root domain: omit subdomain field entirely (Pangolin rejects empty string but accepts absent field)
const resourcePayload: CreateHttpResourcePayload = {
name: def.name,
domainId: matchingDomain.domainId,
subdomain: def.subdomain || '',
...(def.subdomain ? { subdomain: def.subdomain } : {}),
http: true,
protocol: 'tcp',
};
@ -583,10 +584,10 @@ router.post('/setup', pangolinSetupLimiter, async (req: Request, res: Response)
const resourceId = resource.resourceId;
logger.info(`Created resource: ${fullDomain} (ID: ${resourceId})`);
// Make resource publicly accessible
// Make resource publicly accessible (no SSO auth, no access block)
try {
await pangolinClient.updateResource(resourceId, { blockAccess: false });
logger.info(`Set ${fullDomain} as publicly accessible`);
await pangolinClient.updateResource(resourceId, { sso: false, blockAccess: false });
logger.info(`Set ${fullDomain} as public (no auth)`);
} catch (updateErr) {
logger.warn(`Created ${fullDomain} but failed to set public access:`, updateErr);
}
@ -814,10 +815,11 @@ router.post('/sync', pangolinSetupLimiter, async (_req: Request, res: Response)
} else {
// Create new resource + target
try {
// Root domain: omit subdomain field entirely (Pangolin rejects empty string)
const resource = await pangolinClient.createResource({
name: def.name,
domainId: matchingDomain.domainId,
subdomain: def.subdomain || '',
...(def.subdomain ? { subdomain: def.subdomain } : {}),
http: true,
protocol: 'tcp',
});

View File

@ -82,7 +82,7 @@ export interface CreateSitePayload {
export interface CreateHttpResourcePayload {
name: string;
domainId: string;
subdomain: string; // Subdomain only (e.g., "app") or empty string for root
subdomain?: string; // Subdomain only (e.g., "app"); omit entirely for root domain
http: true; // REQUIRED: marks as HTTP proxy resource
protocol: 'tcp'; // REQUIRED for HTTP resources
// Note: ssl and enabled are NOT valid creation fields — set via updateResource()