Files
uptop/internal/monitor/sites.go
T
lerko 8f16a09da1
CI / test (pull_request) Successful in 1m44s
CI / lint (pull_request) Successful in 1m11s
CI / vulncheck (pull_request) Successful in 56s
refactor: decompose monitor.go and sqlstore.go into focused files
Split monitor.go (1131→350 lines) into alerts.go, checks.go,
sites.go, and maintenance.go by concern. Split sqlstore.go
(833→470 lines) into sqlstore_alerts.go, sqlstore_history.go,
and sqlstore_maintenance.go by domain. Tests move with their
implementation. Pure reorganization — no behavioral changes.
2026-06-27 19:23:34 -04:00

159 lines
3.5 KiB
Go

package monitor
import (
"context"
"fmt"
"time"
"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
)
func (e *Engine) SetActive(active bool) {
e.activeMu.Lock()
defer e.activeMu.Unlock()
if e.isActive != active {
e.isActive = active
status := "RESUMED (Active)"
if !active {
status = "PAUSED (Passive)"
}
e.AddLog(fmt.Sprintf("Engine %s", status))
}
}
func (e *Engine) IsActive() bool {
e.activeMu.RLock()
defer e.activeMu.RUnlock()
return e.isActive
}
func (e *Engine) GetAllSites() []models.Site {
e.mu.RLock()
defer e.mu.RUnlock()
sites := make([]models.Site, 0, len(e.liveState))
for _, s := range e.liveState {
sites = append(sites, s)
}
return sites
}
func (e *Engine) GetLiveState() map[int]models.Site {
e.mu.RLock()
defer e.mu.RUnlock()
cp := make(map[int]models.Site, len(e.liveState))
for k, v := range e.liveState {
cp[k] = v
}
return cp
}
func (e *Engine) addToTokenIndex(site models.Site) {
if site.Type == "push" && site.Token != "" {
e.tokenIndex[site.Token] = site.ID
}
}
func (e *Engine) removeFromTokenIndex(id int) {
for token, sid := range e.tokenIndex {
if sid == id {
delete(e.tokenIndex, token)
return
}
}
}
func (e *Engine) UpdateSiteConfig(cfg models.SiteConfig) {
e.mu.Lock()
if existing, ok := e.liveState[cfg.ID]; ok {
e.removeFromTokenIndex(cfg.ID)
existing.SiteConfig = cfg
e.liveState[cfg.ID] = existing
e.addToTokenIndex(existing)
}
e.mu.Unlock()
e.signalRecheck(cfg.ID)
}
func (e *Engine) RemoveSite(id int) {
e.mu.Lock()
e.removeFromTokenIndex(id)
delete(e.liveState, id)
e.mu.Unlock()
e.removeHistory(id)
e.probeResultsMu.Lock()
delete(e.probeResults, id)
e.probeResultsMu.Unlock()
e.recheckMu.Lock()
delete(e.recheck, id)
e.recheckMu.Unlock()
}
func (e *Engine) ToggleSitePause(id int) bool {
var (
paused bool
name string
)
_, ok := e.applyState(id, func(s *models.Site) {
s.Paused = !s.Paused
paused = s.Paused
name = s.Name
})
if !ok {
return false
}
if paused {
e.AddLog(fmt.Sprintf("Monitor '%s' paused", name))
} else {
e.AddLog(fmt.Sprintf("Monitor '%s' resumed", name))
}
return paused
}
// applyState atomically reads, mutates, and writes back the live entry for id.
// The mutator runs under the engine write lock and receives a pointer to the
// CURRENT live state, so concurrent config edits, pauses, and heartbeats are
// never clobbered by a stale snapshot. The mutator must only touch runtime /
// check-result fields — config fields (Name/URL/Type/Token/Interval/AlertID/…)
// are owned by UpdateSiteConfig and must not be written here. Returns the
// post-mutation copy and whether the site still exists.
func (e *Engine) applyState(id int, mutate func(s *models.Site)) (models.Site, bool) {
e.mu.Lock()
defer e.mu.Unlock()
cur, ok := e.liveState[id]
if !ok {
return models.Site{}, false
}
mutate(&cur)
e.liveState[id] = cur
return cur, true
}
func (e *Engine) GetDisplayStatus(site models.Site) string {
if site.Paused {
return "PAUSED"
}
if e.isInMaintenance(site.ID) {
return "MAINT"
}
return string(site.Status)
}
func (e *Engine) GetStateChanges(siteID int, limit int) []models.StateChange {
changes, err := e.db.GetStateChanges(context.Background(), siteID, limit)
if err != nil {
return nil
}
return changes
}
func (e *Engine) GetStateChangesSince(siteID int, since time.Time) []models.StateChange {
changes, err := e.db.GetStateChangesSince(context.Background(), siteID, since)
if err != nil {
return nil
}
return changes
}