#!/usr/bin/env node /** * Test Different Pangolin Raw Resource Creation Configurations * * Goal: Determine correct payload format for HTTP resources * * Tests: * 1. Current payload (fails with "Unrecognized keys: type, ssl, enabled") * 2. Payload without `type` field * 3. Payload with only name, domainId, subdomain, http: true * 4. Payload with `protocol: "http"` instead of `type: "http"` * 5. TCP resource to understand base payload format * 6. Minimal HTTP resource payload * 7. Check actual resource structure from listResources() */ import { pangolinClient, CreateResourcePayload, PangolinResource } from './src/services/pangolin.client'; import { logger } from './src/utils/logger'; const orgId = process.env.PANGOLIN_ORG_ID || ''; const apiUrl = process.env.PANGOLIN_API_URL || ''; const apiKey = process.env.PANGOLIN_API_KEY || ''; if (!orgId || !apiUrl || !apiKey) { logger.error('Missing required env vars: PANGOLIN_ORG_URL, PANGOLIN_API_KEY, PANGOLIN_ORG_ID'); process.exit(1); } logger.info('=== Pangolin Resource Payload Test ==='); logger.info(`OrgId: ${orgId}`); logger.info(`API URL: ${apiUrl}`); logger.info(''); // Helper to make raw requests async function rawRequest(method: string, path: string, body?: unknown): Promise { const url = `${apiUrl}${path}`; logger.info(`[${method}] ${url}`); if (body) { logger.info(`Body: ${JSON.stringify(body, null, 2)}`); } try { const res = await fetch(url, { method, headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: body ? JSON.stringify(body) : undefined, }); const text = await res.text(); logger.info(`Response Status: ${res.status}`); if (text) { try { const json = JSON.parse(text); logger.info(`Response: ${JSON.stringify(json, null, 2)}`); return json; } catch (e) { logger.info(`Response (text): ${text}`); return text; } } return undefined; } catch (err) { logger.error(`Request failed: ${err instanceof Error ? err.message : String(err)}`); return undefined; } } async function testListExistingResources() { logger.info('\n--- Test 0: List Existing Resources ---'); const resources = await pangolinClient.listResources(); logger.info(`Found ${resources.length} resources`); if (resources.length > 0) { logger.info('Sample resource structure:'); logger.info(JSON.stringify(resources[0], null, 2)); } } async function testPayload1() { logger.info('\n--- Test 1: Current Payload (WITH type, ssl, enabled) ---'); logger.info('Expected error: "Unrecognized keys: type, ssl, enabled"'); const payload = { name: 'Test HTTP 1', type: 'http', domainId: 'test-domain-id', subdomain: 'test1', http: true, ssl: true, enabled: true, }; await rawRequest('PUT', `/org/${orgId}/site-resource`, payload); } async function testPayload2() { logger.info('\n--- Test 2: Payload WITHOUT type field ---'); logger.info('Maybe endpoint determines type from other fields'); const payload = { name: 'Test HTTP 2', domainId: 'test-domain-id', subdomain: 'test2', http: true, }; await rawRequest('PUT', `/org/${orgId}/site-resource`, payload); } async function testPayload3() { logger.info('\n--- Test 3: Minimal Payload (name, domainId, subdomain, http) ---'); const payload = { name: 'Test HTTP 3', domainId: 'test-domain-id', subdomain: 'test3', http: true, }; await rawRequest('PUT', `/org/${orgId}/site-resource`, payload); } async function testPayload4() { logger.info('\n--- Test 4: Payload with protocol instead of type ---'); const payload = { name: 'Test HTTP 4', domainId: 'test-domain-id', subdomain: 'test4', protocol: 'http', }; await rawRequest('PUT', `/org/${orgId}/site-resource`, payload); } async function testPayload5() { logger.info('\n--- Test 5: TCP Resource on /resource endpoint ---'); logger.info('TCP uses `/org/{orgId}/resource` endpoint (different from /site-resource)'); const payload = { name: 'Test TCP 1', type: 'tcp', proxyPort: 2222, stickySession: false, enabled: true, }; await rawRequest('PUT', `/org/${orgId}/resource`, payload); } async function testPayload6() { logger.info('\n--- Test 6: Only Name + Subdomain (simplest) ---'); const payload = { name: 'Test HTTP 6', subdomain: 'test6', }; await rawRequest('PUT', `/org/${orgId}/site-resource`, payload); } async function testPayload7() { logger.info('\n--- Test 7: Check if siteId is required ---'); // First get a valid siteId const sites = await pangolinClient.listSites(); if (sites.length === 0) { logger.warn('No sites found - skipping test'); return; } const siteId = sites[0].siteId; logger.info(`Using siteId: ${siteId}`); const payload = { name: 'Test HTTP 7', subdomain: 'test7', siteId, }; await rawRequest('PUT', `/org/${orgId}/site-resource`, payload); } async function testPayload8() { logger.info('\n--- Test 8: Try on different endpoint (/org/{orgId}/resource) with http resource ---'); const payload = { name: 'Test HTTP 8', domainId: 'test-domain-id', subdomain: 'test8', http: true, }; await rawRequest('PUT', `/org/${orgId}/resource`, payload); } async function main() { try { // Check health first const healthy = await pangolinClient.healthCheck(); if (!healthy) { logger.error('Pangolin API health check failed'); process.exit(1); } logger.info('Pangolin API health check: OK'); // Run tests await testListExistingResources(); await testPayload1(); await testPayload2(); await testPayload3(); await testPayload4(); await testPayload5(); await testPayload6(); await testPayload7(); await testPayload8(); logger.info('\n=== Test Complete ==='); } catch (err) { logger.error('Test failed:', err); process.exit(1); } } main().catch(err => { logger.error('Uncaught error:', err); process.exit(1); });