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
@@ -61,6 +61,13 @@ type Server struct {
|
||||
anaSecret string
|
||||
anaTry map[string][]time.Time
|
||||
|
||||
statusMu sync.Mutex // the status page settings cache and the signing secret
|
||||
statusCfg *StatusSettings
|
||||
statusKey []byte
|
||||
subMu sync.Mutex // per-address limit on public subscribe requests
|
||||
subTry map[string][]time.Time
|
||||
notifyWG sync.WaitGroup // status page notifications in flight
|
||||
|
||||
stop chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
@@ -75,7 +82,7 @@ func New(opts Options, st *Store) *Server {
|
||||
if opts.HitDays <= 0 {
|
||||
opts.HitDays = 365
|
||||
}
|
||||
return &Server{opts: opts, st: st, hub: newHub(), running: map[string]bool{}, loginTry: map[string][]time.Time{}, anaTry: map[string][]time.Time{}, stop: make(chan struct{})}
|
||||
return &Server{opts: opts, st: st, hub: newHub(), running: map[string]bool{}, loginTry: map[string][]time.Time{}, anaTry: map[string][]time.Time{}, subTry: map[string][]time.Time{}, stop: make(chan struct{})}
|
||||
}
|
||||
|
||||
func (s *Server) now() time.Time {
|
||||
@@ -89,12 +96,14 @@ func (s *Server) now() time.Time {
|
||||
func (s *Server) Start() {
|
||||
s.every(time.Second, func() { s.runDueMonitors(s.now()) })
|
||||
s.every(5*time.Second, func() { s.escalate(s.now()) })
|
||||
s.every(30*time.Second, func() { s.runMaintenance(s.now()) })
|
||||
s.every(time.Hour, func() { s.st.prune(s.now(), s.opts.MessageDays, s.opts.CheckDays, s.opts.HitDays) })
|
||||
}
|
||||
|
||||
func (s *Server) Close() {
|
||||
close(s.stop)
|
||||
s.wg.Wait()
|
||||
s.notifyWG.Wait()
|
||||
}
|
||||
|
||||
func (s *Server) every(d time.Duration, f func()) {
|
||||
|
||||
Reference in new issue
Block a user