diff --git a/client/__tests__/SettingsPage.test.tsx b/client/__tests__/SettingsPage.test.tsx index 36d4252c..8f2d2251 100644 --- a/client/__tests__/SettingsPage.test.tsx +++ b/client/__tests__/SettingsPage.test.tsx @@ -332,6 +332,8 @@ describe("SettingsPage", () => { apiUrl: "http://apprise:8000", key: "", urls: "discord://webhook/xyz", + username: "", + password: "", }); }); }); diff --git a/client/src/pages/settings.tsx b/client/src/pages/settings.tsx index b25f1de5..abcb197a 100644 --- a/client/src/pages/settings.tsx +++ b/client/src/pages/settings.tsx @@ -102,6 +102,14 @@ const NOTIFICATION_EVENT_ROWS: { key: NotificationEvent; label: string; group: s { key: "errorDetected", label: "Error Detected", group: "system" }, ]; +function isHttpUrl(value: string): boolean { + try { + return new URL(value.trim()).protocol === "http:"; + } catch { + return false; + } +} + function SettingsToggleRow({ id, label, @@ -374,6 +382,8 @@ export default function SettingsPage() { const [appriseApiUrl, setAppriseApiUrl] = useState(""); const [appriseKey, setAppriseKey] = useState(""); const [appriseUrls, setAppriseUrls] = useState(""); + const [appriseUsername, setAppriseUsername] = useState(""); + const [apprisePassword, setApprisePassword] = useState(""); const appriseLoadedRef = useRef(false); const settingsLoadedRef = useRef(false); @@ -531,6 +541,8 @@ export default function SettingsPage() { apiUrl: string | null; key: string | null; urls: string | null; + username?: string; + password?: string; }>({ queryKey: ["/api/settings/apprise"], queryFn: () => apiRequest("GET", "/api/settings/apprise").then((r) => r.json()), @@ -542,6 +554,8 @@ export default function SettingsPage() { if (appriseSettings.apiUrl !== undefined) setAppriseApiUrl(appriseSettings.apiUrl ?? ""); if (appriseSettings.key !== undefined) setAppriseKey(appriseSettings.key ?? ""); if (appriseSettings.urls !== undefined) setAppriseUrls(appriseSettings.urls ?? ""); + setAppriseUsername(appriseSettings.username ?? ""); + setApprisePassword(appriseSettings.password ?? ""); appriseLoadedRef.current = true; } }, [appriseSettings]); @@ -560,6 +574,8 @@ export default function SettingsPage() { apiUrl?: string; key?: string; urls?: string; + username?: string; + password?: string; }) => { const res = await apiRequest("POST", "/api/settings/apprise", data); return res.json(); @@ -1655,6 +1671,50 @@ export default function SettingsPage() {

)} + {appriseMode === "api" && ( +
+
+ + setAppriseUsername(e.target.value)} + /> +
+
+ + setApprisePassword(e.target.value)} + /> +
+

+ Only needed when your Apprise API server requires a login:{" "} + APPRISE_AUTH_REQUIRED=yes. +

+ {(appriseUsername.trim() || apprisePassword) && isHttpUrl(appriseApiUrl) && ( +

+ This API URL uses http://, so the login is sent unencrypted. Use https:// + unless Apprise runs on a network you trust. +

+ )} +
+ )}