9eaddbb6e8
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.
149 lines
4.8 KiB
Go
149 lines
4.8 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
|
|
"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
|
|
)
|
|
|
|
func (s *SQLStore) scanMaintenanceWindow(rows *sql.Rows) (models.MaintenanceWindow, error) {
|
|
var mw models.MaintenanceWindow
|
|
var endTime sql.NullTime
|
|
if err := rows.Scan(&mw.ID, &mw.MonitorID, &mw.Title, &mw.Description, &mw.Type, &mw.StartTime, &endTime, &mw.CreatedBy, &mw.CreatedAt); err != nil {
|
|
return mw, err
|
|
}
|
|
if endTime.Valid {
|
|
mw.EndTime = endTime.Time
|
|
}
|
|
return mw, nil
|
|
}
|
|
|
|
func (s *SQLStore) GetActiveMaintenanceWindows(ctx context.Context) ([]models.MaintenanceWindow, error) {
|
|
rows, err := s.db.QueryContext(ctx, s.q("SELECT id, monitor_id, title, description, type, start_time, end_time, created_by, created_at FROM maintenance_windows WHERE start_time <= CURRENT_TIMESTAMP AND (end_time IS NULL OR end_time > CURRENT_TIMESTAMP) ORDER BY start_time DESC"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var windows []models.MaintenanceWindow
|
|
for rows.Next() {
|
|
mw, err := s.scanMaintenanceWindow(rows)
|
|
if err != nil {
|
|
return windows, err
|
|
}
|
|
windows = append(windows, mw)
|
|
}
|
|
return windows, rows.Err()
|
|
}
|
|
|
|
func (s *SQLStore) GetAllMaintenanceWindows(ctx context.Context, limit int) ([]models.MaintenanceWindow, error) {
|
|
rows, err := s.db.QueryContext(ctx, s.q("SELECT id, monitor_id, title, description, type, start_time, end_time, created_by, created_at FROM maintenance_windows ORDER BY created_at DESC LIMIT ?"), limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var windows []models.MaintenanceWindow
|
|
for rows.Next() {
|
|
mw, err := s.scanMaintenanceWindow(rows)
|
|
if err != nil {
|
|
return windows, err
|
|
}
|
|
windows = append(windows, mw)
|
|
}
|
|
return windows, rows.Err()
|
|
}
|
|
|
|
func (s *SQLStore) GetOverlappingMaintenanceWindows(ctx context.Context, monitorID int, startTime, endTime time.Time) ([]models.MaintenanceWindow, error) {
|
|
var timeClause string
|
|
var args []interface{}
|
|
|
|
if endTime.IsZero() {
|
|
timeClause = "(end_time IS NULL OR end_time > ?)"
|
|
args = append(args, startTime)
|
|
} else {
|
|
timeClause = "(end_time IS NULL OR end_time > ?) AND start_time < ?"
|
|
args = append(args, startTime, endTime)
|
|
}
|
|
|
|
var scopeClause string
|
|
if monitorID == 0 {
|
|
scopeClause = "1=1"
|
|
} else {
|
|
scopeClause = "(monitor_id = ? OR monitor_id = 0 OR monitor_id IN (SELECT parent_id FROM sites WHERE id = ? AND parent_id > 0))"
|
|
args = append(args, monitorID, monitorID)
|
|
}
|
|
|
|
query := fmt.Sprintf(
|
|
"SELECT id, monitor_id, title, description, type, start_time, end_time, created_by, created_at FROM maintenance_windows WHERE %s AND %s ORDER BY start_time",
|
|
timeClause, scopeClause,
|
|
)
|
|
|
|
rows, err := s.db.QueryContext(ctx, s.q(query), args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
var windows []models.MaintenanceWindow
|
|
for rows.Next() {
|
|
mw, err := s.scanMaintenanceWindow(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
windows = append(windows, mw)
|
|
}
|
|
return windows, rows.Err()
|
|
}
|
|
|
|
func (s *SQLStore) AddMaintenanceWindow(ctx context.Context, mw models.MaintenanceWindow) error {
|
|
if mw.StartTime.IsZero() {
|
|
mw.StartTime = time.Now()
|
|
}
|
|
_, err := s.db.ExecContext(ctx, s.q("INSERT INTO maintenance_windows (monitor_id, title, description, type, start_time, end_time, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)"),
|
|
mw.MonitorID, mw.Title, mw.Description, mw.Type, mw.StartTime, sql.NullTime{Time: mw.EndTime, Valid: !mw.EndTime.IsZero()}, mw.CreatedBy)
|
|
return err
|
|
}
|
|
|
|
func (s *SQLStore) EndMaintenanceWindow(ctx context.Context, id int) error {
|
|
_, err := s.db.ExecContext(ctx, s.q("UPDATE maintenance_windows SET end_time = CURRENT_TIMESTAMP WHERE id = ?"), id)
|
|
return err
|
|
}
|
|
|
|
func (s *SQLStore) DeleteMaintenanceWindow(ctx context.Context, id int) error {
|
|
_, err := s.db.ExecContext(ctx, s.q("DELETE FROM maintenance_windows WHERE id = ?"), id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.dialect.ResetSequenceOnEmpty(s.db, "maintenance_windows")
|
|
return nil
|
|
}
|
|
|
|
func (s *SQLStore) PruneExpiredMaintenanceWindows(ctx context.Context, retention time.Duration) (int64, error) {
|
|
cutoff := time.Now().Add(-retention)
|
|
result, err := s.db.ExecContext(ctx,
|
|
s.q("DELETE FROM maintenance_windows WHERE end_time IS NOT NULL AND end_time < ?"),
|
|
cutoff,
|
|
)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected()
|
|
}
|
|
|
|
func (s *SQLStore) IsMonitorInMaintenance(ctx context.Context, monitorID int) (bool, error) {
|
|
var count int
|
|
err := s.db.QueryRowContext(ctx, s.q(`SELECT COUNT(*) FROM maintenance_windows
|
|
WHERE type = 'maintenance'
|
|
AND start_time <= CURRENT_TIMESTAMP
|
|
AND (end_time IS NULL OR end_time > CURRENT_TIMESTAMP)
|
|
AND (monitor_id = 0 OR monitor_id = ?
|
|
OR monitor_id IN (SELECT parent_id FROM sites WHERE id = ? AND parent_id > 0))`),
|
|
monitorID, monitorID).Scan(&count)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return count > 0, nil
|
|
}
|