From 773eab9e91cb2499599935bd58f664d0155c1e89 Mon Sep 17 00:00:00 2001 From: hided62 Date: Sun, 9 Aug 2026 10:34:26 +0000 Subject: [PATCH] fix(gateway): accept normal tRPC batch paths --- app/gateway-api/src/fastifyOptions.ts | 6 ++++ app/gateway-api/src/server.ts | 2 ++ app/gateway-api/test/fastifyOptions.test.ts | 39 +++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 app/gateway-api/src/fastifyOptions.ts create mode 100644 app/gateway-api/test/fastifyOptions.test.ts diff --git a/app/gateway-api/src/fastifyOptions.ts b/app/gateway-api/src/fastifyOptions.ts new file mode 100644 index 00000000..013c54dc --- /dev/null +++ b/app/gateway-api/src/fastifyOptions.ts @@ -0,0 +1,6 @@ +export const gatewayFastifyRouterOptions = { + // tRPC joins batched procedure names in a single route parameter. Fastify's + // default limit is 100 characters, which is shorter than normal admin-page + // startup batches such as capabilities + profiles + release state. + maxParamLength: 2_048, +} as const; diff --git a/app/gateway-api/src/server.ts b/app/gateway-api/src/server.ts index 25357d98..cda40579 100644 --- a/app/gateway-api/src/server.ts +++ b/app/gateway-api/src/server.ts @@ -28,6 +28,7 @@ import { RepositoryProfileStatusService } from './lobby/profileStatusService.js' import { registerAccountIconInternalRoute } from './auth/accountIconInternalRoute.js'; import { installGatewayShutdownController } from './lifecycle/shutdownController.js'; import { RemoteUserIconStore } from './account/remoteUserIconStore.js'; +import { gatewayFastifyRouterOptions } from './fastifyOptions.js'; export const createGatewayApiServer = async () => { const config = resolveGatewayApiConfigFromEnv(); @@ -80,6 +81,7 @@ export const createGatewayApiServer = async () => { const app = fastify({ logger: true, + routerOptions: gatewayFastifyRouterOptions, }); await app.register(cors, { diff --git a/app/gateway-api/test/fastifyOptions.test.ts b/app/gateway-api/test/fastifyOptions.test.ts new file mode 100644 index 00000000..76dc209f --- /dev/null +++ b/app/gateway-api/test/fastifyOptions.test.ts @@ -0,0 +1,39 @@ +import fastify from 'fastify'; +import { afterEach, describe, expect, it } from 'vitest'; + +import { gatewayFastifyRouterOptions } from '../src/fastifyOptions.js'; + +const apps = new Set>(); + +afterEach(async () => { + await Promise.allSettled(Array.from(apps, (app) => app.close())); + apps.clear(); +}); + +describe('Gateway Fastify routing', () => { + it('accepts the admin release page initial tRPC batch path', async () => { + const app = fastify({ + logger: false, + routerOptions: gatewayFastifyRouterOptions, + }); + apps.add(app); + app.get('/gateway/api/trpc/:path', async (request) => request.params); + + const batchPath = [ + 'admin.capabilities.list', + 'admin.profiles.list', + 'admin.capabilities.list', + 'admin.releases.gatewayState', + 'admin.releases.list', + ].join(','); + expect(batchPath.length).toBeGreaterThan(100); + + const response = await app.inject({ + method: 'GET', + url: `/gateway/api/trpc/${batchPath}?batch=1`, + }); + + expect(response.statusCode).toBe(200); + expect(response.json()).toEqual({ path: batchPath }); + }); +});