feat: add real-time invoice status updates via SSE (#537)#593
feat: add real-time invoice status updates via SSE (#537)#593goldandrew wants to merge 1 commit into
Conversation
Implements GET /api/events as a true Server-Sent Events endpoint that streams contract event notifications to authenticated clients in real time, replacing the need to wait for the next polling cycle. - frontend/app/api/events/route.ts: SSE endpoint that validates the wallet JWT (passed as ?token= since EventSource has no custom-header support), polls the Soroban RPC every 3 s for new contract events from the invoice and pool contracts, filters by invoiceId when supplied, and streams each event as an SSE message. A 30 s heartbeat ping keeps proxies from closing idle connections. - frontend/lib/useContractEvents.ts: React hook that opens an EventSource to /api/events, invalidates the relevant SWR cache keys (invoice, funded-invoice, position, token-totals) on each arriving event, and appends the event to the Zustand recent-events list. EventSource reconnects automatically on drop; the hook tracks live connection state via useState so callers can reflect it in UI. Closes astera-hq#537
|
Good implementation overall, server-side polling, native SecurityJWT in the query parameter leaks to server logs. The standard mitigation is a short-lived one-time connection token: add a Must fix before merge
const onEventRef = useRef(onEvent);
useEffect(() => { onEventRef.current = onEvent; });
// In handler: onEventRef.current?.(data)
// Remove onEvent from deps: }, [enabled, invoiceId, walletAddress, setRecentEvents]); |
Summary
Closes #537
Replaces the polling-only model for contract event delivery with a true
Server-Sent Events (SSE) stream, so users see invoice status changes
within seconds instead of waiting for the next poll cycle.
frontend/app/api/events/route.ts— newGET /api/eventsSSEendpoint. Validates the wallet JWT (passed as
?token=query parambecause
EventSourcecannot send customAuthorizationheaders),polls the Soroban RPC every 3 s for new contract events from the
invoice and pool contracts, optionally filters by
invoiceId, andstreams each event as a standard SSE message. A 30 s
pingheartbeatprevents proxies from closing idle connections. The stream is cleaned
up on client disconnect via
request.signal.frontend/lib/useContractEvents.ts— newuseContractEventshook. Opens a native
EventSourceto/api/events, invalidates theappropriate SWR cache keys (
invoice,funded-invoice,position,token-totals) on every arriving event, and appends each event to theZustand
recentEventslist.EventSourcereconnects automaticallyon drop; connection state is tracked via
useStateso callers canreflect live/offline status in the UI.
Acceptance criteria met
GET /api/eventsSSE endpoint implemented in Next.js API routesTest plan
shows an open
EventSourceto/api/eventstab) → confirm the counterparty dashboard refreshes within ~5 s
without a manual page reload
automatically
GET /api/eventswithout a token → confirm 401 response