1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
// Persisted ntfy config — single global JSON blob under one settings key.
//
// One global config (no per-user split): the rest of Dispatch's settings
// table is global today (cf. `title_model_*`, `perm_*`), so notification
// config follows the same pattern.
import { deleteSetting, getSetting, setSetting } from "../db/settings.js";
import type { NotificationEventType, NtfyConfig } from "./types.js";
import { NTFY_DEFAULT_EVENTS, NTFY_EVENT_TYPES } from "./types.js";
export const NTFY_CONFIG_KEY = "ntfy_config";
/** Defaults returned when nothing is persisted yet. */
export function defaultNtfyConfig(): NtfyConfig {
return {
enabled: false,
topic: "",
authToken: "",
events: { ...NTFY_DEFAULT_EVENTS },
notifySubagents: false,
};
}
/**
* Normalize an arbitrary parsed JSON value into a complete `NtfyConfig`.
* Tolerant of missing / unexpected fields so a config from an older build
* never throws — missing event toggles fall back to defaults.
*/
export function normalizeNtfyConfig(raw: unknown): NtfyConfig {
const base = defaultNtfyConfig();
if (!raw || typeof raw !== "object") return base;
const obj = raw as Record<string, unknown>;
const out: NtfyConfig = {
enabled: typeof obj.enabled === "boolean" ? obj.enabled : base.enabled,
topic: typeof obj.topic === "string" ? obj.topic : base.topic,
authToken: typeof obj.authToken === "string" ? obj.authToken : base.authToken,
events: { ...base.events },
notifySubagents:
typeof obj.notifySubagents === "boolean" ? obj.notifySubagents : base.notifySubagents,
};
const rawEvents = obj.events;
if (rawEvents && typeof rawEvents === "object") {
const evObj = rawEvents as Record<string, unknown>;
for (const key of NTFY_EVENT_TYPES) {
const v = evObj[key];
if (typeof v === "boolean") out.events[key as NotificationEventType] = v;
}
}
return out;
}
/** Load the persisted config (or defaults if none/corrupt). */
export function loadNtfyConfig(): NtfyConfig {
const raw = getSetting(NTFY_CONFIG_KEY);
if (!raw) return defaultNtfyConfig();
try {
return normalizeNtfyConfig(JSON.parse(raw));
} catch {
return defaultNtfyConfig();
}
}
/** Persist a complete config (after server-side normalization). */
export function saveNtfyConfig(config: NtfyConfig): void {
const normalized = normalizeNtfyConfig(config);
setSetting(NTFY_CONFIG_KEY, JSON.stringify(normalized));
}
/** Wipe the persisted config (revert to defaults on next load). */
export function clearNtfyConfig(): void {
deleteSetting(NTFY_CONFIG_KEY);
}
/** Strip the auth token from a config before returning it over the API. */
export function redactNtfyConfig(config: NtfyConfig): NtfyConfig & { hasAuthToken: boolean } {
return { ...config, authToken: "", hasAuthToken: config.authToken.trim().length > 0 };
}
|