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.
+
+ )}
+
+ )}