40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
|
import { useEffect, useRef } from 'react'
|
|
import { useAuthStore } from '../store/auth'
|
|
|
|
interface SSEHandlers {
|
|
onTaskEvent?: (data: { message: string; level: string; ts: string }) => void
|
|
onTaskResult?: (data: { device_id: number; device_ip: string; status: string; duration_ms: number }) => void
|
|
onTaskStatus?: (data: { status: string; total: number; ok: number; failed: number }) => void
|
|
}
|
|
|
|
export function useSSE(taskId: string | null | undefined, handlers: SSEHandlers) {
|
|
const accessToken = useAuthStore((s) => s.accessToken)
|
|
const handlersRef = useRef(handlers)
|
|
handlersRef.current = handlers
|
|
|
|
useEffect(() => {
|
|
if (!taskId || !accessToken) return
|
|
const ctrl = new AbortController()
|
|
|
|
fetchEventSource(`/api/v1/events?task_id=${taskId}`, {
|
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
signal: ctrl.signal,
|
|
onmessage(ev) {
|
|
try {
|
|
const data = JSON.parse(ev.data)
|
|
if (ev.event === 'task_event') handlersRef.current.onTaskEvent?.(data)
|
|
else if (ev.event === 'task_result') handlersRef.current.onTaskResult?.(data)
|
|
else if (ev.event === 'task_status') handlersRef.current.onTaskStatus?.(data)
|
|
} catch {
|
|
// ignore parse errors
|
|
}
|
|
},
|
|
onerror() {
|
|
ctrl.abort()
|
|
},
|
|
})
|
|
|
|
return () => ctrl.abort()
|
|
}, [taskId, accessToken])
|
|
}
|