Initial commit: webmail client (M0/M1)
Three-pane webmail client. - Backend: FastAPI + async SQLAlchemy + arq worker (Postgres/Redis), IMAP/SMTP sync, threading, search, drafts/outbox with undo, attachments, 2FA. - Frontend: React 18 + Vite + TypeScript + TanStack Query + Tailwind; three-pane UI with rich-text composer, light/dark themes, resizable panels. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""Shared API response models (SPEC §9.1)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import uuid
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class Address(BaseModel):
|
||||
name: str = ""
|
||||
email: str = ""
|
||||
|
||||
|
||||
class ThreadSummary(BaseModel):
|
||||
id: uuid.UUID
|
||||
subject: str
|
||||
snippet: str
|
||||
participants: list[Address]
|
||||
account_ids: list[uuid.UUID]
|
||||
message_count: int
|
||||
unread_count: int
|
||||
has_attachments: bool
|
||||
pinned: bool
|
||||
last_message_at: dt.datetime
|
||||
|
||||
|
||||
class ThreadListOut(BaseModel):
|
||||
threads: list[ThreadSummary]
|
||||
cursor: str | None = None
|
||||
|
||||
|
||||
class AttachmentOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
filename: str
|
||||
mime_type: str
|
||||
size_bytes: int
|
||||
|
||||
|
||||
class MessageEnvelope(BaseModel):
|
||||
id: uuid.UUID
|
||||
account_id: uuid.UUID
|
||||
mailbox_id: uuid.UUID
|
||||
thread_id: uuid.UUID | None
|
||||
subject: str
|
||||
from_addr: list[Address]
|
||||
to_addr: list[Address]
|
||||
cc_addr: list[Address]
|
||||
date: dt.datetime
|
||||
flags: list[str]
|
||||
is_unread: bool
|
||||
is_starred: bool
|
||||
has_attachments: bool
|
||||
has_remote_images: bool
|
||||
snippet: str
|
||||
|
||||
|
||||
class ThreadDetail(BaseModel):
|
||||
id: uuid.UUID
|
||||
subject: str
|
||||
order: str # "asc" | "desc"
|
||||
pinned: bool
|
||||
messages: list[MessageEnvelope] # always ASC (SPEC §9.4)
|
||||
|
||||
|
||||
class FlagChangeRequest(BaseModel):
|
||||
add: list[str] = []
|
||||
remove: list[str] = []
|
||||
|
||||
|
||||
class MoveRequest(BaseModel):
|
||||
mailbox_id: uuid.UUID
|
||||
|
||||
|
||||
class AccountOut(BaseModel):
|
||||
id: uuid.UUID
|
||||
email: str
|
||||
display_name: str
|
||||
color: str
|
||||
state: str
|
||||
|
||||
|
||||
class SearchThreadHit(BaseModel):
|
||||
thread: ThreadSummary
|
||||
highlights: dict[str, str]
|
||||
|
||||
|
||||
class SearchOut(BaseModel):
|
||||
threads: list[SearchThreadHit]
|
||||
cursor: str | None = None
|
||||
Reference in New Issue
Block a user