Turn the status page into a full status page generator
Components can be put in groups and given public names. Public incidents carry posts written for customers (the internal timeline stays internal), the components they affect, and a page for each incident plus paged history. Scheduled maintenance has its own window, shows affected components as under maintenance, and is announced when scheduled, started, ended or canceled. Branding (title, logo, accent checked for contrast, contact link, footer) and a custom domain that serves only the status page. Visitors follow by RSS, by confirmed email, or by webhook that must answer a test request. Admin UI for all of it, tests, README and design notes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b4fd31a182
commit
cf4d5b3ce2
12 files changed
+2403
-147
No files matched your search
@@ -21,7 +21,8 @@ type Incident struct {
|
||||
Comms string `json:"comms"` // user id
|
||||
Summary string `json:"summary"`
|
||||
Postmortem string `json:"postmortem"`
|
||||
Public bool `json:"public"` // shown on the status page
|
||||
Public bool `json:"public"` // shown on the status page
|
||||
Components []string `json:"components"` // monitor ids the status page says are affected
|
||||
Created int64 `json:"created"`
|
||||
Resolved int64 `json:"resolved"`
|
||||
AlertIDs []string `json:"alertIds"`
|
||||
@@ -66,7 +67,7 @@ func (s *Server) DeclareIncident(title, severity, summary, lead, actor string, a
|
||||
return nil, errors.New("severity must be sev1, sev2 or sev3")
|
||||
}
|
||||
now := s.now()
|
||||
in := &Incident{ID: newID(), Title: title, Severity: severity, Status: "investigating", Lead: lead, Summary: summary, Created: now.Unix(), AlertIDs: []string{}}
|
||||
in := &Incident{ID: newID(), Title: title, Severity: severity, Status: "investigating", Lead: lead, Summary: summary, Created: now.Unix(), AlertIDs: []string{}, Components: []string{}}
|
||||
for _, id := range alertIDs {
|
||||
if a, err := s.getAlert(id); err == nil {
|
||||
in.AlertIDs = append(in.AlertIDs, a.ID)
|
||||
@@ -96,15 +97,16 @@ func firstNonEmpty(v ...string) string {
|
||||
|
||||
// IncidentPatch holds the fields an update may change; nil means unchanged.
|
||||
type IncidentPatch struct {
|
||||
Title *string `json:"title"`
|
||||
Severity *string `json:"severity"`
|
||||
Status *string `json:"status"`
|
||||
Lead *string `json:"lead"`
|
||||
Comms *string `json:"comms"`
|
||||
Summary *string `json:"summary"`
|
||||
Postmortem *string `json:"postmortem"`
|
||||
Public *bool `json:"public"`
|
||||
Note string `json:"note"` // text for the update announcement and timeline
|
||||
Title *string `json:"title"`
|
||||
Severity *string `json:"severity"`
|
||||
Status *string `json:"status"`
|
||||
Lead *string `json:"lead"`
|
||||
Comms *string `json:"comms"`
|
||||
Summary *string `json:"summary"`
|
||||
Postmortem *string `json:"postmortem"`
|
||||
Public *bool `json:"public"`
|
||||
Components *[]string `json:"components"`
|
||||
Note string `json:"note"` // text for the update announcement and timeline
|
||||
}
|
||||
|
||||
func (s *Server) UpdateIncident(id, actor string, p IncidentPatch) (*Incident, error) {
|
||||
@@ -113,6 +115,7 @@ func (s *Server) UpdateIncident(id, actor string, p IncidentPatch) (*Incident, e
|
||||
return nil, err
|
||||
}
|
||||
now := s.now()
|
||||
prevStatus := in.Status
|
||||
var changes []string
|
||||
if p.Severity != nil && *p.Severity != in.Severity {
|
||||
if !validSeverity(*p.Severity) {
|
||||
@@ -151,12 +154,17 @@ func (s *Server) UpdateIncident(id, actor string, p IncidentPatch) (*Incident, e
|
||||
if p.Postmortem != nil {
|
||||
in.Postmortem = *p.Postmortem
|
||||
}
|
||||
wasPublic, statusChanged := in.Public, in.Status != prevStatus
|
||||
if p.Public != nil {
|
||||
in.Public = *p.Public
|
||||
}
|
||||
if p.Components != nil {
|
||||
in.Components = cleanIDs(*p.Components)
|
||||
}
|
||||
if err := s.st.put("incident", in.ID, in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.postToStatusPage(in, wasPublic, statusChanged, p.Note, now)
|
||||
if len(changes) > 0 || p.Note != "" {
|
||||
text := strings.Join(changes, "; ")
|
||||
if p.Note != "" {
|
||||
|
||||
Reference in new issue
Block a user