feat(FN-2339): add configurable ntfy server support

- Add ntfy base URL to global settings schema/types with persistence coverage and regression tests
- Extend dashboard settings API/routes and Settings modal UI to edit and save a custom ntfy server
- Update notifier runtime to use configured ntfy base URL when sending notifications
- Document the new setting and include a changeset for @runfusion/fusion
This commit is contained in:
Fusion
2026-04-24 15:57:35 -07:00
committed by gsxdsm
parent 726a12de1c
commit a8f5591126
13 changed files with 385 additions and 10 deletions

View File

@@ -168,6 +168,7 @@ describe("GlobalSettingsStore", () => {
expect(settings.colorTheme).toBe("ocean");
// Defaults are filled in for missing fields
expect(settings.ntfyEnabled).toBe(false);
expect(settings.ntfyBaseUrl).toBeUndefined();
expect(settings.defaultProvider).toBeUndefined();
});
@@ -273,6 +274,23 @@ describe("GlobalSettingsStore", () => {
expect(settings.ntfyDashboardHost).toBeUndefined();
});
it("clearing ntfyBaseUrl with null removes it from disk and falls back to default behavior", async () => {
await store.init();
await store.updateSettings({ ntfyBaseUrl: "https://ntfy.internal.example" });
const rawBefore = JSON.parse(await readFile(join(dir, "settings.json"), "utf-8"));
expect(rawBefore.ntfyBaseUrl).toBe("https://ntfy.internal.example");
// @ts-expect-error - null is intentionally used to clear field (null-as-delete)
await store.updateSettings({ ntfyBaseUrl: null });
const rawAfter = JSON.parse(await readFile(join(dir, "settings.json"), "utf-8"));
expect(rawAfter.ntfyBaseUrl).toBeUndefined();
const settings = await store.getSettings();
expect(settings.ntfyBaseUrl).toBeUndefined();
});
it("persists custom ntfy event lists including planning-awaiting-input", async () => {
await store.init();

View File

@@ -21,6 +21,7 @@ export const DEFAULT_GLOBAL_SETTINGS = {
defaultThinkingLevel: undefined,
ntfyEnabled: false,
ntfyTopic: undefined,
ntfyBaseUrl: undefined,
ntfyEvents: ["in-review", "merged", "failed", "awaiting-approval", "awaiting-user-review", "planning-awaiting-input"],
ntfyDashboardHost: undefined,
defaultProjectId: undefined,

View File

@@ -1000,8 +1000,13 @@ export interface GlobalSettings {
* Requires ntfyTopic to be set. Default: false. */
ntfyEnabled?: boolean;
/** ntfy.sh topic name for push notifications. When set along with ntfyEnabled,
* notifications are sent to https://ntfy.sh/{topic} when tasks complete or fail. */
* notifications are sent to {ntfyBaseUrl}/{topic} (default: https://ntfy.sh/{topic})
* when tasks complete or fail. */
ntfyTopic?: string;
/** Optional ntfy server base URL for push notifications.
* Must be an http:// or https:// URL. When omitted, notifications default to
* https://ntfy.sh. Example: "https://ntfy.internal.example" */
ntfyBaseUrl?: string;
/** List of notification events to send via ntfy.sh.
* When ntfyEnabled is true, only events in this list will trigger notifications.
* If undefined or empty when ntfyEnabled is true, all events are sent (backward compatible).