Spaces:
Sleeping
Sleeping
| import { NextResponse } from 'next/server' | |
| import { requireRole } from '@/lib/auth' | |
| import { config } from '@/lib/config' | |
| import { logger } from '@/lib/logger' | |
| const GATEWAY_BASE = `http://${config.gatewayHost}:${config.gatewayPort}` | |
| async function gatewayFetch( | |
| path: string, | |
| options: { method?: string; body?: string; timeoutMs?: number } = {} | |
| ): Promise<Response> { | |
| const { method = 'GET', body, timeoutMs = 5000 } = options | |
| const controller = new AbortController() | |
| const timer = setTimeout(() => controller.abort(), timeoutMs) | |
| try { | |
| const res = await fetch(`${GATEWAY_BASE}${path}`, { | |
| method, | |
| signal: controller.signal, | |
| headers: body ? { 'Content-Type': 'application/json' } : undefined, | |
| body, | |
| }) | |
| return res | |
| } finally { | |
| clearTimeout(timer) | |
| } | |
| } | |
| export async function GET(request: Request) { | |
| const auth = requireRole(request, 'admin') | |
| if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status }) | |
| const { searchParams } = new URL(request.url) | |
| const action = searchParams.get('action') || 'status' | |
| try { | |
| switch (action) { | |
| case 'status': { | |
| try { | |
| const res = await gatewayFetch('/api/status') | |
| const data = await res.json() | |
| return NextResponse.json(data) | |
| } catch (err) { | |
| logger.warn({ err }, 'debug: gateway unreachable for status') | |
| return NextResponse.json({ gatewayReachable: false }) | |
| } | |
| } | |
| case 'health': { | |
| try { | |
| const res = await gatewayFetch('/api/health') | |
| const data = await res.json() | |
| return NextResponse.json(data) | |
| } catch (err) { | |
| logger.warn({ err }, 'debug: gateway unreachable for health') | |
| return NextResponse.json({ healthy: false, error: 'Gateway unreachable' }) | |
| } | |
| } | |
| case 'models': { | |
| try { | |
| const res = await gatewayFetch('/api/models') | |
| const data = await res.json() | |
| return NextResponse.json(data) | |
| } catch (err) { | |
| logger.warn({ err }, 'debug: gateway unreachable for models') | |
| return NextResponse.json({ models: [] }) | |
| } | |
| } | |
| case 'heartbeat': { | |
| const start = performance.now() | |
| try { | |
| const res = await gatewayFetch('/api/heartbeat', { timeoutMs: 3000 }) | |
| const latencyMs = Math.round(performance.now() - start) | |
| const ok = res.ok | |
| return NextResponse.json({ ok, latencyMs, timestamp: Date.now() }) | |
| } catch { | |
| const latencyMs = Math.round(performance.now() - start) | |
| return NextResponse.json({ ok: false, latencyMs, timestamp: Date.now() }) | |
| } | |
| } | |
| default: | |
| return NextResponse.json({ error: `Unknown action: ${action}` }, { status: 400 }) | |
| } | |
| } catch (err) { | |
| logger.error({ err }, 'debug: unexpected error') | |
| return NextResponse.json({ error: 'Internal error' }, { status: 500 }) | |
| } | |
| } | |
| export async function POST(request: Request) { | |
| const auth = requireRole(request, 'admin') | |
| if ('error' in auth) return NextResponse.json({ error: auth.error }, { status: auth.status }) | |
| const { searchParams } = new URL(request.url) | |
| const action = searchParams.get('action') | |
| if (action !== 'call') { | |
| return NextResponse.json({ error: 'POST only supports action=call' }, { status: 400 }) | |
| } | |
| let body: { method?: string; path?: string; body?: any } | |
| try { | |
| body = await request.json() | |
| } catch { | |
| return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 }) | |
| } | |
| const { method, path, body: callBody } = body | |
| if (!method || !['GET', 'POST'].includes(method)) { | |
| return NextResponse.json({ error: 'method must be GET or POST' }, { status: 400 }) | |
| } | |
| if (!path || typeof path !== 'string' || !path.startsWith('/api/')) { | |
| return NextResponse.json({ error: 'path must start with /api/' }, { status: 400 }) | |
| } | |
| try { | |
| const res = await gatewayFetch(path, { | |
| method, | |
| body: callBody ? JSON.stringify(callBody) : undefined, | |
| timeoutMs: 5000, | |
| }) | |
| let responseBody: any | |
| const contentType = res.headers.get('content-type') || '' | |
| if (contentType.includes('application/json')) { | |
| responseBody = await res.json() | |
| } else { | |
| responseBody = await res.text() | |
| } | |
| return NextResponse.json({ | |
| status: res.status, | |
| statusText: res.statusText, | |
| contentType, | |
| body: responseBody, | |
| }) | |
| } catch (err) { | |
| logger.warn({ err, path }, 'debug: gateway call failed') | |
| return NextResponse.json({ error: 'Gateway unreachable', path }, { status: 502 }) | |
| } | |
| } | |