settings: digest opt-in + tone (PATCH /api/settings/digest + UI)

Adds DigestPrefsIn/Out models, PATCH /api/settings/digest endpoint, email
digest section in settings.html, and last_email_send context in pages.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-25 23:23:03 +02:00
parent 5c89f4d04a
commit 14fe47103f
4 changed files with 176 additions and 1 deletions

View file

@ -8,6 +8,7 @@ from __future__ import annotations
import calendar as _cal
import re
from datetime import date, datetime, timedelta, timezone
from typing import Literal
from fastapi import APIRouter, Depends, File, Form, HTTPException, Query, Request, UploadFile
from fastapi.responses import HTMLResponse, JSONResponse
@ -809,3 +810,33 @@ async def chat(
"prompt_tokens": result.prompt_tokens,
"completion_tokens": result.completion_tokens,
}
# ---------------------------------------------------------------------------
# Settings — digest preferences
# ---------------------------------------------------------------------------
class DigestPrefsIn(BaseModel):
opt_in: bool
tone: Literal["NOVICE", "INTERMEDIATE"]
class DigestPrefsOut(BaseModel):
opt_in: bool
tone: str
@router.patch("/settings/digest", response_model=DigestPrefsOut)
async def patch_digest_prefs(
payload: DigestPrefsIn,
principal: CurrentUser = Depends(require_token),
session: AsyncSession = Depends(get_session),
) -> DigestPrefsOut:
if principal.user is None:
# Admin bearer-token path — no per-user row to persist to.
raise HTTPException(status_code=400, detail="no_user_context")
principal.user.email_digest_opt_in = payload.opt_in
principal.user.digest_tone = payload.tone
await session.commit()
return DigestPrefsOut(opt_in=payload.opt_in, tone=payload.tone)