feat: email allowlist management in Settings + Files panel

Settings page gets an editable textarea (POST /settings/email-allowlist)
so users can view and update their per-user regex allowlist without
touching the raw JSON file.

Files panel gains a "Settings" group containing email_allowlist.json as
a raw JSON editor backup — served from home/{user}/ via files.py USER_FILES.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scott Idem
2026-04-29 21:56:45 -04:00
parent e0e3170de3
commit db3dd465b2
4 changed files with 85 additions and 3 deletions

View File

@@ -8,6 +8,8 @@ Routes:
POST /settings/persona/rename → rename a persona directory
"""
import html as _html
import json
import logging
import re
from pathlib import Path
@@ -63,6 +65,14 @@ def _settings_page(username: str, personas: list[str], back_persona: str = "", s
role = auth_data.get("role", "user")
html = html.replace("{{ user_role }}", role)
al_path = app_settings.home_root() / username / "email_allowlist.json"
try:
patterns = json.loads(al_path.read_text())
allowlist_text = _html.escape("\n".join(str(p) for p in patterns if str(p).strip()))
except Exception:
allowlist_text = ""
html = html.replace("{{ email_allowlist }}", allowlist_text)
persona_items = "\n".join(
f'''<li>
<a href="/{username}/{p}" class="persona-link">{p}</a>
@@ -228,3 +238,21 @@ async def rename_persona(
old_dir.rename(new_dir)
logger.info("persona renamed: %s/%s%s", username, old_name, new_name)
return RedirectResponse("/settings", status_code=302)
@router.post("/settings/email-allowlist", include_in_schema=False)
async def save_email_allowlist(
request: Request,
patterns: str = Form(""),
):
username = _get_session_user(request)
if not username:
return RedirectResponse("/login", status_code=302)
personas = list_user_personas(username)
back_persona = _preferred_persona(request, username)
lines = [ln.strip() for ln in patterns.splitlines() if ln.strip()]
path = app_settings.home_root() / username / "email_allowlist.json"
path.write_text(json.dumps(lines, indent=2))
logger.info("email allowlist updated for %s (%d patterns)", username, len(lines))
return HTMLResponse(_settings_page(username, personas, back_persona, success=f"Email allowlist saved ({len(lines)} pattern{'s' if len(lines) != 1 else ''})."))