Files
reactive-mail-server/README.md
T

15 KiB
Raw Blame History

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, телефон, компания

TODO

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

Лицензия

MIT