Files
webmail-client/backend/app/api/snooze.py
T

77 lines
2.5 KiB
Python
Raw Normal View History

"""Snooze endpoints: hide a conversation from the Inbox until a set time."""
from __future__ import annotations
import datetime as dt
import uuid
from typing import Annotated
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app import snooze
from app.api.errors import raise_api_error
from app.db import get_session
from app.events import get_event_bus
from app.identity.deps import CurrentUser, require_csrf
from app.models import Thread
router = APIRouter(prefix="/threads", tags=["mail"], dependencies=[Depends(require_csrf)])
DbSession = Annotated[AsyncSession, Depends(get_session)]
class SnoozeIn(BaseModel):
until: dt.datetime
class SnoozeOut(BaseModel):
thread_id: uuid.UUID
snoozed_until: dt.datetime | None
async def _thread(db: AsyncSession, user_id: uuid.UUID, thread_id: uuid.UUID) -> Thread:
thread = await db.scalar(
select(Thread).where(Thread.id == thread_id, Thread.user_id == user_id)
)
if thread is None:
raise_api_error(404, "not_found", "thread not found")
return thread
async def _announce(user_id: uuid.UUID, thread_id: uuid.UUID) -> None:
await get_event_bus().publish(
"thread.updated", {"user_id": str(user_id), "thread_id": str(thread_id)}
)
@router.post("/{thread_id}/snooze", response_model=SnoozeOut)
async def snooze_thread(
thread_id: uuid.UUID, body: SnoozeIn, user: CurrentUser, db: DbSession
) -> SnoozeOut:
until = body.until
if until.tzinfo is None:
raise_api_error(400, "invalid_time", "the time needs a time zone")
now = dt.datetime.now(dt.UTC)
if until <= now:
raise_api_error(400, "invalid_time", "pick a time in the future")
if until > now + snooze.MAX_SNOOZE:
raise_api_error(400, "invalid_time", "snooze for at most a year")
thread = await _thread(db, user.id, thread_id)
snooze.snooze(thread, until, now)
await db.commit()
await _announce(user.id, thread_id)
return SnoozeOut(thread_id=thread_id, snoozed_until=thread.snoozed_until)
@router.post("/{thread_id}/unsnooze", response_model=SnoozeOut)
async def unsnooze_thread(thread_id: uuid.UUID, user: CurrentUser, db: DbSession) -> SnoozeOut:
thread = await _thread(db, user.id, thread_id)
if thread.snoozed_until is not None:
snooze.wake(thread, dt.datetime.now(dt.UTC))
await db.commit()
await _announce(user.id, thread_id)
return SnoozeOut(thread_id=thread_id, snoozed_until=None)