fix(alerts): harden with security fixes, design compliance, and test coverage (#570)

* fix(alerts): harden with security fixes, design compliance, and test coverage

Add authMiddleware to all alert endpoints, validate notification test
dispatch inputs, fix restart_count metric via Docker inspect, correct
network metric units, replace any types with DockerContainerStats
interface, add webhook timeouts and dispatch error tracking.

Frontend: migrate Select to Combobox, add ScrollArea and delete
confirmation AlertDialog, fix icon strokeWidth to 1.5.

Add update availability notifications for both Sencho version updates
(6-hour check in MonitorService) and stack image updates (state
transition detection in ImageUpdateService). Extract shared version
fetch logic into utils/version-check.ts.

Add diagnostic logging gated behind developer_mode for MonitorService
breach state machine and NotificationService dispatch routing.

Tests: 24 new alert API integration tests, restart_count and version
check unit tests (688 total passing). Docs updated with HTTPS
requirement, update notifications section, and troubleshooting guide.

* fix(alerts): remove unused TEST_USERNAME import in alerts-api tests
This commit is contained in:
Anso
2026-04-13 21:40:53 -04:00
committed by GitHub
parent 4cf6949e9f
commit 0a94df318a
11 changed files with 963 additions and 282 deletions
+9 -2
View File
@@ -655,6 +655,8 @@ export class DatabaseService {
);
CREATE INDEX IF NOT EXISTS idx_notification_routes_priority ON notification_routes(priority);
`);
// Track external dispatch errors on notification records
try { this.db.prepare('ALTER TABLE notification_history ADD COLUMN dispatch_error TEXT').run(); } catch { /* already exists */ }
}
// --- Agents ---
@@ -796,11 +798,11 @@ export class DatabaseService {
}
}
public addStackAlert(alert: StackAlert): void {
public addStackAlert(alert: StackAlert): StackAlert {
const stmt = this.db.prepare(
'INSERT INTO stack_alerts (stack_name, metric, operator, threshold, duration_mins, cooldown_mins, last_fired_at) VALUES (?, ?, ?, ?, ?, ?, ?)'
);
stmt.run(
const result = stmt.run(
alert.stack_name,
alert.metric,
alert.operator,
@@ -809,6 +811,7 @@ export class DatabaseService {
alert.cooldown_mins,
alert.last_fired_at || 0
);
return this.db.prepare('SELECT * FROM stack_alerts WHERE id = ?').get(result.lastInsertRowid) as StackAlert;
}
public deleteStackAlert(id: number): void {
@@ -866,6 +869,10 @@ export class DatabaseService {
stmt.run();
}
public updateNotificationDispatchError(id: number, error: string): void {
this.db.prepare('UPDATE notification_history SET dispatch_error = ? WHERE id = ?').run(error, id);
}
// --- Container Metrics ---
public addContainerMetric(metric: Omit<any, 'id'>): void {