import { useEffect, useState } from "react";
import {
api,
type AppSettings,
type GotifySettings,
type NtfySettings,
type SmtpSettings,
type WebhookSettings,
type NotificationEvents,
} from "../../api/client";
const TIMEZONES = [
"UTC",
"Europe/Stockholm",
"Europe/London",
"Europe/Berlin",
"Europe/Paris",
"Europe/Oslo",
"Europe/Helsinki",
"Europe/Amsterdam",
"Europe/Copenhagen",
"America/New_York",
"America/Chicago",
"America/Denver",
"America/Los_Angeles",
"America/Toronto",
"America/Sao_Paulo",
"Asia/Tokyo",
"Asia/Shanghai",
"Asia/Singapore",
"Asia/Dubai",
"Australia/Sydney",
];
const DEFAULT_GOTIFY: GotifySettings = { enabled: false, url: "", token: "", priority: 5 };
const DEFAULT_NTFY: NtfySettings = { enabled: false, url: "https://ntfy.sh", topic: "", token: "", priority: 3 };
const DEFAULT_SMTP: SmtpSettings = { enabled: false, host: "", port: 587, secure: false, username: "", password: "", from: "", to: "" };
const DEFAULT_WEBHOOK: WebhookSettings = { enabled: false, url: "", secret: "" };
const DEFAULT_NOTIFICATIONS: NotificationEvents = {
dnsAdd: true,
dnsUpdate: true,
dnsDelete: true,
secretCheck: true,
tailscaleKeyCheck: true,
secretCheckTime: "08:00",
timezone: "UTC",
};
type TestResult = { ok: boolean; message: string } | null;
function TestBadge({ result }: { result: TestResult }) {
if (!result) return null;
return (
{result.ok ? "✓" : "✕"} {result.message}
);
}
export default function NotificationSettings() {
const [loading, setLoading] = useState(true);
const [loadError, setLoadError] = useState(null);
const [saving, setSaving] = useState(false);
const [saved, setSaved] = useState(false);
const [saveError, setSaveError] = useState(null);
const [gotify, setGotify] = useState(DEFAULT_GOTIFY);
const [ntfy, setNtfy] = useState(DEFAULT_NTFY);
const [smtp, setSmtp] = useState(DEFAULT_SMTP);
const [webhook, setWebhook] = useState(DEFAULT_WEBHOOK);
const [notifications, setNotifications] = useState(DEFAULT_NOTIFICATIONS);
const [testingGotify, setTestingGotify] = useState(false);
const [testGotifyResult, setTestGotifyResult] = useState(null);
const [testingNtfy, setTestingNtfy] = useState(false);
const [testNtfyResult, setTestNtfyResult] = useState(null);
const [testingSmtp, setTestingSmtp] = useState(false);
const [testSmtpResult, setTestSmtpResult] = useState(null);
const [testingWebhook, setTestingWebhook] = useState(false);
const [testWebhookResult, setTestWebhookResult] = useState(null);
useEffect(() => {
api.settings
.get()
.then((res: { settings: AppSettings }) => {
setGotify(res.settings.gotify);
setNtfy(res.settings.ntfy);
setSmtp(res.settings.smtp);
setWebhook(res.settings.webhook);
setNotifications(res.settings.notifications);
})
.catch((err) => setLoadError(err instanceof Error ? err.message : String(err)))
.finally(() => setLoading(false));
}, []);
async function handleSave() {
setSaving(true);
setSaveError(null);
setSaved(false);
try {
await api.settings.update({ gotify, ntfy, smtp, webhook, notifications });
setSaved(true);
setTimeout(() => setSaved(false), 3000);
} catch (err) {
setSaveError(err instanceof Error ? err.message : String(err));
} finally {
setSaving(false);
}
}
async function handleTestGotify() {
setTestingGotify(true);
setTestGotifyResult(null);
try {
await api.settings.testGotify(gotify);
setTestGotifyResult({ ok: true, message: "Notification sent successfully." });
} catch (err) {
setTestGotifyResult({ ok: false, message: err instanceof Error ? err.message : String(err) });
} finally {
setTestingGotify(false);
}
}
async function handleTestNtfy() {
setTestingNtfy(true);
setTestNtfyResult(null);
try {
await api.settings.testNtfy(ntfy);
setTestNtfyResult({ ok: true, message: "Notification sent successfully." });
} catch (err) {
setTestNtfyResult({ ok: false, message: err instanceof Error ? err.message : String(err) });
} finally {
setTestingNtfy(false);
}
}
async function handleTestSmtp() {
setTestingSmtp(true);
setTestSmtpResult(null);
try {
await api.settings.testSmtp(smtp);
setTestSmtpResult({ ok: true, message: "Test email sent successfully." });
} catch (err) {
setTestSmtpResult({ ok: false, message: err instanceof Error ? err.message : String(err) });
} finally {
setTestingSmtp(false);
}
}
async function handleTestWebhook() {
setTestingWebhook(true);
setTestWebhookResult(null);
try {
await api.settings.testWebhook(webhook);
setTestWebhookResult({ ok: true, message: "Webhook delivered successfully." });
} catch (err) {
setTestWebhookResult({ ok: false, message: err instanceof Error ? err.message : String(err) });
} finally {
setTestingWebhook(false);
}
}
if (loading) return Loading…
;
return (
<>
Notifications
{saveError && {saveError}}
{saved && ✓ Settings saved}
{loadError && {loadError}
}
Gotify
ntfy
Email (SMTP)
Webhook
Notify on
Controls which events trigger a notification on every enabled channel above.
{[
{ key: "dnsAdd" as const, label: "DNS record added" },
{ key: "dnsUpdate" as const, label: "DNS record updated" },
{ key: "dnsDelete" as const, label: "DNS record deleted" },
{ key: "secretCheck" as const, label: "Secret expiry reminder" },
{ key: "tailscaleKeyCheck" as const, label: "Tailscale key expiry reminder" },
].map(({ key, label }) => (
))}
{(() => {
const dailyChecksEnabled = notifications.secretCheck || notifications.tailscaleKeyCheck;
return (
);
})()}
>
);
}