diff --git a/backend/app/version.py b/backend/app/version.py index 7ddb07e..2de8f6a 100644 --- a/backend/app/version.py +++ b/backend/app/version.py @@ -1,5 +1,5 @@ """Единый источник версии SAC (API, health, логи, OpenAPI).""" APP_NAME = "Security Alert Center" -APP_VERSION = "0.8.0" +APP_VERSION = "0.8.1" APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}" diff --git a/frontend/src/api.ts b/frontend/src/api.ts index a58357c..826fecc 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -214,6 +214,10 @@ export function fetchHost(id: number): Promise { return apiFetch(`/api/v1/hosts/${id}`); } +export function fetchRecentEvents(limit = 1): Promise { + return apiFetch(`/api/v1/dashboards/recent-events?limit=${limit}`); +} + export interface HostListResponse { items: HostSummary[]; total: number; diff --git a/frontend/src/composables/useSacLiveEventStream.ts b/frontend/src/composables/useSacLiveEventStream.ts new file mode 100644 index 0000000..4375de8 --- /dev/null +++ b/frontend/src/composables/useSacLiveEventStream.ts @@ -0,0 +1,73 @@ +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?token=${encodeURIComponent(token)}`; + 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 }; +} diff --git a/frontend/src/utils/hostsLiveUpdate.ts b/frontend/src/utils/hostsLiveUpdate.ts new file mode 100644 index 0000000..fd027fb --- /dev/null +++ b/frontend/src/utils/hostsLiveUpdate.ts @@ -0,0 +1,15 @@ +import type { HostDetail, HostSummary } from "../api"; + +/** Обновляет поля строки списка хостов из GET /hosts/{id} (без перезагрузки таблицы). */ +export function patchHostSummaryFromDetail(target: HostSummary, detail: HostDetail): void { + target.display_name = detail.display_name; + target.os_version = detail.os_version; + target.product_version = detail.product_version; + target.ipv4 = detail.ipv4; + target.last_seen_at = detail.last_seen_at; + target.event_count = detail.event_count; + target.last_heartbeat_at = detail.last_heartbeat_at; + target.last_daily_report_at = detail.last_daily_report_at; + target.last_inventory_at = detail.last_inventory_at; + target.agent_status = detail.agent_status; +} diff --git a/frontend/src/version.ts b/frontend/src/version.ts index b05fce3..7cee056 100644 --- a/frontend/src/version.ts +++ b/frontend/src/version.ts @@ -1,3 +1,3 @@ export const APP_NAME = "Security Alert Center"; -export const APP_VERSION = "0.8.0"; +export const APP_VERSION = "0.8.1"; export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`; diff --git a/frontend/src/views/HostDetailView.vue b/frontend/src/views/HostDetailView.vue index 75f0451..a0c706e 100644 --- a/frontend/src/views/HostDetailView.vue +++ b/frontend/src/views/HostDetailView.vue @@ -130,7 +130,14 @@