feat(observability): phases 4-7 - complete frontend UI (big bang)

Add all frontend pages for observability & proxy management:
- Proxy Viewer: /proxies with grouped view, filtering, health indicators
- Proxy Creation: form with live validation, diagnostic hints, edit/delete
- Stale Containers: /containers/stale with dashboard widget, cleanup actions
- Event Log: /events with filters, pagination, real-time SSE streaming
- Navigation: proxies and events links in sidebar
- i18n: full EN/RU translations for all new features
- Settings: stale threshold configuration
This commit is contained in:
2026-03-30 11:29:10 +03:00
parent 7a85441b81
commit 79a40f3d9c
29 changed files with 2237 additions and 12 deletions
+14 -2
View File
@@ -7,7 +7,7 @@
// ── Types ──────────────────────────────────────────────────────────
export type SSEEventType = 'deploy_log' | 'instance_status' | 'deploy_status';
export type SSEEventType = 'deploy_log' | 'instance_status' | 'deploy_status' | 'event_log';
export interface SSEEvent<T = unknown> {
type: SSEEventType;
@@ -36,7 +36,16 @@ export interface DeployStatusPayload {
error?: string;
}
type SSEPayload = DeployLogPayload | InstanceStatusPayload | DeployStatusPayload;
export interface EventLogSSEPayload {
id: number;
source: string;
severity: string;
message: string;
metadata: string;
created_at: string;
}
type SSEPayload = DeployLogPayload | InstanceStatusPayload | DeployStatusPayload | EventLogSSEPayload;
export interface SSEOptions {
/** Called for each SSE event received. */
@@ -179,6 +188,7 @@ export function connectDeployLogs(
export function connectGlobalEvents(callbacks: {
onInstanceStatus?: (payload: InstanceStatusPayload) => void;
onDeployStatus?: (payload: DeployStatusPayload) => void;
onEventLog?: (payload: EventLogSSEPayload) => void;
onOpen?: () => void;
onError?: (attempt: number) => void;
}): SSEConnection {
@@ -188,6 +198,8 @@ export function connectGlobalEvents(callbacks: {
callbacks.onInstanceStatus?.(event.payload as InstanceStatusPayload);
} else if (event.type === 'deploy_status') {
callbacks.onDeployStatus?.(event.payload as DeployStatusPayload);
} else if (event.type === 'event_log') {
callbacks.onEventLog?.(event.payload as EventLogSSEPayload);
}
},
onOpen: callbacks.onOpen,