summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/notifications/config.ts
blob: 49e6ff445f72ea53a4ba7b8d1c340226051ecce7 (plain)
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 };
}