feat(alerts): metric-alert rule-management UI (Phase 2)

Completes metric-threshold alerting end-to-end: /metric-alert-rules
list/new/edit routes (mirroring log-scan-rules) with metric/comparator/
threshold fields, the workload scope picker, ToggleSwitch, and a
ConfirmDialog delete flow; an api.ts MetricAlertRule CRUD client; an
"Observe" nav entry; and a full metricalert.* i18n namespace (en/ru
parity). Create-form cooldown defaults to 300s to match the server.

Rules are now manageable in the WebUI; breaches already surface in the
per-app activity timeline and fire any configured event-trigger webhook.

Reviewed: typescript APPROVE (0 CRITICAL/HIGH).
This commit is contained in:
2026-05-29 14:34:01 +03:00
parent 2e26f555c5
commit 7576f54e76
7 changed files with 1927 additions and 1 deletions
+53
View File
@@ -1375,3 +1375,56 @@ export function getLogScanStats(signal?: AbortSignal): Promise<LogScanStats> {
return get<LogScanStats>('/api/log-scan-rules/stats', signal);
}
// ── Metric alert rules ──────────────────────────────────────────────
// Backend: internal/api/metric_alert_rules.go. Rules compare a sampled
// container metric (cpu/memory) against a threshold using a comparator.
// Scope model: workload_id="" → global; workload_id set → workload-only.
// Unlike log-scan rules there is no override / test / effective-rules
// concept — a metric-alert rule is a flat threshold check.
export interface MetricAlertRule {
id: number;
workload_id: string; // "" = global
name: string;
metric: 'cpu_percent' | 'memory_percent' | 'memory_bytes';
comparator: 'gt' | 'lt';
threshold: number;
severity: 'info' | 'warn' | 'error';
cooldown_seconds: number;
enabled: boolean;
created_at: string;
updated_at: string;
}
export interface MetricAlertRuleInput {
workload_id?: string;
name: string;
metric: 'cpu_percent' | 'memory_percent' | 'memory_bytes';
comparator: 'gt' | 'lt';
threshold: number;
severity?: 'info' | 'warn' | 'error';
cooldown_seconds?: number;
enabled?: boolean;
}
export function listMetricAlertRules(opts?: {
workloadID?: string;
signal?: AbortSignal;
}): Promise<MetricAlertRule[]> {
const params = opts?.workloadID ? `?workload_id=${encodeURIComponent(opts.workloadID)}` : '';
return get<MetricAlertRule[]>(`/api/metric-alert-rules${params}`, opts?.signal);
}
export function getMetricAlertRule(id: number, signal?: AbortSignal): Promise<MetricAlertRule> {
return get<MetricAlertRule>(`/api/metric-alert-rules/${id}`, signal);
}
export function createMetricAlertRule(data: MetricAlertRuleInput): Promise<MetricAlertRule> {
return post<MetricAlertRule>('/api/metric-alert-rules', data);
}
export function updateMetricAlertRule(
id: number,
data: MetricAlertRuleInput
): Promise<MetricAlertRule> {
return patch<MetricAlertRule>(`/api/metric-alert-rules/${id}`, data);
}
export function deleteMetricAlertRule(id: number): Promise<void> {
return del<void>(`/api/metric-alert-rules/${id}`);
}