Add infrastructure reliability monitoring

This commit is contained in:
Maxim
2026-08-01 16:58:25 +03:00
parent f6ac7eeb1a
commit a570537e64
12 changed files with 926 additions and 51 deletions

View File

@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"time"
_ "modernc.org/sqlite"
@@ -58,7 +59,13 @@ type HistoryPoint struct {
GuestsMemory float64 `json:"guestsMemory"`
}
type Store struct{ db *sql.DB }
type Store struct {
db *sql.DB
path string
healthMu sync.RWMutex
lastHistoryAt time.Time
lastHistoryError string
}
type EmailSettings struct {
Enabled bool `json:"enabled"`
@@ -130,7 +137,7 @@ func openStore(path string) (*Store, error) {
_, _ = db.Exec(`ALTER TABLE alert_events ADD COLUMN recovery_sent_at INTEGER`)
_, _ = db.Exec(`ALTER TABLE service_checks ADD COLUMN endpoint TEXT NOT NULL DEFAULT 'public'`)
_, _ = db.Exec(`PRAGMA foreign_keys=ON`)
return &Store{db: db}, nil
return &Store{db: db, path: path}, nil
}
type EntityHistoryPoint struct {
@@ -285,9 +292,45 @@ func (s *Store) AddHistory(point HistoryPoint) error {
_, err := s.db.Exec(`INSERT OR REPLACE INTO history(ts,cpu_usage,cpu_temp,memory_usage,swap_usage,root_usage,network_rx,network_tx,ups_charge,ups_load,zfs_usage,guests_cpu,guests_memory) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)`,
point.Timestamp, point.CPUUsage, point.CPUTemperature, point.MemoryUsage, point.SwapUsage, point.RootUsage, point.NetworkReceive, point.NetworkTransmit, point.UPSCharge, point.UPSLoad, point.ZFSUsage, point.GuestsCPU, point.GuestsMemory)
if err != nil {
s.setHistoryHealth(time.Time{}, err)
return err
}
_, err = s.db.Exec(`DELETE FROM history WHERE ts < ?`, time.Now().Add(-historyRetention).Unix())
s.setHistoryHealth(time.Now(), err)
return err
}
func (s *Store) setHistoryHealth(at time.Time, err error) {
s.healthMu.Lock()
defer s.healthMu.Unlock()
if !at.IsZero() {
s.lastHistoryAt = at
}
if err != nil {
s.lastHistoryError = err.Error()
} else {
s.lastHistoryError = ""
}
}
func (s *Store) historyHealth() (time.Time, string) {
s.healthMu.RLock()
defer s.healthMu.RUnlock()
return s.lastHistoryAt, s.lastHistoryError
}
func (s *Store) loadJSONSetting(key string, target any) bool {
var value string
if s.db.QueryRow(`SELECT value FROM settings WHERE key=?`, key).Scan(&value) != nil {
return false
}
return json.Unmarshal([]byte(value), target) == nil
}
func (s *Store) saveJSONSetting(key string, value any) error {
data, err := json.Marshal(value)
if err != nil {
return err
}
_, err = s.db.Exec(`INSERT INTO settings(key,value) VALUES(?,?) ON CONFLICT(key) DO UPDATE SET value=excluded.value`, key, string(data))
return err
}