package models import "time" // Incident is the human work item: the thing that gets acknowledged, assigned, // snoozed, discussed and resolved. Alerts are the machine-owned signal records // underneath it — many alerts map to one incident, correlated by the groupKey // Alertmanager already computed from the operator's group_by configuration. // // Nothing here is ever written by the Alertmanager webhook except Status, which // the webhook and the sweeper may flip to "resolved" once every member alert has // stopped firing. type Incident struct { // EscalationLevel is which rung of its team's ladder this incident is on, // 0 for none — either the team has no ladder, or somebody has answered. // EscalationDueAt is when the current level runs out, so a client can say // how long is left rather than only what already happened. EscalationLevel int64 `json:"escalation_level"` EscalationDueAt *time.Time `json:"escalation_due_at,omitempty"` // TeamID is the team that owns this incident, fixed when it opens: an // incident never moves between teams. TeamName rides along so the combined // queue can badge each row without a second request. TeamID int64 `json:"team_id"` TeamName string `json:"team_name,omitempty"` ID int64 `json:"id"` GroupKey string `json:"group_key"` Title string `json:"title"` GroupLabels map[string]string `json:"group_labels"` // Status is "triggered", "acknowledged" or "resolved". Status string `json:"status"` // Severity is the highest `severity` label across the alerts that were // firing when it was last recomputed. It is deliberately not cleared when an // incident resolves — a resolved incident should still say how bad it was. Severity *string `json:"severity,omitempty"` TriggeredAt time.Time `json:"triggered_at"` AcknowledgedByID *int64 `json:"acknowledged_by_id,omitempty"` AcknowledgedByUser *string `json:"acknowledged_by,omitempty"` AcknowledgedAt *time.Time `json:"acknowledged_at,omitempty"` AssignedToID *int64 `json:"assigned_to_id,omitempty"` AssignedToUser *string `json:"assigned_to,omitempty"` // SnoozedUntil hides the incident from the default queue without closing it. // A timestamp in the past reads as "not snoozed"; nothing sweeps it. SnoozedUntil *time.Time `json:"snoozed_until,omitempty"` ResolvedAt *time.Time `json:"resolved_at,omitempty"` // ResolutionSource is "alerts" when every member alert stopped firing, or // "manual" when a human closed it. Manual resolution is terminal: a later // occurrence opens a new incident rather than reopening this one. ResolutionSource *string `json:"resolution_source,omitempty"` ArchivedAt *time.Time `json:"archived_at,omitempty"` // Alerts is populated by GET /api/incidents/{id} only. Alerts []Alert `json:"alerts,omitempty"` } // IncidentEvent is one entry in an incident's timeline. The table is append-only // and is the only history this server keeps — alert rows are mutated in place. // // Type is one of: triggered, alert_added, alert_resolved, acknowledged, // unacknowledged, assigned, snoozed, unsnoozed, resolved, note. A nil UserID // means the server acted rather than a person. type IncidentEvent struct { ID int64 `json:"id"` IncidentID int64 `json:"incident_id"` Type string `json:"type"` UserID *int64 `json:"user_id,omitempty"` Username *string `json:"username,omitempty"` AlertID *int64 `json:"alert_id,omitempty"` Detail *string `json:"detail,omitempty"` CreatedAt time.Time `json:"created_at"` }