feat/next: alert providers, prometheus metrics, core refactors #6
@@ -174,7 +174,8 @@ func startSSHServer(port int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func seedDemoData(s store.Store) {
|
func seedDemoData(s store.Store) {
|
||||||
if existing := s.GetSites(); len(existing) > 0 {
|
existing, _ := s.GetSites()
|
||||||
|
if len(existing) > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("Seeding demo data...")
|
fmt.Println("Seeding demo data...")
|
||||||
@@ -187,7 +188,7 @@ func seedDemoData(s store.Store) {
|
|||||||
"from": "oncall@example.com", "to": "team@example.com",
|
"from": "oncall@example.com", "to": "team@example.com",
|
||||||
})
|
})
|
||||||
|
|
||||||
alerts := s.GetAllAlerts()
|
alerts, _ := s.GetAllAlerts()
|
||||||
alertID := 0
|
alertID := 0
|
||||||
if len(alerts) > 0 {
|
if len(alerts) > 0 {
|
||||||
alertID = alerts[0].ID
|
alertID = alerts[0].ID
|
||||||
@@ -206,7 +207,10 @@ func seedDemoData(s store.Store) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isKeyAllowed(incomingKey ssh.PublicKey) bool {
|
func isKeyAllowed(incomingKey ssh.PublicKey) bool {
|
||||||
users := store.Get().GetAllUsers()
|
users, err := store.Get().GetAllUsers()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
allowedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(u.PublicKey))
|
allowedKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(u.PublicKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -25,7 +25,11 @@ func InitHistoryFromStore() {
|
|||||||
if s == nil {
|
if s == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
all := s.LoadAllHistory(maxHistoryLen)
|
all, err := s.LoadAllHistory(maxHistoryLen)
|
||||||
|
if err != nil {
|
||||||
|
AddLog("Failed to load check history: " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
historyMu.Lock()
|
historyMu.Lock()
|
||||||
defer historyMu.Unlock()
|
defer historyMu.Unlock()
|
||||||
for siteID, records := range all {
|
for siteID, records := range all {
|
||||||
@@ -71,7 +75,7 @@ func RecordCheck(siteID int, latency time.Duration, isUp bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s := store.Get(); s != nil {
|
if s := store.Get(); s != nil {
|
||||||
go s.SaveCheck(siteID, latency.Nanoseconds(), isUp)
|
go func() { _ = s.SaveCheck(siteID, latency.Nanoseconds(), isUp) }()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,12 @@ func StartEngine() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sites := s_instance.GetSites()
|
sites, err := s_instance.GetSites()
|
||||||
|
if err != nil {
|
||||||
|
AddLog(fmt.Sprintf("Failed to load sites: %v", err))
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, s := range sites {
|
for _, s := range sites {
|
||||||
Mutex.RLock()
|
Mutex.RLock()
|
||||||
_, exists := LiveState[s.ID]
|
_, exists := LiveState[s.ID]
|
||||||
@@ -406,8 +411,8 @@ func triggerAlert(alertID int, title, message string) {
|
|||||||
if s_instance == nil {
|
if s_instance == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg, ok := s_instance.GetAlert(alertID)
|
cfg, err := s_instance.GetAlert(alertID)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
provider := alert.GetProvider(cfg)
|
provider := alert.GetProvider(cfg)
|
||||||
|
|||||||
@@ -185,7 +185,12 @@ func Start(cfg ServerConfig) {
|
|||||||
http.Error(w, "Unauthorized: UPKEEP_CLUSTER_SECRET required", 401)
|
http.Error(w, "Unauthorized: UPKEEP_CLUSTER_SECRET required", 401)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data := store.Get().ExportData()
|
data, err := store.Get().ExportData()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Export failed: %v", err)
|
||||||
|
http.Error(w, "Export failed", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
json.NewEncoder(w).Encode(data)
|
json.NewEncoder(w).Encode(data)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
+97
-49
@@ -48,7 +48,7 @@ func (s *SQLStore) Init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetSites() []models.Site {
|
func (s *SQLStore) GetSites() ([]models.Site, error) {
|
||||||
bf := s.dialect.BoolFalse()
|
bf := s.dialect.BoolFalse()
|
||||||
query := fmt.Sprintf(
|
query := fmt.Sprintf(
|
||||||
"SELECT id, COALESCE(name, url), url, COALESCE(type, 'http'), COALESCE(token, ''), interval, alert_id, check_ssl, threshold, max_retries, COALESCE(hostname, ''), COALESCE(port, 0), COALESCE(timeout, 0), COALESCE(method, 'GET'), COALESCE(description, ''), COALESCE(parent_id, 0), COALESCE(accepted_codes, '200-299'), COALESCE(dns_resolve_type, ''), COALESCE(dns_server, ''), COALESCE(ignore_tls, %s), COALESCE(paused, %s) FROM sites",
|
"SELECT id, COALESCE(name, url), url, COALESCE(type, 'http'), COALESCE(token, ''), interval, alert_id, check_ssl, threshold, max_retries, COALESCE(hostname, ''), COALESCE(port, 0), COALESCE(timeout, 0), COALESCE(method, 'GET'), COALESCE(description, ''), COALESCE(parent_id, 0), COALESCE(accepted_codes, '200-299'), COALESCE(dns_resolve_type, ''), COALESCE(dns_server, ''), COALESCE(ignore_tls, %s), COALESCE(paused, %s) FROM sites",
|
||||||
@@ -56,107 +56,132 @@ func (s *SQLStore) GetSites() []models.Site {
|
|||||||
)
|
)
|
||||||
rows, err := s.db.Query(query)
|
rows, err := s.db.Query(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []models.Site{}
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var sites []models.Site
|
var sites []models.Site
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var st models.Site
|
var st models.Site
|
||||||
rows.Scan(&st.ID, &st.Name, &st.URL, &st.Type, &st.Token, &st.Interval, &st.AlertID,
|
if err := rows.Scan(&st.ID, &st.Name, &st.URL, &st.Type, &st.Token, &st.Interval, &st.AlertID,
|
||||||
&st.CheckSSL, &st.ExpiryThreshold, &st.MaxRetries, &st.Hostname, &st.Port, &st.Timeout,
|
&st.CheckSSL, &st.ExpiryThreshold, &st.MaxRetries, &st.Hostname, &st.Port, &st.Timeout,
|
||||||
&st.Method, &st.Description, &st.ParentID, &st.AcceptedCodes, &st.DNSResolveType,
|
&st.Method, &st.Description, &st.ParentID, &st.AcceptedCodes, &st.DNSResolveType,
|
||||||
&st.DNSServer, &st.IgnoreTLS, &st.Paused)
|
&st.DNSServer, &st.IgnoreTLS, &st.Paused); err != nil {
|
||||||
|
return sites, err
|
||||||
|
}
|
||||||
sites = append(sites, st)
|
sites = append(sites, st)
|
||||||
}
|
}
|
||||||
return sites
|
return sites, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) AddSite(site models.Site) {
|
func (s *SQLStore) AddSite(site models.Site) error {
|
||||||
token := ""
|
token := ""
|
||||||
if site.Type == "push" {
|
if site.Type == "push" {
|
||||||
token = generateToken()
|
token = generateToken()
|
||||||
}
|
}
|
||||||
s.db.Exec(s.q("INSERT INTO sites (name, url, type, token, interval, alert_id, check_ssl, threshold, max_retries, hostname, port, timeout, method, description, parent_id, accepted_codes, dns_resolve_type, dns_server, ignore_tls, paused) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
_, err := s.db.Exec(s.q("INSERT INTO sites (name, url, type, token, interval, alert_id, check_ssl, threshold, max_retries, hostname, port, timeout, method, description, parent_id, accepted_codes, dns_resolve_type, dns_server, ignore_tls, paused) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
||||||
site.Name, site.URL, site.Type, token, site.Interval, site.AlertID, site.CheckSSL, site.ExpiryThreshold, site.MaxRetries,
|
site.Name, site.URL, site.Type, token, site.Interval, site.AlertID, site.CheckSSL, site.ExpiryThreshold, site.MaxRetries,
|
||||||
site.Hostname, site.Port, site.Timeout, site.Method, site.Description, site.ParentID, site.AcceptedCodes, site.DNSResolveType, site.DNSServer, site.IgnoreTLS, site.Paused)
|
site.Hostname, site.Port, site.Timeout, site.Method, site.Description, site.ParentID, site.AcceptedCodes, site.DNSResolveType, site.DNSServer, site.IgnoreTLS, site.Paused)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) UpdateSite(site models.Site) {
|
func (s *SQLStore) UpdateSite(site models.Site) error {
|
||||||
var existingToken string
|
var existingToken string
|
||||||
s.db.QueryRow(s.q("SELECT token FROM sites WHERE id=?"), site.ID).Scan(&existingToken)
|
s.db.QueryRow(s.q("SELECT token FROM sites WHERE id=?"), site.ID).Scan(&existingToken)
|
||||||
if site.Type == "push" && existingToken == "" {
|
if site.Type == "push" && existingToken == "" {
|
||||||
existingToken = generateToken()
|
existingToken = generateToken()
|
||||||
}
|
}
|
||||||
s.db.Exec(s.q("UPDATE sites SET name=?, url=?, type=?, token=?, interval=?, alert_id=?, check_ssl=?, threshold=?, max_retries=?, hostname=?, port=?, timeout=?, method=?, description=?, parent_id=?, accepted_codes=?, dns_resolve_type=?, dns_server=?, ignore_tls=?, paused=? WHERE id=?"),
|
_, err := s.db.Exec(s.q("UPDATE sites SET name=?, url=?, type=?, token=?, interval=?, alert_id=?, check_ssl=?, threshold=?, max_retries=?, hostname=?, port=?, timeout=?, method=?, description=?, parent_id=?, accepted_codes=?, dns_resolve_type=?, dns_server=?, ignore_tls=?, paused=? WHERE id=?"),
|
||||||
site.Name, site.URL, site.Type, existingToken, site.Interval, site.AlertID, site.CheckSSL, site.ExpiryThreshold, site.MaxRetries,
|
site.Name, site.URL, site.Type, existingToken, site.Interval, site.AlertID, site.CheckSSL, site.ExpiryThreshold, site.MaxRetries,
|
||||||
site.Hostname, site.Port, site.Timeout, site.Method, site.Description, site.ParentID, site.AcceptedCodes, site.DNSResolveType, site.DNSServer, site.IgnoreTLS, site.Paused, site.ID)
|
site.Hostname, site.Port, site.Timeout, site.Method, site.Description, site.ParentID, site.AcceptedCodes, site.DNSResolveType, site.DNSServer, site.IgnoreTLS, site.Paused, site.ID)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) UpdateSitePaused(id int, paused bool) {
|
func (s *SQLStore) UpdateSitePaused(id int, paused bool) error {
|
||||||
s.db.Exec(s.q("UPDATE sites SET paused=? WHERE id=?"), paused, id)
|
_, err := s.db.Exec(s.q("UPDATE sites SET paused=? WHERE id=?"), paused, id)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) DeleteSite(id int) {
|
func (s *SQLStore) DeleteSite(id int) error {
|
||||||
s.db.Exec(s.q("DELETE FROM sites WHERE id=?"), id)
|
_, err := s.db.Exec(s.q("DELETE FROM sites WHERE id=?"), id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
s.dialect.ResetSequenceOnEmpty(s.db, "sites")
|
s.dialect.ResetSequenceOnEmpty(s.db, "sites")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetAllAlerts() []models.AlertConfig {
|
func (s *SQLStore) GetAllAlerts() ([]models.AlertConfig, error) {
|
||||||
rows, err := s.db.Query("SELECT id, name, type, settings FROM alerts")
|
rows, err := s.db.Query("SELECT id, name, type, settings FROM alerts")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []models.AlertConfig{}
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var alerts []models.AlertConfig
|
var alerts []models.AlertConfig
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var a models.AlertConfig
|
var a models.AlertConfig
|
||||||
var settingsJSON string
|
var settingsJSON string
|
||||||
rows.Scan(&a.ID, &a.Name, &a.Type, &settingsJSON)
|
if err := rows.Scan(&a.ID, &a.Name, &a.Type, &settingsJSON); err != nil {
|
||||||
|
return alerts, err
|
||||||
|
}
|
||||||
json.Unmarshal([]byte(settingsJSON), &a.Settings)
|
json.Unmarshal([]byte(settingsJSON), &a.Settings)
|
||||||
alerts = append(alerts, a)
|
alerts = append(alerts, a)
|
||||||
}
|
}
|
||||||
return alerts
|
return alerts, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetAlert(id int) (models.AlertConfig, bool) {
|
func (s *SQLStore) GetAlert(id int) (models.AlertConfig, error) {
|
||||||
var a models.AlertConfig
|
var a models.AlertConfig
|
||||||
var settingsJSON string
|
var settingsJSON string
|
||||||
err := s.db.QueryRow(s.q("SELECT id, name, type, settings FROM alerts WHERE id = ?"), id).Scan(&a.ID, &a.Name, &a.Type, &settingsJSON)
|
err := s.db.QueryRow(s.q("SELECT id, name, type, settings FROM alerts WHERE id = ?"), id).Scan(&a.ID, &a.Name, &a.Type, &settingsJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a, false
|
return a, err
|
||||||
}
|
}
|
||||||
json.Unmarshal([]byte(settingsJSON), &a.Settings)
|
json.Unmarshal([]byte(settingsJSON), &a.Settings)
|
||||||
return a, true
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) AddAlert(name, aType string, settings map[string]string) {
|
func (s *SQLStore) AddAlert(name, aType string, settings map[string]string) error {
|
||||||
jsonBytes, _ := json.Marshal(settings)
|
jsonBytes, err := json.Marshal(settings)
|
||||||
s.db.Exec(s.q("INSERT INTO alerts (name, type, settings) VALUES (?, ?, ?)"), name, aType, string(jsonBytes))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = s.db.Exec(s.q("INSERT INTO alerts (name, type, settings) VALUES (?, ?, ?)"), name, aType, string(jsonBytes))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) UpdateAlert(id int, name, aType string, settings map[string]string) {
|
func (s *SQLStore) UpdateAlert(id int, name, aType string, settings map[string]string) error {
|
||||||
jsonBytes, _ := json.Marshal(settings)
|
jsonBytes, err := json.Marshal(settings)
|
||||||
s.db.Exec(s.q("UPDATE alerts SET name=?, type=?, settings=? WHERE id=?"), name, aType, string(jsonBytes), id)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = s.db.Exec(s.q("UPDATE alerts SET name=?, type=?, settings=? WHERE id=?"), name, aType, string(jsonBytes), id)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) DeleteAlert(id int) {
|
func (s *SQLStore) DeleteAlert(id int) error {
|
||||||
s.db.Exec(s.q("DELETE FROM alerts WHERE id=?"), id)
|
_, err := s.db.Exec(s.q("DELETE FROM alerts WHERE id=?"), id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
s.dialect.ResetSequenceOnEmpty(s.db, "alerts")
|
s.dialect.ResetSequenceOnEmpty(s.db, "alerts")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetAllUsers() []models.User {
|
func (s *SQLStore) GetAllUsers() ([]models.User, error) {
|
||||||
rows, err := s.db.Query("SELECT id, username, public_key, role FROM users")
|
rows, err := s.db.Query("SELECT id, username, public_key, role FROM users")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []models.User{}
|
return nil, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
var users []models.User
|
var users []models.User
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var u models.User
|
var u models.User
|
||||||
rows.Scan(&u.ID, &u.Username, &u.PublicKey, &u.Role)
|
if err := rows.Scan(&u.ID, &u.Username, &u.PublicKey, &u.Role); err != nil {
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
users = append(users, u)
|
users = append(users, u)
|
||||||
}
|
}
|
||||||
return users
|
return users, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) AddUser(username, publicKey, role string) error {
|
func (s *SQLStore) AddUser(username, publicKey, role string) error {
|
||||||
@@ -174,14 +199,18 @@ func (s *SQLStore) DeleteUser(id int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) SaveCheck(siteID int, latencyNs int64, isUp bool) {
|
func (s *SQLStore) SaveCheck(siteID int, latencyNs int64, isUp bool) error {
|
||||||
s.db.Exec(s.q("INSERT INTO check_history (site_id, latency_ns, is_up) VALUES (?, ?, ?)"), siteID, latencyNs, isUp)
|
_, err := s.db.Exec(s.q("INSERT INTO check_history (site_id, latency_ns, is_up) VALUES (?, ?, ?)"), siteID, latencyNs, isUp)
|
||||||
s.db.Exec(s.q(`DELETE FROM check_history WHERE site_id = ? AND id NOT IN (
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = s.db.Exec(s.q(`DELETE FROM check_history WHERE site_id = ? AND id NOT IN (
|
||||||
SELECT id FROM check_history WHERE site_id = ? ORDER BY checked_at DESC LIMIT 1000
|
SELECT id FROM check_history WHERE site_id = ? ORDER BY checked_at DESC LIMIT 1000
|
||||||
)`), siteID, siteID)
|
)`), siteID, siteID)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) LoadAllHistory(limit int) map[int][]models.CheckRecord {
|
func (s *SQLStore) LoadAllHistory(limit int) (map[int][]models.CheckRecord, error) {
|
||||||
result := make(map[int][]models.CheckRecord)
|
result := make(map[int][]models.CheckRecord)
|
||||||
rows, err := s.db.Query(s.q(`
|
rows, err := s.db.Query(s.q(`
|
||||||
SELECT site_id, latency_ns, is_up FROM (
|
SELECT site_id, latency_ns, is_up FROM (
|
||||||
@@ -190,12 +219,14 @@ func (s *SQLStore) LoadAllHistory(limit int) map[int][]models.CheckRecord {
|
|||||||
FROM check_history
|
FROM check_history
|
||||||
) sub WHERE rn <= ?`), limit)
|
) sub WHERE rn <= ?`), limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result
|
return result, err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var r models.CheckRecord
|
var r models.CheckRecord
|
||||||
rows.Scan(&r.SiteID, &r.LatencyNs, &r.IsUp)
|
if err := rows.Scan(&r.SiteID, &r.LatencyNs, &r.IsUp); err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
result[r.SiteID] = append(result[r.SiteID], r)
|
result[r.SiteID] = append(result[r.SiteID], r)
|
||||||
}
|
}
|
||||||
for id, records := range result {
|
for id, records := range result {
|
||||||
@@ -204,15 +235,23 @@ func (s *SQLStore) LoadAllHistory(limit int) map[int][]models.CheckRecord {
|
|||||||
}
|
}
|
||||||
result[id] = records
|
result[id] = records
|
||||||
}
|
}
|
||||||
return result
|
return result, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) ExportData() models.Backup {
|
func (s *SQLStore) ExportData() (models.Backup, error) {
|
||||||
return models.Backup{
|
sites, err := s.GetSites()
|
||||||
Sites: s.GetSites(),
|
if err != nil {
|
||||||
Alerts: s.GetAllAlerts(),
|
return models.Backup{}, err
|
||||||
Users: s.GetAllUsers(),
|
|
||||||
}
|
}
|
||||||
|
alerts, err := s.GetAllAlerts()
|
||||||
|
if err != nil {
|
||||||
|
return models.Backup{}, err
|
||||||
|
}
|
||||||
|
users, err := s.GetAllUsers()
|
||||||
|
if err != nil {
|
||||||
|
return models.Backup{}, err
|
||||||
|
}
|
||||||
|
return models.Backup{Sites: sites, Alerts: alerts, Users: users}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) ImportData(data models.Backup) error {
|
func (s *SQLStore) ImportData(data models.Backup) error {
|
||||||
@@ -225,16 +264,25 @@ func (s *SQLStore) ImportData(data models.Backup) error {
|
|||||||
s.dialect.ImportWipe(tx)
|
s.dialect.ImportWipe(tx)
|
||||||
|
|
||||||
for _, u := range data.Users {
|
for _, u := range data.Users {
|
||||||
tx.Exec(s.q("INSERT INTO users (username, public_key, role) VALUES (?, ?, ?)"), u.Username, u.PublicKey, u.Role)
|
if _, err := tx.Exec(s.q("INSERT INTO users (username, public_key, role) VALUES (?, ?, ?)"), u.Username, u.PublicKey, u.Role); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, a := range data.Alerts {
|
for _, a := range data.Alerts {
|
||||||
jsonBytes, _ := json.Marshal(a.Settings)
|
jsonBytes, err := json.Marshal(a.Settings)
|
||||||
tx.Exec(s.q("INSERT INTO alerts (id, name, type, settings) VALUES (?, ?, ?, ?)"), a.ID, a.Name, a.Type, string(jsonBytes))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := tx.Exec(s.q("INSERT INTO alerts (id, name, type, settings) VALUES (?, ?, ?, ?)"), a.ID, a.Name, a.Type, string(jsonBytes)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, st := range data.Sites {
|
for _, st := range data.Sites {
|
||||||
tx.Exec(s.q("INSERT INTO sites (id, name, url, type, token, interval, alert_id, check_ssl, threshold, max_retries, hostname, port, timeout, method, description, parent_id, accepted_codes, dns_resolve_type, dns_server, ignore_tls, paused) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
if _, err := tx.Exec(s.q("INSERT INTO sites (id, name, url, type, token, interval, alert_id, check_ssl, threshold, max_retries, hostname, port, timeout, method, description, parent_id, accepted_codes, dns_resolve_type, dns_server, ignore_tls, paused) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
||||||
st.ID, st.Name, st.URL, st.Type, st.Token, st.Interval, st.AlertID, st.CheckSSL, st.ExpiryThreshold, st.MaxRetries,
|
st.ID, st.Name, st.URL, st.Type, st.Token, st.Interval, st.AlertID, st.CheckSSL, st.ExpiryThreshold, st.MaxRetries,
|
||||||
st.Hostname, st.Port, st.Timeout, st.Method, st.Description, st.ParentID, st.AcceptedCodes, st.DNSResolveType, st.DNSServer, st.IgnoreTLS, st.Paused)
|
st.Hostname, st.Port, st.Timeout, st.Method, st.Description, st.ParentID, st.AcceptedCodes, st.DNSResolveType, st.DNSServer, st.IgnoreTLS, st.Paused); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.dialect.ImportResetSequences(tx)
|
s.dialect.ImportResetSequences(tx)
|
||||||
|
|||||||
+14
-14
@@ -8,31 +8,31 @@ type Store interface {
|
|||||||
Init() error
|
Init() error
|
||||||
|
|
||||||
// Sites
|
// Sites
|
||||||
GetSites() []models.Site
|
GetSites() ([]models.Site, error)
|
||||||
AddSite(site models.Site)
|
AddSite(site models.Site) error
|
||||||
UpdateSite(site models.Site)
|
UpdateSite(site models.Site) error
|
||||||
UpdateSitePaused(id int, paused bool)
|
UpdateSitePaused(id int, paused bool) error
|
||||||
DeleteSite(id int)
|
DeleteSite(id int) error
|
||||||
|
|
||||||
// Alerts
|
// Alerts
|
||||||
GetAllAlerts() []models.AlertConfig
|
GetAllAlerts() ([]models.AlertConfig, error)
|
||||||
GetAlert(id int) (models.AlertConfig, bool)
|
GetAlert(id int) (models.AlertConfig, error)
|
||||||
AddAlert(name, aType string, settings map[string]string)
|
AddAlert(name, aType string, settings map[string]string) error
|
||||||
UpdateAlert(id int, name, aType string, settings map[string]string)
|
UpdateAlert(id int, name, aType string, settings map[string]string) error
|
||||||
DeleteAlert(id int)
|
DeleteAlert(id int) error
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
GetAllUsers() []models.User
|
GetAllUsers() ([]models.User, error)
|
||||||
AddUser(username, publicKey, role string) error
|
AddUser(username, publicKey, role string) error
|
||||||
UpdateUser(id int, username, publicKey, role string) error
|
UpdateUser(id int, username, publicKey, role string) error
|
||||||
DeleteUser(id int) error
|
DeleteUser(id int) error
|
||||||
|
|
||||||
// History
|
// History
|
||||||
SaveCheck(siteID int, latencyNs int64, isUp bool)
|
SaveCheck(siteID int, latencyNs int64, isUp bool) error
|
||||||
LoadAllHistory(limit int) map[int][]models.CheckRecord
|
LoadAllHistory(limit int) (map[int][]models.CheckRecord, error)
|
||||||
|
|
||||||
// Backup & Restore
|
// Backup & Restore
|
||||||
ExportData() models.Backup
|
ExportData() (models.Backup, error)
|
||||||
ImportData(data models.Backup) error
|
ImportData(data models.Backup) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package tui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go-upkeep/internal/monitor"
|
||||||
"go-upkeep/internal/store"
|
"go-upkeep/internal/store"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
@@ -277,9 +278,13 @@ func (m *Model) submitAlertForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.editID > 0 {
|
if m.editID > 0 {
|
||||||
store.Get().UpdateAlert(m.editID, d.Name, d.AlertType, settings)
|
if err := store.Get().UpdateAlert(m.editID, d.Name, d.AlertType, settings); err != nil {
|
||||||
|
monitor.AddLog("Update alert failed: " + err.Error())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
store.Get().AddAlert(d.Name, d.AlertType, settings)
|
if err := store.Get().AddAlert(d.Name, d.AlertType, settings); err != nil {
|
||||||
|
monitor.AddLog("Add alert failed: " + err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.state = stateDashboard
|
m.state = stateDashboard
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -361,12 +361,14 @@ func (m *Model) initSiteHuhForm() tea.Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
alertOpts := []huh.Option[string]{huh.NewOption("None", "0")}
|
alertOpts := []huh.Option[string]{huh.NewOption("None", "0")}
|
||||||
if store.Get() != nil {
|
if s := store.Get(); s != nil {
|
||||||
for _, a := range store.Get().GetAllAlerts() {
|
if alerts, err := s.GetAllAlerts(); err == nil {
|
||||||
alertOpts = append(alertOpts, huh.NewOption(
|
for _, a := range alerts {
|
||||||
fmt.Sprintf("%s (%s)", a.Name, a.Type),
|
alertOpts = append(alertOpts, huh.NewOption(
|
||||||
strconv.Itoa(a.ID),
|
fmt.Sprintf("%s (%s)", a.Name, a.Type),
|
||||||
))
|
strconv.Itoa(a.ID),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,10 +560,14 @@ func (m *Model) submitSiteForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if m.editID > 0 {
|
if m.editID > 0 {
|
||||||
store.Get().UpdateSite(site)
|
if err := store.Get().UpdateSite(site); err != nil {
|
||||||
|
monitor.AddLog("Update site failed: " + err.Error())
|
||||||
|
}
|
||||||
monitor.UpdateSiteConfig(site)
|
monitor.UpdateSiteConfig(site)
|
||||||
} else {
|
} else {
|
||||||
store.Get().AddSite(site)
|
if err := store.Get().AddSite(site); err != nil {
|
||||||
|
monitor.AddLog("Add site failed: " + err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.state = stateDashboard
|
m.state = stateDashboard
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package tui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go-upkeep/internal/monitor"
|
||||||
"go-upkeep/internal/store"
|
"go-upkeep/internal/store"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
@@ -145,9 +146,13 @@ func (m *Model) initUserHuhForm() tea.Cmd {
|
|||||||
func (m *Model) submitUserForm() {
|
func (m *Model) submitUserForm() {
|
||||||
d := m.userFormData
|
d := m.userFormData
|
||||||
if m.editID > 0 {
|
if m.editID > 0 {
|
||||||
store.Get().UpdateUser(m.editID, d.Username, d.PublicKey, d.Role)
|
if err := store.Get().UpdateUser(m.editID, d.Username, d.PublicKey, d.Role); err != nil {
|
||||||
|
monitor.AddLog("Update user failed: " + err.Error())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
store.Get().AddUser(d.Username, d.PublicKey, d.Role)
|
if err := store.Get().AddUser(d.Username, d.PublicKey, d.Role); err != nil {
|
||||||
|
monitor.AddLog("Add user failed: " + err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m.state = stateUsers
|
m.state = stateUsers
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-9
@@ -107,17 +107,23 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if keyMsg, ok := msg.(tea.KeyMsg); ok {
|
if keyMsg, ok := msg.(tea.KeyMsg); ok {
|
||||||
switch keyMsg.String() {
|
switch keyMsg.String() {
|
||||||
case "y", "Y":
|
case "y", "Y":
|
||||||
if store.Get() != nil {
|
if s := store.Get(); s != nil {
|
||||||
switch m.deleteTab {
|
switch m.deleteTab {
|
||||||
case 0:
|
case 0:
|
||||||
store.Get().DeleteSite(m.deleteID)
|
if err := s.DeleteSite(m.deleteID); err != nil {
|
||||||
|
monitor.AddLog("Delete site failed: " + err.Error())
|
||||||
|
}
|
||||||
monitor.RemoveSite(m.deleteID)
|
monitor.RemoveSite(m.deleteID)
|
||||||
m.adjustCursor(len(m.sites) - 1)
|
m.adjustCursor(len(m.sites) - 1)
|
||||||
case 1:
|
case 1:
|
||||||
store.Get().DeleteAlert(m.deleteID)
|
if err := s.DeleteAlert(m.deleteID); err != nil {
|
||||||
|
monitor.AddLog("Delete alert failed: " + err.Error())
|
||||||
|
}
|
||||||
m.adjustCursor(len(m.alerts) - 1)
|
m.adjustCursor(len(m.alerts) - 1)
|
||||||
case 3:
|
case 3:
|
||||||
store.Get().DeleteUser(m.deleteID)
|
if err := s.DeleteUser(m.deleteID); err != nil {
|
||||||
|
monitor.AddLog("Delete user failed: " + err.Error())
|
||||||
|
}
|
||||||
m.adjustCursor(len(m.users) - 1)
|
m.adjustCursor(len(m.users) - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,8 +319,8 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
site := m.sites[m.cursor]
|
site := m.sites[m.cursor]
|
||||||
monitor.ToggleSitePause(site.ID)
|
monitor.ToggleSitePause(site.ID)
|
||||||
site.Paused = !site.Paused
|
site.Paused = !site.Paused
|
||||||
if store.Get() != nil {
|
if s := store.Get(); s != nil {
|
||||||
store.Get().UpdateSitePaused(site.ID, site.Paused)
|
_ = s.UpdateSitePaused(site.ID, site.Paused)
|
||||||
}
|
}
|
||||||
m.refreshData()
|
m.refreshData()
|
||||||
}
|
}
|
||||||
@@ -464,10 +470,14 @@ func (m *Model) refreshData() {
|
|||||||
}
|
}
|
||||||
ordered = append(ordered, ungrouped...)
|
ordered = append(ordered, ungrouped...)
|
||||||
m.sites = ordered
|
m.sites = ordered
|
||||||
if store.Get() != nil {
|
if s := store.Get(); s != nil {
|
||||||
m.alerts = store.Get().GetAllAlerts()
|
if alerts, err := s.GetAllAlerts(); err == nil {
|
||||||
|
m.alerts = alerts
|
||||||
|
}
|
||||||
if m.isAdmin {
|
if m.isAdmin {
|
||||||
m.users = store.Get().GetAllUsers()
|
if users, err := s.GetAllUsers(); err == nil {
|
||||||
|
m.users = users
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.logViewport.SetContent(strings.Join(monitor.GetLogs(), "\n"))
|
m.logViewport.SetContent(strings.Join(monitor.GetLogs(), "\n"))
|
||||||
|
|||||||
Reference in New Issue
Block a user