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:
2026-07-17 23:20:34 +02:00
co-authored by Claude Opus 4.8
commit a5ec0d21cb
174 changed files with 22239 additions and 0 deletions
+30
View File
@@ -0,0 +1,30 @@
"""Build a MailProvider from an account row (SPEC §10.2 decryption boundary).
This module runs only inside the worker/sync context; ``decrypt_secret`` (the
credential vault) is invoked here to obtain the plaintext mailbox password. It
must never be imported by API request code.
"""
from __future__ import annotations
from app.models import Account
from app.providers.imap_smtp import ImapSmtpProvider, ProviderConfig
from app.security.crypto import decrypt_secret
def build_provider(account: Account) -> ImapSmtpProvider:
"""Construct an :class:`ImapSmtpProvider` for ``account`` (password auth, M0)."""
imap = account.imap_config
smtp = account.smtp_config
password = decrypt_secret(account.secret_ct).decode("utf-8")
config = ProviderConfig(
imap_host=str(imap["host"]),
imap_port=int(str(imap["port"])),
imap_tls=str(imap.get("tls", "ssl")),
smtp_host=str(smtp["host"]),
smtp_port=int(str(smtp["port"])),
smtp_tls=str(smtp.get("tls", "ssl")),
username=account.email,
password=password,
)
return ImapSmtpProvider(config)