74 lines
1.7 KiB
TypeScript
74 lines
1.7 KiB
TypeScript
import { onMounted, onUnmounted, ref } from "vue";
|
|
import { getToken } from "../api";
|
|
|
|
export interface SacDashboardStreamMessage {
|
|
type?: string;
|
|
last_event_id?: number | null;
|
|
}
|
|
|
|
/** SSE /api/v1/stream/events — вызывает onNewEvent при новом last_event_id (после первого тика). */
|
|
export function useSacLiveEventStream(onNewEvent: () => void) {
|
|
const live = ref(false);
|
|
let eventSource: EventSource | null = null;
|
|
let lastKnownEventDbId: number | null = null;
|
|
|
|
function handleStreamMessage(msg: SacDashboardStreamMessage) {
|
|
if (msg.type !== "dashboard") return;
|
|
const incoming = msg.last_event_id;
|
|
if (incoming === undefined) return;
|
|
|
|
if (incoming === null) {
|
|
if (lastKnownEventDbId !== null) {
|
|
onNewEvent();
|
|
}
|
|
lastKnownEventDbId = null;
|
|
return;
|
|
}
|
|
|
|
if (lastKnownEventDbId === null) {
|
|
lastKnownEventDbId = incoming;
|
|
return;
|
|
}
|
|
|
|
if (incoming !== lastKnownEventDbId) {
|
|
lastKnownEventDbId = incoming;
|
|
onNewEvent();
|
|
}
|
|
}
|
|
|
|
function connect() {
|
|
const token = getToken();
|
|
if (!token) return;
|
|
|
|
eventSource?.close();
|
|
const url = `/api/v1/stream/events`;
|
|
eventSource = new EventSource(url);
|
|
|
|
eventSource.onopen = () => {
|
|
live.value = true;
|
|
};
|
|
|
|
eventSource.onmessage = (ev) => {
|
|
try {
|
|
handleStreamMessage(JSON.parse(ev.data) as SacDashboardStreamMessage);
|
|
} catch {
|
|
/* ignore malformed SSE */
|
|
}
|
|
};
|
|
|
|
eventSource.onerror = () => {
|
|
live.value = false;
|
|
};
|
|
}
|
|
|
|
onMounted(() => connect());
|
|
|
|
onUnmounted(() => {
|
|
eventSource?.close();
|
|
eventSource = null;
|
|
live.value = false;
|
|
});
|
|
|
|
return { live };
|
|
}
|