diff --git a/apps/api/src/modules/workshop/workshop-client.test.ts b/apps/api/src/modules/workshop/workshop-client.test.ts index 7d6254f..068a945 100644 --- a/apps/api/src/modules/workshop/workshop-client.test.ts +++ b/apps/api/src/modules/workshop/workshop-client.test.ts @@ -60,6 +60,28 @@ describe('normalizeImageUrl', () => { }); describe('WorkshopClient image enrichment', () => { + it('identifies panel traffic to the upstream API', async () => { + const fetchImpl = vi.fn(async () => { + return new Response(JSON.stringify({ status: 'ok' }), { status: 200 }); + }); + const client = new WorkshopClient({ + baseUrl: 'https://workshop.test', + fetchImpl: fetchImpl as unknown as typeof fetch, + }); + + await client.health(); + + expect(fetchImpl).toHaveBeenCalledWith( + 'https://workshop.test/v1/health', + expect.objectContaining({ + headers: expect.objectContaining({ + 'User-Agent': 'reforger.dzr.tools', + 'X-API-Client': 'reforger.dzr.tools', + }), + }), + ); + }); + it('warms list images from the detail endpoint in the background and caches them', async () => { const fetchImpl = vi.fn(async (url: string | URL) => { const path = String(url); diff --git a/apps/api/src/modules/workshop/workshop-client.ts b/apps/api/src/modules/workshop/workshop-client.ts index 8dc83fe..362f8c0 100644 --- a/apps/api/src/modules/workshop/workshop-client.ts +++ b/apps/api/src/modules/workshop/workshop-client.ts @@ -106,6 +106,7 @@ function toPreview(mod: z.infer): WorkshopModPreview { const IMAGE_CACHE_TTL_MS = 60 * 60 * 1000; // matches upstream's 1 h detail cache const IMAGE_FETCH_CONCURRENCY = 5; +const CLIENT_IDENTITY = 'reforger.dzr.tools'; export class WorkshopClient { private readonly baseUrl: string; @@ -124,7 +125,11 @@ export class WorkshopClient { let response: Response; try { response = await this.fetchImpl(`${this.baseUrl}${path}`, { - headers: { Accept: 'application/json' }, + headers: { + Accept: 'application/json', + 'User-Agent': CLIENT_IDENTITY, + 'X-API-Client': CLIENT_IDENTITY, + }, signal: AbortSignal.timeout(this.timeoutMs), }); } catch (error) {