""" Account settings router. Routes: GET /settings → show account settings page (requires auth) POST /settings/password → change password """ import logging from pathlib import Path import jwt from fastapi import APIRouter, Form, Request from fastapi.responses import HTMLResponse, RedirectResponse from auth_utils import COOKIE_NAME, decode_token, check_credentials, set_password from persona import list_user_personas logger = logging.getLogger(__name__) router = APIRouter() _STATIC = Path(__file__).parent.parent / "static" def _get_session_user(request: Request) -> str | None: token = request.cookies.get(COOKIE_NAME) if not token: return None try: return decode_token(token) except jwt.InvalidTokenError: return None def _settings_page(username: str, personas: list[str], success: str = "", error: str = "") -> str: html = (_STATIC / "settings.html").read_text() html = html.replace("{{ username }}", username) persona_items = "\n".join( f'
  • {p}
  • ' for p in personas ) html = html.replace("{{ persona_items }}", persona_items or "
  • No personas yet.
  • ") back_persona = personas[0] if personas else "" html = html.replace("{{ back_href }}", f"/{username}/{back_persona}" if back_persona else "/") if success: html = html.replace("", f'

    {success}

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

    {error}

    ') return html @router.get("/settings", include_in_schema=False) async def settings_page(request: Request): username = _get_session_user(request) if not username: return RedirectResponse("/login", status_code=302) personas = list_user_personas(username) return HTMLResponse(_settings_page(username, personas)) @router.post("/settings/password", include_in_schema=False) async def change_password( request: Request, current_password: str = Form(...), new_password: str = Form(...), confirm_password: str = Form(...), ): username = _get_session_user(request) if not username: return RedirectResponse("/login", status_code=302) personas = list_user_personas(username) if not check_credentials(username, current_password): return HTMLResponse(_settings_page(username, personas, error="Current password is incorrect.")) if len(new_password) < 8: return HTMLResponse(_settings_page(username, personas, error="New password must be at least 8 characters.")) if new_password != confirm_password: return HTMLResponse(_settings_page(username, personas, error="New passwords do not match.")) set_password(username, new_password) logger.info("password changed: %s", username) return HTMLResponse(_settings_page(username, personas, success="Password updated successfully."))