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
+23
View File
@@ -0,0 +1,23 @@
"""Keyset cursor helpers for thread listings (SPEC §9.1: cursor = last_message_at,id)."""
from __future__ import annotations
import base64
import datetime as dt
import uuid
def encode_cursor(last_message_at: dt.datetime, thread_id: uuid.UUID) -> str:
raw = f"{last_message_at.isoformat()}|{thread_id}"
return base64.urlsafe_b64encode(raw.encode()).decode()
def decode_cursor(cursor: str | None) -> tuple[dt.datetime, uuid.UUID] | None:
if not cursor:
return None
try:
raw = base64.urlsafe_b64decode(cursor.encode()).decode()
date_str, id_str = raw.split("|", 1)
return dt.datetime.fromisoformat(date_str), uuid.UUID(id_str)
except (ValueError, TypeError):
return None