fix(tui,status,store): add delete confirm, input validation, XSS fix, history persistence
Prevent accidental deletes with y/n confirmation dialog. Validate all numeric form inputs (interval, port, timeout, threshold, retries) with range checks instead of silently defaulting to zero. Escape user-supplied data in status page JavaScript to close XSS via monitor names. Persist check history to new check_history table so sparklines and uptime percentages survive restarts.
This commit is contained in:
@@ -56,6 +56,13 @@ func (p *PostgresStore) Init() error {
|
||||
public_key TEXT NOT NULL,
|
||||
role TEXT DEFAULT 'user'
|
||||
);`,
|
||||
`CREATE TABLE IF NOT EXISTS check_history (
|
||||
id SERIAL PRIMARY KEY,
|
||||
site_id INTEGER NOT NULL,
|
||||
latency_ns BIGINT,
|
||||
is_up BOOLEAN,
|
||||
checked_at TIMESTAMP DEFAULT NOW()
|
||||
);`,
|
||||
}
|
||||
for _, q := range queries {
|
||||
if _, err := p.db.Exec(q); err != nil {
|
||||
@@ -63,6 +70,8 @@ func (p *PostgresStore) Init() error {
|
||||
}
|
||||
}
|
||||
|
||||
p.db.Exec("CREATE INDEX IF NOT EXISTS idx_check_history_site ON check_history(site_id, checked_at DESC)")
|
||||
|
||||
migrations := []string{
|
||||
"ALTER TABLE sites ADD COLUMN IF NOT EXISTS hostname TEXT DEFAULT ''",
|
||||
"ALTER TABLE sites ADD COLUMN IF NOT EXISTS port INTEGER DEFAULT 0",
|
||||
@@ -184,7 +193,38 @@ func (p *PostgresStore) DeleteUser(id int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// --- PHASE 5 ---
|
||||
func (p *PostgresStore) SaveCheck(siteID int, latencyNs int64, isUp bool) {
|
||||
p.db.Exec("INSERT INTO check_history (site_id, latency_ns, is_up) VALUES ($1, $2, $3)", siteID, latencyNs, isUp)
|
||||
p.db.Exec(`DELETE FROM check_history WHERE site_id = $1 AND id NOT IN (
|
||||
SELECT id FROM check_history WHERE site_id = $1 ORDER BY checked_at DESC LIMIT 1000
|
||||
)`, siteID)
|
||||
}
|
||||
|
||||
func (p *PostgresStore) LoadAllHistory(limit int) map[int][]models.CheckRecord {
|
||||
result := make(map[int][]models.CheckRecord)
|
||||
rows, err := p.db.Query(`
|
||||
SELECT site_id, latency_ns, is_up FROM (
|
||||
SELECT site_id, latency_ns, is_up,
|
||||
ROW_NUMBER() OVER (PARTITION BY site_id ORDER BY checked_at DESC) AS rn
|
||||
FROM check_history
|
||||
) sub WHERE rn <= $1`, limit)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var r models.CheckRecord
|
||||
rows.Scan(&r.SiteID, &r.LatencyNs, &r.IsUp)
|
||||
result[r.SiteID] = append(result[r.SiteID], r)
|
||||
}
|
||||
for id, records := range result {
|
||||
for i, j := 0, len(records)-1; i < j; i, j = i+1, j-1 {
|
||||
records[i], records[j] = records[j], records[i]
|
||||
}
|
||||
result[id] = records
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *PostgresStore) ExportData() models.Backup {
|
||||
return models.Backup{
|
||||
|
||||
Reference in New Issue
Block a user