fix: fail closed on corrupt mute schedules and clean invalid replicas

Empty or whitespace stored schedules no longer act as all-day mutes. Invalid schedules trigger remote DELETE cleanup, and the weekly-window form gains accessibility attributes plus component coverage.
This commit is contained in:
SaelixCode
2026-07-21 11:47:56 -04:00
parent 48a2662f5e
commit 1d1fba0e33
7 changed files with 303 additions and 29 deletions
+13 -6
View File
@@ -73,22 +73,29 @@ export function parseNotificationSchedule(raw: unknown): ParseNotificationSchedu
}
/**
* Parse a DB column value. Missing/null → legacy null (always in window).
* Non-null corrupt JSON → invalid (caller must not suppress).
* Parse a DB column value. Only SQL null / undefined → legacy null (always in window).
* Empty string, whitespace, or any non-null corrupt value → invalid (must not suppress).
*/
export function parseStoredNotificationSchedule(
raw: unknown,
): { kind: 'null' } | { kind: 'ok'; schedule: NotificationSchedule } | { kind: 'invalid' } {
if (raw == null || raw === '') return { kind: 'null' };
let parsed: unknown = raw;
if (raw == null) return { kind: 'null' };
if (typeof raw === 'string') {
const trimmed = raw.trim();
if (trimmed === '') return { kind: 'invalid' };
let parsed: unknown;
try {
parsed = JSON.parse(raw);
parsed = JSON.parse(trimmed);
} catch {
return { kind: 'invalid' };
}
// JSON null is not SQL NULL; treat as corrupt rather than legacy always-on.
if (parsed == null) return { kind: 'invalid' };
const result = parseNotificationSchedule(parsed);
if (!result.ok) return { kind: 'invalid' };
return { kind: 'ok', schedule: result.schedule };
}
const result = parseNotificationSchedule(parsed);
const result = parseNotificationSchedule(raw);
if (!result.ok) return { kind: 'invalid' };
return { kind: 'ok', schedule: result.schedule };
}