docs: Added Api documentation

AI GENERATED
This commit is contained in:
Black_Raven committed 2026-10-23 21:22:18 +03:00
1 parent 1a7a78971b
commit ac6d49bcda
1 file changed
+459
+459
View File
@@ -0,0 +1,459 @@
# REST API: система автоматизированной email-рассылки
## 1. Базовые параметры
- Base URL: `https://api.example.com/api/v1`
- Протокол: HTTPS
- Формат: JSON
- Авторизация: `Authorization: Bearer <access_token>`
- Дата и время: ISO 8601, UTC
### Бизнес-правила
1. Одна компания — один аккаунт и один адрес отправителя, заданный при регистрации.
2. У компании может быть только одна незавершённая задача (`draft`, `queued`, `running`, `paused`).
3. Новая задача разрешена после `completed`, `failed` или `cancelled`.
4. Каждая задача принадлежит авторизованной компании; сервер проверяет доступ к каждому ресурсу.
5. Каждому получателю отправляется каждое письмо из списка по порядку. Количество отправок = количество получателей × количество писем.
6. Отправка выполняется фоновым обработчиком.
7. Нужно исключать отписавшихся адресатов и учитывать согласие на рассылку.
## 2. Архитектура
```mermaid
flowchart TD
A[Клиент] <-->|HTTPS / REST / JSON| B[Backend API]
B <--> C[(База данных)]
B --> D[Очередь задач]
D --> E[Фоновый обработчик]
E --> F[SMTP / почтовый провайдер]
F -. События доставки .-> B
```
## 3. Общие ошибки
```json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Проверьте переданные данные.",
"details": {
"interval_seconds": "Значение должно быть больше нуля."
}
}
}
```
| HTTP-код | Значение |
|---|---|
| `200` | Успешный запрос |
| `201` | Ресурс создан |
| `202` | Запрос принят в обработку |
| `400` | Некорректный запрос |
| `401` | Требуется авторизация |
| `403` | Нет доступа |
| `404` | Ресурс не найден |
| `409` | Конфликт состояния |
| `422` | Ошибка бизнес-валидации |
| `429` | Превышен лимит запросов |
| `500` | Внутренняя ошибка сервера |
## 4. Регистрация и авторизация
### `POST /auth/register` — регистрация компании
**Request**
```json
{
"company_name": "ООО Пример",
"email": "mail@example.com",
"password": "strong-account-password"
}
```
**Response `201`**
```json
{
"user": {
"id": "usr_123",
"company_name": "ООО Пример",
"email": "mail@example.com",
"sender_email": "mail@example.com"
},
"access_token": "eyJhbGciOi...",
"token_type": "Bearer"
}
```
### `POST /auth/login` — вход
**Request**
```json
{
"email": "mail@example.com",
"password": "strong-account-password"
}
```
**Response `200`**
```json
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer"
}
```
### `GET /me` — данные компании
**Response `200`**
```json
{
"id": "usr_123",
"company_name": "ООО Пример",
"email": "mail@example.com",
"sender_email": "mail@example.com",
"smtp_configured": true
}
```
## 5. SMTP-настройки
### `PUT /me/smtp-settings` — настроить почтовое подключение
**Request**
```json
{
"host": "smtp.example.com",
"port": 587,
"encryption": "STARTTLS",
"username": "mail@example.com",
"password": "mail-provider-app-password"
}
```
**Response `200`**
```json
{
"status": "configured",
"sender_email": "mail@example.com"
}
```
`sender_email` задаётся при регистрации и не передаётся в настройках SMTP. SMTP-пароль не возвращается клиенту и хранится на сервере в защищённом виде. Пароль аккаунта приложения и SMTP-пароль — разные секреты.
## 6. Задачи рассылки
### `POST /campaigns` — создать задачу
**Request**
```json
{
"name": "Осенняя рассылка",
"interval_seconds": 60
}
```
**Response `201`**
```json
{
"id": "cmp_456",
"name": "Осенняя рассылка",
"status": "draft",
"interval_seconds": 60,
"recipients_count": 0,
"messages_count": 0
}
```
Если уже существует незавершённая задача — `409 Conflict`:
```json
{
"error": {
"code": "ACTIVE_CAMPAIGN_EXISTS",
"message": "У компании уже есть незавершённая задача рассылки."
}
}
```
Ограничение должно обеспечиваться на сервере с учётом параллельных запросов.
### `GET /campaigns/current` — текущая незавершённая задача
**Response `200`**
```json
{
"id": "cmp_456",
"name": "Осенняя рассылка",
"status": "draft",
"interval_seconds": 60,
"recipients_count": 2,
"messages_count": 1
}
```
Если активной задачи нет — `404 Not Found`.
### `PATCH /campaigns/{campaign_id}` — изменить параметры
**Request**
```json
{
"interval_seconds": 120
}
```
**Response `200`**
```json
{
"id": "cmp_456",
"interval_seconds": 120,
"status": "draft"
}
```
Изменение параметров разрешено в состоянии `draft`.
## 7. Получатели
### `PUT /campaigns/{campaign_id}/recipients` — заменить список получателей
**Request**
```json
{
"recipients": [
{ "email": "client1@example.com", "name": "Иван" },
{ "email": "client2@example.com", "name": "Анна" }
]
}
```
**Response `200`**
```json
{
"campaign_id": "cmp_456",
"total": 2,
"added": 2,
"invalid": 0,
"duplicates": 0
}
```
Проверять формат адресов, устранять дубликаты и исключать адреса, отписавшиеся от рассылки.
### `POST /campaigns/{campaign_id}/recipients/import` — импорт CSV (опционально)
## 8. Письма
### `PUT /campaigns/{campaign_id}/messages` — заменить список писем
**Request**
```json
{
"messages": [
{
"subject": "Предложение для вашей компании",
"body_text": "Здравствуйте! Предлагаем ознакомиться с нашими услугами."
},
{
"subject": "Дополнительная информация",
"body_text": "Здравствуйте! Отправляем дополнительную информацию."
}
]
}
```
**Response `200`**
```json
{
"campaign_id": "cmp_456",
"messages_count": 2,
"status": "draft"
}
```
`PUT` заменяет весь список. `body_text` — текст письма; поле `body_html` можно добавить при необходимости.
## 9. Управление рассылкой
### `POST /campaigns/{campaign_id}/start` — запуск
**Response `202`**
```json
{
"campaign_id": "cmp_456",
"status": "queued",
"total_emails": 4
}
```
Перед запуском проверить SMTP, право использовать адрес отправителя, наличие получателей и писем, корректность параметров и отсутствие другой незавершённой задачи. Запуск создаёт задания в очереди; отправка выполняется фоновым обработчиком.
### `POST /campaigns/{campaign_id}/pause` — пауза
**Response**
```json
{
"campaign_id": "cmp_456",
"status": "paused"
}
```
### `POST /campaigns/{campaign_id}/resume` — продолжить
**Response**
```json
{
"campaign_id": "cmp_456",
"status": "running"
}
```
### `POST /campaigns/{campaign_id}/cancel` — отменить
**Response**
```json
{
"campaign_id": "cmp_456",
"status": "cancelled"
}
```
Отмена останавливает оставшуюся очередь; уже отправленные письма отозвать нельзя. Сервер проверяет допустимость перехода между состояниями.
## 10. Статус и прогресс
### `GET /campaigns/{campaign_id}/status`
**Response `200`**
```json
{
"campaign_id": "cmp_456",
"status": "running",
"progress": {
"total": 200,
"processed": 75,
"queued": 125,
"accepted": 72,
"failed": 3
},
"started_at": "2026-10-23T18:00:00Z"
}
```
- `total` — всего запланировано отправок.
- `processed` — отправки, для которых зафиксирован результат обработки.
- `queued` — отправки в очереди.
- `accepted` — письма приняты почтовой стороной к дальнейшей обработке.
- `failed` — отправки, завершившиеся ошибкой на уровне обработки.
Клиент может опрашивать endpoint с интервалом 3–5 секунд. Повторные попытки не должны повторно увеличивать счётчики одной отправки.
## 11. Отчёт и отдельные отправки
### `GET /campaigns/{campaign_id}/report` — итоговый отчёт
**Response `200`**
```json
{
"campaign_id": "cmp_456",
"status": "completed",
"summary": {
"total": 200,
"accepted": 198,
"delivered": 190,
"bounced": 8,
"spam_complaints": 2,
"unknown": 0
},
"completed_at": "2026-10-23T22:00:00Z"
}
```
| Поле | Значение |
|---|---|
| `total` | Запланировано отправок |
| `accepted` | Почтовый сервис принял письмо |
| `delivered` | Получено подтверждение приёма сервером получателя |
| `bounced` | Письмо возвращено или отклонено |
| `spam_complaints` | Зарегистрированные жалобы на спам |
| `unknown` | Итоговый статус неизвестен |
Для `delivered`, `bounced` и `spam_complaints` нужны события или вебхуки почтового провайдера. SMTP-подтверждение отправки не доказывает попадание письма во «Входящие»; жалобы на спам не равны числу писем, помещённых фильтрами в папку «Спам».
### `GET /campaigns/{campaign_id}/deliveries?status=bounced&limit=50` — отдельные отправки
**Response `200`**
```json
{
"campaign_id": "cmp_456",
"items": [
{
"recipient_email": "client1@example.com",
"message_subject": "Предложение для вашей компании",
"status": "bounced",
"reason": "Mailbox unavailable",
"attempted_at": "2026-10-23T18:10:00Z"
}
],
"limit": 50,
"next_cursor": null
}
```
## 12. Состояния задачи
| Состояние | Значение | Разрешить новую задачу |
|---|---|---|
| `draft` | Настройка | Нет |
| `queued` | В очереди | Нет |
| `running` | Выполняется | Нет |
| `paused` | Приостановлена | Нет |
| `completed` | Завершена | Да |
| `failed` | Ошибка выполнения | Да |
| `cancelled` | Отменена | Да |
## 13. Сводный список endpoint-ов
| Метод | Endpoint | Назначение |
|---|---|---|
| `POST` | `/auth/register` | Регистрация |
| `POST` | `/auth/login` | Вход |
| `GET` | `/me` | Данные компании |
| `PUT` | `/me/smtp-settings` | Настройки SMTP |
| `POST` | `/campaigns` | Создать задачу |
| `GET` | `/campaigns/current` | Текущая незавершённая задача |
| `PATCH` | `/campaigns/{campaign_id}` | Изменить параметры задачи |
| `PUT` | `/campaigns/{campaign_id}/recipients` | Заменить список получателей |
| `POST` | `/campaigns/{campaign_id}/recipients/import` | Импорт CSV, опционально |
| `PUT` | `/campaigns/{campaign_id}/messages` | Заменить список писем |
| `POST` | `/campaigns/{campaign_id}/start` | Запустить рассылку |
| `POST` | `/campaigns/{campaign_id}/pause` | Приостановить |
| `POST` | `/campaigns/{campaign_id}/resume` | Продолжить |
| `POST` | `/campaigns/{campaign_id}/cancel` | Отменить |
| `GET` | `/campaigns/{campaign_id}/status` | Статус и прогресс |
| `GET` | `/campaigns/{campaign_id}/report` | Итоговый отчёт |
| `GET` | `/campaigns/{campaign_id}/deliveries` | Отдельные отправки |