mail/CLAUDE.md
deeily 5024bf9a8d init: full mail stack — phases 0..8 (web client, admin, IMAP/SMTP,
sieve, search, sessions, dramatiq, deploy/install, ELK, monitoring)
2026-04-29 16:30:43 +03:00

6.2 KiB
Raw Permalink Blame History

Контекст проекта — почтовый модуль

Веб-клиент почты + админка почтового сервера для внутренней платформы (~300 пользователей). Встраивается через iframe. Самописный аналог Nextcloud/Bitrix. Отдельный репо, не git-инициализирован.

Стек

  • Flask 3 + Python 3.13 (SSR Jinja2, blueprints), pip + requirements.txt
  • docker-mailserver v14 (Postfix + Dovecot + Rspamd) — источник правды для почты
  • PostgreSQL + Redis + Meilisearch + Dramatiq (docker-compose)
  • IMAPClient — IMAP, smtplib — SMTP submission port 587
  • UI: web_template/ стиль (Notion-like, 0.5px borders, inline SVG, 13px, CSS vars, без фреймворков)
  • Nginx Proxy Manager на хосте, Let's Encrypt, домены koreana.link / koreana.rest

Сервер разработки

  • 10.101.10.223 (Debian 13), пользователь deeily, SSH-ключ ~/.ssh/mail_deploy
  • Приложение: /home/deeily/mail/, venv: .venv/, запуск: nohup .venv/bin/python wsgi.py >/tmp/flask.log 2>&1 &
  • Конфиг: /home/deeily/mail/.env (USE_MOCK_MAIL=0, IMAP_PASSWORD=admin123, SEND_DEFER_SECONDS=10)
  • Docker: cd ~/mail/docker && docker compose up -d
  • DMS hostname: smtp.mail.local, аккаунты: admin/admin123, user1/user123, user2/user123
  • Деплой: rsync -az -e "ssh -i ~/.ssh/mail_deploy" <src> deeily@10.101.10.223:<dst>

Ключевые файлы

app/__init__.py              — Flask factory, context_processor inject_nav
app/config.py                — Config class, USE_MOCK_MAIL, IMAP_*, SMTP_*, SEND_DEFER_SECONDS
app/services/mock_mail.py    — in-memory mock (USE_MOCK_MAIL=1), источник групп/правил/подписей/shared
app/services/imap_client.py  — реальный IMAP (IMAPClient), list_threads/get_message/move/flags/bulk
app/services/smtp_sender.py  — реальный SMTP (smtplib port 587)
app/services/mail_service.py — адаптер mock↔real, единый API для blueprints
app/services/imap_idle.py    — IMAP IDLE manager, push через SSE
app/services/md.py           — markdown→sanitized HTML (bleach)
app/blueprints/mail/         — routes: /f/<folder_key>, ?uid=, ?filter=
app/blueprints/api/          — AJAX: /api/mail/<uid>/*, /api/mail/bulk/*, /api/mail/send, /api/stream/<folder>
app/blueprints/rules/        — CRUD правил
app/blueprints/settings/     — подписи + автоответчик
app/blueprints/shared/       — общие ящики (user + admin)
app/blueprints/groups/       — группы рассылок
app/templates/layout/base.html       — sidebar + topbar + compose modal include
app/templates/mail/_compose_modal.html
app/templates/mail/folder.html       — 3-колонка: список треддов + просмотр письма
app/static/app.js            — весь JS (event delegation, chips, MD toolbar, send toast, SSE)
app/static/styles.css        — весь CSS
docker/docker-compose.yml    — postgres+redis+meili+mailserver
docker/dms-config/           — postfix-accounts.cf, postfix-main.cf (mydestination override)

Что реализовано (UI mock + реальный IMAP)

UI (Фаза 0b — done)

  • Sidebar: кнопка «Написать», системные + пользовательские папки, общие ящики, Организация, Админка; collapse 196↔44px, localStorage
  • Список писем: resizable (240800px), select-all, прочитать-все, фильтр (все/непрочитанные/с флажком/с вложениями), группировка в треды по нормализованной теме, bulk-toolbar
  • Просмотр письма: звёздочка, важное, ответить/переслать/спам/переместить/удалить
  • Compose modal: chips To/Cc/Bcc, выбор «От», MD-toolbar (B/I/S/H/quote/code/ul/ol/link/hr/preview), подписи с live-preview, важное, развернуть
  • Send toast: 10 сек таймер, синяя полоска снизу карточки, кнопка отмены — реальный POST только после таймера
  • IMAP IDLE + SSE: новые письма появляются без перезагрузки страницы

Инфраструктура (Фазы 13 — done)

  • DMS v14 запущен, IMAP/SMTP работают
  • Flask переключён на реальный IMAP (USE_MOCK_MAIL=0)
  • Реальные письма отображаются, отправка работает

Известные баги (следующие задачи)

  1. Sent пустой — smtp_sender.py не делает IMAP APPEND в папку Sent после отправки
  2. Черновики не сохраняются — при закрытии compose без отправки письмо теряется

Фазы (что впереди)

  • Фаза 4: IMAP APPEND в Sent + автосейв черновиков в IMAP Drafts
  • Фаза 5: Админка сервера (dms_config.py, домены/ящики/алиасы/DKIM)
  • Фаза 6: Alembic миграции + Sieve-правила + Meilisearch поиск
  • Фаза 7: Auth (Dovecot SASL логин, сессии в PG, app-passwords)
  • Фаза 8: Прод (ACME, открытые порты, NPM, бэкап, мониторинг)

Правила работы

  • Стиль UI строго по web_template: без Bootstrap/Tailwind, inline SVG, 0.5px borders
  • Деплой всегда через rsync на 10.101.10.223, Flask подхватывает изменения автоматически (debug mode)
  • Группы/правила/подписи/shared mailboxes пока в памяти (mock_mail.py), в Фазе 6 переедут в Postgres
  • IMAP = источник правды, Flask только читает/пишет через IMAPClient
  • Не добавлять alert() — использовать toast-уведомления