diff --git a/src/server/routes/search.ts b/src/server/routes/search.ts index ff954d6..aa75d66 100644 --- a/src/server/routes/search.ts +++ b/src/server/routes/search.ts @@ -66,24 +66,41 @@ export const searchRoutes: FastifyPluginAsync = async (app) => { ? stores.split(',').map(Number).filter((n) => !isNaN(n)) : undefined; - reply.raw.writeHead(200, { + // Hijack the response from Fastify so we can stream SSE directly + reply.hijack(); + + const res = reply.raw; + + res.writeHead(200, { 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', + 'Cache-Control': 'no-cache, no-transform', 'Connection': 'keep-alive', + 'X-Accel-Buffering': 'no', }); - await searchStreaming( - { - query: q, - storeIds, - categoryId: category ? Number(category) : undefined, - groupId: group ? Number(group) : undefined, - }, - (event) => { - reply.raw.write(`data: ${JSON.stringify(event)}\n\n`); - }, - ); + // Send initial comment to establish connection + res.write(':ok\n\n'); - reply.raw.end(); + const sendEvent = (event: any) => { + if (!res.closed) { + res.write(`data: ${JSON.stringify(event)}\n\n`); + } + }; + + try { + await searchStreaming( + { + query: q, + storeIds, + categoryId: category ? Number(category) : undefined, + groupId: group ? Number(group) : undefined, + }, + sendEvent, + ); + } catch (err) { + sendEvent({ type: 'done', meta: { query: q, duration: 0, storeCount: 0, totalResults: 0, errors: [{ storeId: 0, storeName: 'System', error: String(err) }] } }); + } + + res.end(); }); };