Liosha84andJunie b6f6d41813 Синхронизация моделей, Docker настройки и исправление Angular шаблонов
- V1__init.sql: синхронизированы таблицы с Java моделями (users, folders, messages, contacts)
- docker-compose.yml: обновлён контекст сборки, переменные R2DBC/Flyway, порты (5433, 3026, 1443)
- backend/Dockerfile: обновлены пути COPY для нового контекста сборки
- settings.gradle: удалён frontend проект из Gradle
- contacts.component.html: исправлен ternary оператор с HTML на Angular @if/@else
- Фронтенд компоненты: обновления compose, inbox, layout, login, message-detail, register
- index.html, styles.css: обновления стилей и мета-данных

Co-authored-by: Junie <junie@jetbrains.com>
2026-11-05 03:08:36 +01:00

Mail Server

Прототип почтового сервера на Spring Boot + Angular с REST API для управления письмами.

Архитектура

┌─────────────────────────────────────────────────────┐
│                    Angular Frontend                  │
│  (веб-почта: список, чтение, написание, контакты)    │
└──────────────────────┬──────────────────────────────┘
                       │ REST API (JSON)
┌──────────────────────▼──────────────────────────────┐
│                  Spring Boot Backend                  │
│  ┌────────────┐  ┌────────────┐  ┌──────────────┐  │
│  │  Users     │  │  Messages  │  │  Contacts    │  │
│  └────────────┘  └────────────┘  └──────────────┘  │
│  ┌────────────┐  ┌────────────┐                      │
│  │  Folders   │  │Attachments │                      │
│  └────────────┘  └────────────┘                      │
│                       │                               │
│              ┌────────▼────────┐                      │
│              │  JavaMailSender │                      │
│              │  (SMTP отправка)│                      │
│              └─────────────────┘                      │
└──────────────────────┬──────────────────────────────┘
                       │ JPA
┌──────────────────────▼──────────────────────────────┐
│                  PostgreSQL 17                        │
└─────────────────────────────────────────────────────┘

Запуск

1. Docker Compose (рекомендуется)

cd mail-server
docker compose up --build

Сервисы:

2. Локально

Требования:

  • Java 21
  • PostgreSQL 17
# Запустите PostgreSQL
docker run -d --name mail-db -p 5432:5432 \
  -e POSTGRES_DB=mailserver \
  -e POSTGRES_USER=mail \
  -e POSTGRES_PASSWORD=secret \
  postgres:17

# Запустите Mailpit для тестовой отправки
docker run -d --name mailpit -p 1025:1025 -p 8025:8025 axllent/mailpit

# Соберите и запустите бэкенд
cd backend
./gradlew bootRun

API

Тестовый пользователь

При первом запуске приложения автоматически создаётся тестовый пользователь:

  • Email: admin@mailserver.local
  • Пароль: admin123

Регистрация

curl -X POST http://localhost:8080/api/register \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"secret123","displayName":"Test User"}'

Логин

curl -X POST http://localhost:8080/api/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"secret123"}'

Ответ:

{
  "token": "eyJhbGci...",
  "email": "test@example.com"
}

Все защищённые эндпоинты требуют заголовок Authorization: Bearer <token>.

Получить папки

curl http://localhost:8080/api/folders \
  -H "Authorization: Bearer <token>"

Получить письма из папки

curl "http://localhost:8080/api/messages?folderId=1&page=0&size=20" \
  -H "Authorization: Bearer <token>"

Отправить письмо

curl -X POST http://localhost:8080/api/send \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{"to":"recipient@example.com","subject":"Hello","body":"Test message"}'

Симулировать входящее письмо (для тестов)

curl -X POST http://localhost:8080/api/simulate/receive \
  -H "Content-Type: application/json" \
  -d '{
    "senderEmail":"sender@example.com",
    "senderName":"Sender",
    "recipientEmail":"test@example.com",
    "subject":"Incoming test",
    "body":"This is a test message"
  }'

Отметить как прочитанное

curl -X PUT http://localhost:8080/api/messages/1/read \
  -H "Authorization: Bearer <token>"

Удалить письмо

curl -X DELETE http://localhost:8080/api/messages/1 \
  -H "Authorization: Bearer <token>"

Загрузить вложение к письму

curl -X POST http://localhost:8080/api/messages/1/attachments \
  -H "Authorization: Bearer <token>" \
  -F "file=@/path/to/file.pdf"

Получить список вложений письма

curl http://localhost:8080/api/messages/1/attachments \
  -H "Authorization: Bearer <token>"

Скачать вложение

curl http://localhost:8080/api/attachments/1/download \
  -H "Authorization: Bearer <token>" \
  -o file.pdf

Удалить вложение

curl -X DELETE http://localhost:8080/api/attachments/1 \
  -H "Authorization: Bearer <token>"

Поиск по письмам

Поиск поддерживает два режима: простой поиск по ключевому слову и расширенный поиск с отдельными фильтрами.

Простой поиск — ищет по ключевому слову в теме, теле, отправителе и получателе:

curl "http://localhost:8080/api/messages/search?folderId=1&keyword=hello" \
  -H "Authorization: Bearer <token>"

Расширенный поиск — используйте отдельные параметры для точной фильтрации:

# Поиск по теме
curl "http://localhost:8080/api/messages/search?folderId=1&subject=report" \
  -H "Authorization: Bearer <token>"

# Поиск по тексту письма
curl "http://localhost:8080/api/messages/search?folderId=1&body=invoice" \
  -H "Authorization: Bearer <token>"

# Поиск по отправителю
curl "http://localhost:8080/api/messages/search?folderId=1&sender=company.com" \
  -H "Authorization: Bearer <token>"

# Комбинированный поиск (все фильтры применяются одновременно)
curl "http://localhost:8080/api/messages/search?folderId=1&subject=report&sender=company.com" \
  -H "Authorization: Bearer <token>"

Все параметры поиска (keyword, subject, body, sender, recipient) необязательны. Если ни один параметр не указан, возвращаются все письма папки.

Отправить письмо через SMTP-сервер

Встроенный SMTP-сервер слушает порт 3025. Любое письмо, отправленное на этот порт, будет сохранено в базе данных, если получатель зарегистрирован в системе.

# Отправить письмо через telnet
telnet localhost 3025

Или с помощью curl:

curl --url 'smtp://localhost:3025' \
  --mail-from 'sender@example.com' \
  --mail-rcpt 'test@example.com' \
  -T <(echo -e 'From: sender@example.com\nTo: test@example.com\nSubject: SMTP Test\n\nHello from SMTP!')

Письмо появится во входящих (INBOX) пользователя test@example.com.

Управление чёрным списком

Добавьте отправителя в чёрный список — письма от него будут автоматически помечаться как спам.

Получить список заблокированных отправителей:

curl http://localhost:8080/api/spam/blacklist \
  -H "Authorization: Bearer <token>"

Добавить отправителя в чёрный список:

curl -X POST http://localhost:8080/api/spam/blacklist \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{"senderEmail":"spammer@example.com","reason":"Спам"}'

Удалить отправителя из чёрного списка:

curl -X DELETE "http://localhost:8080/api/spam/blacklist?senderEmail=spammer@example.com" \
  -H "Authorization: Bearer <token>"

Письма от заблокированных отправителей автоматически получают флаг spam: true и могут быть отфильтрованы на стороне клиента.

Подключение через IMAP-сервер

Встроенный IMAP-сервер слушает порт 143. Вы можете подключиться к нему из любого почтового клиента (Thunderbird, Outlook, Apple Mail) или через telnet для тестирования.

# Подключение через telnet
telnet localhost 143

Пример сессии IMAP:

a LOGIN test@example.com secret123
a OK LOGIN completed

a LIST "" "*"
* LIST (\HasNoChildren) "." "INBOX"
* LIST (\HasNoChildren) "." "Sent"
a OK LIST completed

a SELECT "INBOX"
* 5 EXISTS
* 0 RECENT
* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
a OK [READ-WRITE] SELECT completed

a FETCH 1 (BODY[])
* 1 FETCH (UID 1 FLAGS (\Seen) BODY[] {123
Hello, this is a test message.
))
a OK FETCH completed

a SEARCH ALL
* SEARCH 1 2 3 4 5
a OK SEARCH completed

a LOGOUT
* BYE IMAP4rev1 server terminating
a OK LOGOUT completed

Поддерживаемые IMAP-команды:

  • LOGIN — аутентификация по email и паролю
  • SELECT / EXAMINE — выбор папки для работы
  • FETCH — получение заголовков и тела писем
  • SEARCH — поиск по письмам
  • STORE — изменение флагов (прочитано, помечено, удалено)
  • EXPUNGE — удаление помеченных писем
  • LIST / LSUB — список папок
  • STATUS — информация о папке (количество писем, непрочитанных)
  • CAPABILITY — список поддерживаемых возможностей
  • LOGOUT — выход из сессии

Порт IMAP настраивается через mailserver.imap.port в application.properties (по умолчанию 143).

DKIM / SPF / DMARC — защита от подделки писем

Почтовый сервер поддерживает три стандарта защиты электронной почты:

DKIM (DomainKeys Identified Mail) — криптографическая подпись писем.

  • При отправке письма автоматически подписываются RSA-ключом (2048 бит)
  • При получении входящих писем проверяется DKIM-подпись отправителя
  • Ключи хранятся в директории ./dkim-keys/ (генерируются автоматически при первом запуске)
  • Публичный ключ публикуется в DNS TXT-записи: _default._domainkey.<domain>

SPF (Sender Policy Framework) — проверка разрешённых серверов отправителя.

  • При получении письма проверяется SPF DNS-запись домена отправителя
  • Поддерживаемые механизмы: all, ip4, ip6, mx, a, include
  • Локальные адреса (127.0.0.1, 192.168.x.x) пропускаются без проверки

DMARC (Domain-based Message Authentication, Reporting & Conformance) — политика обработки.

  • Объединяет результаты SPF и DKIM с проверкой alignment (совпадение доменов)
  • Поддерживаемые политики: none (мониторинг), quarantine (спам), reject (отклонить)
  • Если SPF и DKIM не пройдены, применяется политика DMARC из DNS

Результат проверки:

  • Письма, не прошедшие SPF/DKIM/DMARC, автоматически помечаются флагом spam: true
  • Логируются результаты всех трёх проверок для каждого входящего письма

Настройки в application.properties:

mailserver.dkim.enabled=true
mailserver.dkim.domain=localhost
mailserver.dkim.keys-dir=./dkim-keys
mailserver.spf.enabled=true
mailserver.spf.local-bypass=true
mailserver.dmarc.enabled=true
mailserver.dmarc.local-bypass=true

Публикация DKIM-ключа в DNS: При первом запуске сервер выводит в лог публичный ключ DKIM. Добавьте его как TXT-запись:

_name: _default._domainkey.yourdomain.com
type: TXT
value: v=DKIM1; k=rsa; p=MIIBojANBgkqhkiG9w0BAQ...

Модели данных

Сущность Описание
User Почтовый ящик, пароль, квота
Folder INBOX, Sent, Drafts, Trash, Spam
Message Заголовки, тело, флаги (read, flagged, spam)
BlacklistEntry Заблокированный отправитель, привязка к пользователю
Attachment Имя, MIME-тип, размер, путь к файлу
Contact Имя, email, телефон, компания

Деплой на VPS

Caddy Docker Proxy

Проект использует Caddy Docker Proxy для автоматического HTTPS и reverse proxy. Caddy сам генерирует конфиг из labels в docker-compose.prod.yml — отдельный Caddyfile не нужен.

Как это работает:

  • Контейнер mail-server имеет labels с настройками Caddy
  • lucaslorentz/caddy-docker-proxy следит за Docker через socket и автоматически создаёт виртуальные хосты
  • HTTPS-сертификат от Let's Encrypt получается автоматически

Ключевые labels:

Label Описание
caddy: mail.jambotron.com Домен, на котором доступен сервис
caddy.route.api.reverse_proxy Проксирует /api/* на Spring Boot (порт 8080)
caddy.route.static.root Корневая директория для Angular фронтенда
caddy.route.static.file_server Раздаёт статические файлы Angular

Запуск на VPS:

# Клонируйте репозиторий
git clone https://gitea.jambotron.com/jambotron_gang/reactive-mail-server.git
cd reactive-mail-server

# Соберите фронтенд
cd frontend && npm install && npx ng build --configuration=production
cd ..

# Запустите
docker compose -f docker-compose.prod.yml up -d --build

Перед запуском:

  1. Замените mail.jambotron.com на ваш домен в docker-compose.prod.yml
  2. Настройте PTR (Reverse DNS) у хостера — IP → mail.jambotron.com
  3. Добавьте DNS-записи: A (mail → IP), MX (@ → mail.jambotron.com), TXT (v=spf1 ip4:<IP> -all)
  4. Откройте порты: 80, 443, 25 (SMTP relay), 3025 (SMTP приём)

TODO

  • Хэширование паролей (BCrypt)
  • Аутентификация (JWT)
  • Приём SMTP (встроенный сервер на WebFlux TcpServer)
  • IMAP-сервер (встроенный, порт 143)
  • Вложения (загрузка/скачивание)
  • Поиск по письмам (по теме, телу, отправителю, получателю)
  • Angular фронтенд (v22 + Tailwind CSS + Synthwave дизайн)
  • Антиспам (чёрный список отправителей)
  • DKIM/SPF/DMARC (подпись, верификация, DNS-проверка)
  • SMTP релей (доставка на внешнюю почту)
  • Контакты (адресная книга)
  • Reply / Forward
  • Пагинация писем
  • HTML/Markdown режим
  • Caddy Docker Proxy (автоматический HTTPS)
  • TLS шифрование SMTP/IMAP

Лицензия

MIT

S
Description
No description provided
Readme
820 KiB
Languages
Java 70.9%
TypeScript 19.2%
HTML 7.7%
CSS 1.4%
PLpgSQL 0.3%
Other 0.5%