diff --git a/src/routes/__tests__/gateway.test.ts b/src/routes/__tests__/gateway.test.ts index d4111dd..37f137a 100644 --- a/src/routes/__tests__/gateway.test.ts +++ b/src/routes/__tests__/gateway.test.ts @@ -6,10 +6,12 @@ import request from 'supertest'; import { createTestApp, signedCookie } from '../../__tests__/test-helpers.js'; let previousUsers: string | undefined; +let previousWebPort: string | undefined; let stateDir: string; beforeEach(() => { previousUsers = process.env.USERS; + previousWebPort = process.env.WEB_PORT; stateDir = mkdtempSync(join(tmpdir(), 'gateway-route-settings-')); process.env.USERS = JSON.stringify([{ id: 'u1', name: 'User', workDir: stateDir, stateDir }]); }); @@ -17,6 +19,8 @@ beforeEach(() => { afterEach(() => { if (previousUsers === undefined) delete process.env.USERS; else process.env.USERS = previousUsers; + if (previousWebPort === undefined) delete process.env.WEB_PORT; + else process.env.WEB_PORT = previousWebPort; rmSync(stateDir, { recursive: true, force: true }); }); @@ -58,4 +62,15 @@ describe('/api/gateway', () => { const disabled = await request(server).post('/api/gateway').set('Cookie', cookie).send({ enabled: false }); expect(disabled.body.gateway).toMatchObject({ enabled: false, configured: false, source: 'state' }); }); -}); \ No newline at end of file + + it('returns backend WEB_PORT base URL when host is vite dev server', async () => { + process.env.WEB_PORT = '3000'; + const cookie = signedCookie('u1'); + const res = await request(await app()) + .get('/api/gateway') + .set('Cookie', cookie) + .set('Host', 'localhost:5173'); + expect(res.status).toBe(200); + expect(res.body.gateway.baseUrl).toBe('http://localhost:3000/v1'); + }); +}); diff --git a/src/routes/gateway.ts b/src/routes/gateway.ts index 4ab78bc..3788d1e 100644 --- a/src/routes/gateway.ts +++ b/src/routes/gateway.ts @@ -11,6 +11,19 @@ function userConfig(userId: string) { return getUsersConfig().find((user) => user.id === userId); } +function gatewayBaseUrl(origin: string | null | undefined): string { + const rawOrigin = typeof origin === 'string' ? origin : ''; + try { + const url = new URL(rawOrigin); + if (url.port === '5173') { + url.port = process.env.WEB_PORT ?? '3000'; + } + return `${url.origin}/v1`; + } catch { + return rawOrigin ? `${rawOrigin.replace(/\/$/, '')}/v1` : '/v1'; + } +} + export function gateway(router: Router): void { router.get('/api/gateway', async (ctx) => { const userId = ctx.state.userId as string | undefined; @@ -26,7 +39,8 @@ export function gateway(router: Router): void { return; } const userCtx = await calcUser(userId); - ctx.body = { gateway: await getGatewayStatus(cfg, userCtx.stateDir, `${ctx.origin}/v1`) }; + const requestOrigin = ctx.origin ?? `${ctx.protocol}://${ctx.host}`; + ctx.body = { gateway: await getGatewayStatus(cfg, userCtx.stateDir, gatewayBaseUrl(requestOrigin)) }; }); router.post('/api/gateway', async (ctx) => { @@ -44,11 +58,12 @@ export function gateway(router: Router): void { } const body = (ctx.request.body ?? {}) as Record; const userCtx = await calcUser(userId); + const requestOrigin = ctx.origin ?? `${ctx.protocol}://${ctx.host}`; ctx.body = { gateway: await updateGatewayStatus(cfg, userCtx.stateDir, { enabled: typeof body.enabled === 'boolean' ? body.enabled : undefined, rotate: body.rotate === true, - }, `${ctx.origin}/v1`), + }, gatewayBaseUrl(requestOrigin)), }; }); -} \ No newline at end of file +}