Files
reactive-mail-server/README.md
T

395 lines
15 KiB
Markdown

# 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 (рекомендуется)
```bash
cd mail-server
docker compose up --build
```
Сервисы:
- **REST API**: http://localhost:8080
- **SMTP (приём)**: localhost:3025
- **IMAP (доступ к письмам)**: localhost:143
- **Mailpit UI**: http://localhost:8025 (SMTP на порту 1025)
- **PostgreSQL**: localhost:5432
### 2. Локально
Требования:
- Java 21
- PostgreSQL 17
```bash
# Запустите 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`
### Регистрация
```bash
curl -X POST http://localhost:8080/api/register \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"secret123","displayName":"Test User"}'
```
### Логин
```bash
curl -X POST http://localhost:8080/api/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"secret123"}'
```
Ответ:
```json
{
"token": "eyJhbGci...",
"email": "test@example.com"
}
```
Все защищённые эндпоинты требуют заголовок `Authorization: Bearer <token>`.
### Получить папки
```bash
curl http://localhost:8080/api/folders \
-H "Authorization: Bearer <token>"
```
### Получить письма из папки
```bash
curl "http://localhost:8080/api/messages?folderId=1&page=0&size=20" \
-H "Authorization: Bearer <token>"
```
### Отправить письмо
```bash
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"}'
```
### Симулировать входящее письмо (для тестов)
```bash
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"
}'
```
### Отметить как прочитанное
```bash
curl -X PUT http://localhost:8080/api/messages/1/read \
-H "Authorization: Bearer <token>"
```
### Удалить письмо
```bash
curl -X DELETE http://localhost:8080/api/messages/1 \
-H "Authorization: Bearer <token>"
```
### Загрузить вложение к письму
```bash
curl -X POST http://localhost:8080/api/messages/1/attachments \
-H "Authorization: Bearer <token>" \
-F "file=@/path/to/file.pdf"
```
### Получить список вложений письма
```bash
curl http://localhost:8080/api/messages/1/attachments \
-H "Authorization: Bearer <token>"
```
### Скачать вложение
```bash
curl http://localhost:8080/api/attachments/1/download \
-H "Authorization: Bearer <token>" \
-o file.pdf
```
### Удалить вложение
```bash
curl -X DELETE http://localhost:8080/api/attachments/1 \
-H "Authorization: Bearer <token>"
```
### Поиск по письмам
Поиск поддерживает два режима: простой поиск по ключевому слову и расширенный поиск с отдельными фильтрами.
**Простой поиск** — ищет по ключевому слову в теме, теле, отправителе и получателе:
```bash
curl "http://localhost:8080/api/messages/search?folderId=1&keyword=hello" \
-H "Authorization: Bearer <token>"
```
**Расширенный поиск** — используйте отдельные параметры для точной фильтрации:
```bash
# Поиск по теме
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**. Любое письмо, отправленное на этот порт, будет сохранено в базе данных, если получатель зарегистрирован в системе.
```bash
# Отправить письмо через telnet
telnet localhost 3025
```
Или с помощью curl:
```bash
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`.
### Управление чёрным списком
Добавьте отправителя в чёрный список — письма от него будут автоматически помечаться как спам.
**Получить список заблокированных отправителей:**
```bash
curl http://localhost:8080/api/spam/blacklist \
-H "Authorization: Bearer <token>"
```
**Добавить отправителя в чёрный список:**
```bash
curl -X POST http://localhost:8080/api/spam/blacklist \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"senderEmail":"spammer@example.com","reason":"Спам"}'
```
**Удалить отправителя из чёрного списка:**
```bash
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 для тестирования.
```bash
# Подключение через 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
- [x] Хэширование паролей (BCrypt)
- [x] Аутентификация (JWT)
- [x] Приём SMTP (SubEtha SMTP встроенный сервер)
- [x] IMAP-сервер (встроенный, порт 143)
- [x] Вложения (загрузка/скачивание)
- [x] Поиск по письмам (по теме, телу, отправителю, получателю)
- [x] Angular фронтенд (v22 + Tailwind CSS + Synthwave дизайн)
- [x] Антиспам (чёрный список отправителей)
- [x] DKIM/SPF/DMARC (подпись, верификация, DNS-проверка)
- [ ] TLS шифрование
## Лицензия
MIT