You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

52 lines
1.7 KiB

/**
* GET /api/chat/sse — Server-Sent Events stream for real-time chat messages
*/
import { subscribe, authorConnected, authorDisconnected, isAuthorOnline, userConnected, userDisconnected } from "#server/service/chat"
import { getCurrentUser } from "#server/utils/context"
export default defineEventHandler(async (event) => {
const res = event.node.res
const user = await getCurrentUser(event)
const isAuthor = user?.role === 'admin'
const userId = user?.id
// Register connection BEFORE sending connected event (so isAuthorOnline is accurate)
if (isAuthor) authorConnected()
if (userId) userConnected(String(userId))
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no',
})
// Send actual global author online status (not just the connecting user's role)
res.write(`data: ${JSON.stringify({ type: 'connected', authorOnline: isAuthorOnline() })}\n\n`)
const unsub = subscribe((msg) => {
// msg is either a ChatMessage object or a status-string from broadcastStatus
if (typeof msg === 'string') {
// Status broadcast: send directly (already JSON: {"type":"authorOnline","data":true})
res.write(`data: ${msg}\n\n`)
} else {
res.write(`data: ${JSON.stringify({ type: 'message', data: msg })}\n\n`)
}
})
const heartbeat = setInterval(() => {
res.write(': heartbeat\n\n')
}, 30_000)
event.node.req.on('close', () => {
unsub()
clearInterval(heartbeat)
if (isAuthor) authorDisconnected()
if (userId) userDisconnected(String(userId))
})
await new Promise<void>((resolve) => {
event.node.req.on('close', () => resolve())
})
})