feat(api+web): stream topic chat live over server-sent events
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

GET /v1/topics/:name/stream opens an SSE firehose, polled server-side
every 2s and streamed as `message` events. Forward-only — clients
hit /messages once for backfill, then live from connect-time onward.
Heartbeats every 30s keep the connection through proxies.

Web chat panel reads the stream via fetch + ReadableStream so the
bearer token stays in the Authorization header (EventSource can't
set custom headers, which would force token-in-URL leaks). Auto-
reconnect with exponential backoff. setInterval polling removed.

Vercel maxDuration bumped to 300s on the catch-all API route so
streams aren't cut at the 10s default.

drizzle migrations/meta/ deleted — superseded by the filename-
tracked custom runner in apps/broker/src/migrate.ts (c2cd67a).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-05-02 19:02:38 +01:00
parent d7cef45640
commit 7e71a61db4
16 changed files with 288 additions and 36155 deletions

View File

@@ -2,6 +2,16 @@ import { handle } from "hono/vercel";
import { appRouter } from "@turbostarter/api";
// Streamable endpoints (e.g. /v1/topics/:name/stream SSE) need to keep
// the connection open for minutes, not the 10s default. 300s is the
// Vercel Pro ceiling; on Hobby the platform clamps to 60s and the
// client auto-reconnects via the SSE retry loop.
export const maxDuration = 300;
// Force dynamic rendering — streaming responses can't be statically
// optimized and we don't want Next caching SSE traffic.
export const dynamic = "force-dynamic";
const handler = handle(appRouter);
export {
handler as GET,