import type { TokenResponse } from '@/types/auth'; const AUTHZ_API_BASE_URL = (process.env.AUTHZ_API_BASE_URL || 'http://127.0.0.1:19090').trim().replace(/\/+$/, ''); const DEPLOY_API_BASE_URL = (process.env.DEPLOY_API_BASE_URL || 'http://127.0.0.1:8090').trim().replace(/\/+$/, ''); const DEPLOY_API_TOKEN = (process.env.DEPLOY_API_TOKEN || '').trim(); const REQUEST_TIMEOUT_MS = 15000; const REGISTER_REQUEST_TIMEOUT_MS = 90000; type JsonObject = Record; export class HttpError extends Error { status: number; constructor(status: number, message: string) { super(message); this.status = status; } } function asObject(value: unknown): JsonObject { return value && typeof value === 'object' && !Array.isArray(value) ? (value as JsonObject) : {}; } function asString(value: unknown): string { return typeof value === 'string' ? value.trim() : ''; } async function fetchJson(url: string, init?: RequestInit, timeoutMs = REQUEST_TIMEOUT_MS): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutMs); try { const response = await fetch(url, { ...init, headers: { 'Content-Type': 'application/json', ...(init?.headers || {}), }, cache: 'no-store', signal: controller.signal, }); const raw = await response.text(); let payload: unknown = {}; if (raw) { try { payload = JSON.parse(raw); } catch { payload = { detail: raw }; } } if (!response.ok) { const detail = asString(asObject(payload).detail) || `request failed with status ${response.status}`; throw new HttpError(response.status, detail); } return payload as T; } catch (error) { if (error instanceof HttpError) { throw error; } if (error instanceof DOMException && error.name === 'AbortError') { throw new HttpError(504, 'request timed out'); } throw new HttpError(502, error instanceof Error ? error.message : 'request failed'); } finally { clearTimeout(timeoutId); } } export async function callDeployControl(path: string, payload: JsonObject): Promise { const headers: Record = {}; if (DEPLOY_API_TOKEN) { headers.Authorization = `Bearer ${DEPLOY_API_TOKEN}`; } return fetchJson(`${DEPLOY_API_BASE_URL}${path}`, { method: 'POST', headers, body: JSON.stringify(payload), }); } export async function callAuthzService(path: string, payload: JsonObject, timeoutMs = REQUEST_TIMEOUT_MS): Promise { return fetchJson(`${AUTHZ_API_BASE_URL}${path}`, { method: 'POST', body: JSON.stringify(payload), }, timeoutMs); } export { REGISTER_REQUEST_TIMEOUT_MS }; export async function callInstanceApi(apiBaseUrl: string, path: string, payload: JsonObject): Promise { const baseUrl = apiBaseUrl.trim().replace(/\/+$/, ''); if (!baseUrl) { throw new HttpError(500, 'instance api base url is missing'); } return fetchJson(`${baseUrl}${path}`, { method: 'POST', body: JSON.stringify(payload), }); } export function normalizeTokenResponse( response: TokenResponse, routing: { frontend_base_url?: unknown; api_base_url?: unknown; public_url?: unknown; } ): TokenResponse { const frontendBaseUrl = asString(routing.frontend_base_url); const apiBaseUrl = asString(routing.api_base_url) || asString(routing.public_url); const publicUrl = asString(routing.public_url) || apiBaseUrl; const backendConnection = asObject(response.backend_connection); const mergedBackendConnection = { ...backendConnection, frontend_base_url: asString(backendConnection.frontend_base_url) || frontendBaseUrl || publicUrl || null, api_base_url: asString(backendConnection.api_base_url) || apiBaseUrl || publicUrl || null, public_base_url: asString(backendConnection.public_base_url) || publicUrl || apiBaseUrl || null, }; return { ...response, backend_connection: mergedBackendConnection, }; }