diff --git a/cortex/routers/settings.py b/cortex/routers/settings.py index 29e9556..9695bca 100644 --- a/cortex/routers/settings.py +++ b/cortex/routers/settings.py @@ -54,6 +54,26 @@ def _preferred_persona(request: Request, username: str) -> str: return names[0] +def _notifications_page(username: str, back_persona: str = "", success: str = "", error: str = "") -> str: + html = (_STATIC / "notifications.html").read_text() + channels = get_user_channels(username) + notify_ch = _html.escape(channels.get("notification_channel", "") or "") + notify_email = _html.escape(channels.get("notification_email", "") or "") + nc_room = _html.escape((channels.get("nextcloud") or {}).get("notification_room", "") or "") + gc_webhook = _html.escape((channels.get("google_chat") or {}).get("outbound_webhook", "") or "") + html = html.replace("{{ notify_channel }}", notify_ch) + html = html.replace("{{ notify_email_override }}", notify_email) + html = html.replace("{{ nc_notify_room }}", nc_room) + html = html.replace("{{ gc_webhook }}", gc_webhook) + html = html.replace("{{ back_href }}", f"/{username}/{back_persona}" if back_persona else "/") + html = html.replace("{{ help_href }}", f"/help?persona={back_persona}" if back_persona else "/help") + if success: + html = html.replace("", f'

{success}

') + if error: + html = html.replace("", f'

{error}

') + return html + + def _settings_page(username: str, personas: list[str], back_persona: str = "", success: str = "", error: str = "") -> str: html = (_STATIC / "settings.html").read_text() html = html.replace("{{ username }}", username) @@ -74,17 +94,6 @@ def _settings_page(username: str, personas: list[str], back_persona: str = "", s allowlist_text = "" html = html.replace("{{ email_allowlist }}", allowlist_text) - # Notification channel settings - channels = get_user_channels(username) - notify_ch = _html.escape(channels.get("notification_channel", "") or "") - notify_email = _html.escape(channels.get("notification_email", "") or "") - nc_room = _html.escape((channels.get("nextcloud") or {}).get("notification_room", "") or "") - gc_webhook = _html.escape((channels.get("google_chat") or {}).get("outbound_webhook", "") or "") - html = html.replace("{{ notify_channel }}", notify_ch) - html = html.replace("{{ notify_email_override }}", notify_email) - html = html.replace("{{ nc_notify_room }}", nc_room) - html = html.replace("{{ gc_webhook }}", gc_webhook) - # Tool permission policy policy = get_tool_policy(username) tool_allow_text = _html.escape("\n".join(policy.get("allow", []))) @@ -261,6 +270,15 @@ async def rename_persona( return RedirectResponse("/settings", status_code=302) +@router.get("/settings/notifications", include_in_schema=False) +async def notifications_page(request: Request): + username = _get_session_user(request) + if not username: + return RedirectResponse("/login", status_code=302) + back_persona = _preferred_persona(request, username) + return HTMLResponse(_notifications_page(username, back_persona)) + + @router.post("/settings/notifications", include_in_schema=False) async def save_notifications( request: Request, @@ -273,7 +291,6 @@ async def save_notifications( if not username: return RedirectResponse("/login", status_code=302) - personas = list_user_personas(username) back_persona = _preferred_persona(request, username) channels_path = app_settings.home_root() / username / "channels.json" @@ -308,8 +325,7 @@ async def save_notifications( channels_path.write_text(json.dumps(channels, indent=2) + "\n") logger.info("notifications updated for %s (channel=%s)", username, notification_channel or "none") - return HTMLResponse(_settings_page(username, personas, back_persona, - success="Notification settings saved.")) + return HTMLResponse(_notifications_page(username, back_persona, success="Notification settings saved.")) @router.post("/settings/tool-policy", include_in_schema=False) diff --git a/cortex/static/notifications.html b/cortex/static/notifications.html new file mode 100644 index 0000000..af902fd --- /dev/null +++ b/cortex/static/notifications.html @@ -0,0 +1,296 @@ + + + + + + Cortex — Notifications + + + + + + + +
+ + + + + + + + +
+

Channel

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+

Test

+

+ Fire a notification via your configured channel or run the reminder check + immediately — no need to wait for the daily 09:00 scheduler job. +

+
+ + +
+
+
+
+ + + + diff --git a/cortex/static/settings.html b/cortex/static/settings.html index 80710dd..b4a76fb 100644 --- a/cortex/static/settings.html +++ b/cortex/static/settings.html @@ -264,6 +264,7 @@ ← Chat Help Settings + Notifications Sign out @@ -348,50 +349,14 @@

Notifications

- Choose how Inara reaches out proactively — cron jobs, briefs, and future alerts. - Email defaults to your login address when no override is set. + Configure how Inara reaches out proactively — reminders, cron jobs, and memory digests.

-
-
- - -
-
- - -
-
- - -
-
- - -
- -
+ + Notification settings → +
@@ -537,12 +502,6 @@