feat: implement Phase 5 — Alerting & Monitoring
Backend: - AlertEvaluationService: evaluates metrics against AlertRules after each heartbeat - Supports cpu_usage_percent and memory_usage_percent metric paths - Operators: >, >=, <, <=, == - 15-minute dedup window to prevent alert spam - AlertRulesController: full CRUD for alert rules (GET/POST/PUT/DELETE) - AlertsController: list with acknowledged filter + POST acknowledge endpoint - IRmmHubClient: added AlertTriggered push method - Program.cs: AlertEvaluationService registered as Scoped Frontend: - AlertsPage: two-tab layout (active alerts + rules) - Alerts tab: severity badges, acknowledge button, all/unack/ack filter - Rules tab: condition display, enabled toggle, delete with confirm - Create rule modal with MetricPath/Operator/Threshold/Severity selects - api/types.ts: AlertRule, AlertItem, CreateAlertRuleRequest types - api/client.ts: alertRulesApi and alertsApi - useAgentSignalR: handles AlertTriggered → invalidates alerts query - App.tsx: Alerts nav item with Bell icon Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,10 @@ import type {
|
||||
CreateTaskRequest,
|
||||
CreateTicketRequest,
|
||||
UpdateTicketRequest,
|
||||
AlertRule,
|
||||
AlertItem,
|
||||
CreateAlertRuleRequest,
|
||||
UpdateAlertRuleRequest,
|
||||
} from './types'
|
||||
|
||||
const BASE_URL = '/api/v1'
|
||||
@@ -48,3 +52,24 @@ export const ticketsApi = {
|
||||
update: (id: number, data: UpdateTicketRequest) =>
|
||||
request<Ticket>(`/tickets/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
|
||||
}
|
||||
|
||||
// Alert Rules
|
||||
export const alertRulesApi = {
|
||||
list: () => request<AlertRule[]>('/alert-rules'),
|
||||
create: (data: CreateAlertRuleRequest) =>
|
||||
request<AlertRule>('/alert-rules', { method: 'POST', body: JSON.stringify(data) }),
|
||||
update: (id: number, data: UpdateAlertRuleRequest) =>
|
||||
request<AlertRule>(`/alert-rules/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
|
||||
delete: (id: number) =>
|
||||
request<void>(`/alert-rules/${id}`, { method: 'DELETE' }),
|
||||
}
|
||||
|
||||
// Alerts
|
||||
export const alertsApi = {
|
||||
list: (acknowledged?: boolean) => {
|
||||
const param = acknowledged !== undefined ? `?acknowledged=${acknowledged}` : ''
|
||||
return request<AlertItem[]>(`/alerts${param}`)
|
||||
},
|
||||
acknowledge: (id: number) =>
|
||||
request<{ id: number; acknowledged: boolean }>(`/alerts/${id}/acknowledge`, { method: 'POST' }),
|
||||
}
|
||||
|
||||
@@ -93,3 +93,42 @@ export interface UpdateTicketRequest {
|
||||
status?: TicketStatus
|
||||
priority?: TicketPriority
|
||||
}
|
||||
|
||||
export interface AlertRule {
|
||||
id: number
|
||||
name: string
|
||||
metricPath: string
|
||||
operator: string
|
||||
threshold: number
|
||||
severity: AlertSeverity
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export interface AlertItem {
|
||||
id: number
|
||||
message: string
|
||||
severity: AlertSeverity
|
||||
acknowledged: boolean
|
||||
createdAt: string
|
||||
agentId: string
|
||||
agentHostname: string
|
||||
ruleId: number
|
||||
ruleName: string
|
||||
}
|
||||
|
||||
export interface CreateAlertRuleRequest {
|
||||
name: string
|
||||
metricPath: string
|
||||
operator: string
|
||||
threshold: number
|
||||
severity: AlertSeverity
|
||||
}
|
||||
|
||||
export interface UpdateAlertRuleRequest {
|
||||
name?: string
|
||||
metricPath?: string
|
||||
operator?: string
|
||||
threshold?: number
|
||||
severity?: AlertSeverity
|
||||
enabled?: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user