API Документация¶
Обзор¶
RESTful API для системы управления библиотекой. Все эндпоинты требуют аутентификации через JWT токен, кроме /auth/login и /health.
Базовый URL¶
- Development:
http://localhost:3001/api(или/apiчерез Vite proxy на:8080) - Production:
https://your-domain.com/api
Аутентификация¶
Большинство эндпоинтов требуют JWT токен в заголовке Authorization:
Authorization: Bearer <token>
Токен получается через /auth/login и действителен в течение времени, указанного в JWT_EXPIRES_IN.
Формат ответов¶
Успешный ответ¶
{
"success": true,
"data": { ... },
"message": "Операция выполнена успешно"
}
Ошибка¶
{
"success": false,
"error": "Описание ошибки",
"code": "ERROR_CODE",
"details": { ... }
}
Коды статусов HTTP¶
200- Успешный запрос201- Ресурс создан400- Ошибка валидации401- Не авторизован403- Недостаточно прав404- Ресурс не найден409- Конфликт (например, книга уже выдана)422- Ошибка обработки данных500- Внутренняя ошибка сервера
Эндпоинты¶
Аутентификация¶
POST /auth/login¶
Вход в систему с поддержкой LDAP и локальной БД.
Тело запроса:
{
"login": "username",
"password": "password"
}
Ответ:
{
"success": true,
"data": {
"token": "jwt_token_here",
"user": {
"id": "uuid",
"login": "username",
"first_name": "Имя",
"last_name": "Фамилия",
"role": "admin|librarian|reader"
}
}
}
Книги¶
GET /books¶
Получить список книг с фильтрацией и пагинацией.
Параметры запроса:
- search (string, optional) - Поиск по названию, ISBN, описанию
- authorId (number, optional) - Фильтр по автору
- genreId (number, optional) - Фильтр по жанру
- publisherId (number, optional) - Фильтр по издательству
- page (number, default: 1) - Номер страницы
- limit (number, default: 20) - Количество записей
Ответ:
{
"success": true,
"data": [
{
"id": 1,
"title": "Название книги",
"isbn": "978-5-123456-78-9",
"description": "Описание",
"year": 2023,
"pages": 300,
"cover_image_path": "/uploads/book-covers/cover.jpg",
"book_authors": [...],
"book_genres": [...],
"publishers": {...},
"book_copies": [...]
}
]
}
GET /books/:id¶
Получить детальную информацию о книге.
Для авторизованного читателя в каждом элементе book_copies может присутствовать поле my_queue_position (number | null) — позиция в очереди на этот экземпляр без отдельных запросов к /bookings/queue/position/:bookCopyId.
POST /books¶
Создать новую книгу (требует роль admin или librarian).
Тело запроса (FormData):
- title (string, required)
- isbn (string, optional)
- description (string, optional)
- year (number, optional)
- pages (number, optional)
- publisher_id (number, optional)
- language_id (number, optional)
- cover (file, optional) - Изображение обложки
- authorIds[] (array of numbers) - ID авторов
- genreIds[] (array of numbers) - ID жанров
PUT /books/:id¶
Обновить информацию о книге (требует роль admin или librarian).
DELETE /books/:id¶
Удалить книгу (требует роль admin).
POST /books/import¶
Импорт книг из CSV (требует роль admin или librarian).
Тело запроса (multipart/form-data):
- file (file, required) — CSV файл в кодировке UTF-8. Первая строка — заголовки.
Колонки CSV (сопоставление по названию заголовка, порядок произвольный):
- Обязательные: Инвентарный номер, Название, Автор(ы) (несколько через ;)
- Опциональные: ISBN, Год издания, Страниц, Издание, Язык, Издательство, Жанры (;), Полка, Состояние, Статус (доступна/выдана), Цена покупки, Текущая стоимость, Описание
Справочники (авторы, издательства, жанры, полки, языки) создаются при отсутствии или берутся без дубликатов. Книга ищется по названию+ISBN+издательство; при отсутствии создаётся. Экземпляры с уже существующим инв. номером пропускаются.
Ответ (200):
{
"success": true,
"data": {
"success": true,
"imported": 10,
"skipped": 1,
"errors": [
{ "line": 5, "error": "Инвентарный номер 100 уже существует, строка пропущена" }
]
}
}
Экземпляры книг¶
GET /book-copies¶
Получить список экземпляров книг.
Параметры запроса:
- bookId (number, optional) - Фильтр по книге
- shelfId (number, optional) - Фильтр по полке
- isAvailable (boolean, optional) - Фильтр по доступности
- search (string, optional) - Поиск по инвентарному номеру
GET /book-copies/:id¶
Получить информацию об экземпляре.
POST /book-copies¶
Создать новый экземпляр (требует роль admin или librarian).
Тело запроса:
{
"book_title_id": 1,
"inventory_number": 1001,
"shelf_id": 1,
"condition_id": 1,
"purchase_price": 500,
"current_value": 450
}
PUT /book-copies/:id¶
Обновить экземпляр (требует роль admin или librarian).
DELETE /book-copies/:id¶
Удалить экземпляр (требует роль admin).
Операции¶
GET /operations¶
Получить историю операций с фильтрацией.
Параметры запроса:
- type (string, optional) - Тип операции: loan, return, writeoff, renew
- status (string, optional) - Статус: active, completed, overdue
- userId (string, optional) - Фильтр по пользователю
- bookCopyId (number, optional) - Фильтр по экземпляру
- dateFrom (date, optional) - Дата от
- dateTo (date, optional) - Дата до
- page (number, default: 1)
- limit (number, default: 50)
GET /operations/stats/active-loans¶
Получить статистику активных выдач.
POST /operations/loan¶
Создать операцию выдачи книги (требует роль admin или librarian).
Тело запроса:
{
"userId": "user_uuid",
"bookCopyId": 1,
"dueDate": "2024-12-31",
"notes": "Примечания",
"condition_id": 1
}
POST /operations/:id/return¶
Создать операцию возврата книги (требует роль admin или librarian).
Тело запроса:
{
"condition_id": 1,
"notes": "Примечания"
}
POST /operations/:id/renew¶
Продлить выдачу (требует роль admin или librarian).
Тело запроса:
{
"notes": "Примечания"
}
POST /operations/writeoff¶
Списать книгу (требует роль admin или librarian).
Тело запроса:
{
"bookCopyId": 1,
"reason": "Причина списания",
"notes": "Примечания"
}
Бронирования¶
GET /bookings¶
Получить список бронирований.
Параметры запроса:
- status (string, optional) - Статус: pending, fulfilled, cancelled, expired
- dateFrom (date, optional)
- dateTo (date, optional)
- limit (number, optional) - Только для читателей
- offset (number, optional) - Только для читателей
Примечание: Читатели видят только свои бронирования.
POST /bookings¶
Создать бронирование. Если экземпляр занят (выдача или чужая бронь), читатель автоматически добавляется в очередь.
Заголовки: Idempotency-Key (опционально) — защита от двойного создания.
Тело запроса:
{
"bookCopyId": 1,
"notes": "Примечания",
"expiresAt": "2024-12-31T23:59:59Z"
}
Ответ при постановке в очередь:
{
"success": true,
"data": {
"queued": true,
"position": 2,
"message": "Книга недоступна. Вы добавлены в очередь на позиции 2"
}
}
PUT /bookings/:id/cancel¶
Отменить бронирование. После коммита транзакции очередь на этот экземпляр продвигается синхронно (processQueueForBook); дополнительно создаётся outbox-событие как резерв на случай сбоя.
GET /bookings/copy/:bookCopyId/pending¶
Активное pending-бронирование на экземпляр (роли admin, librarian). Используется UI после возврата для опроса очереди.
GET /bookings/queue/my¶
Список очередей текущего пользователя (позиция, книга, инв. номер).
POST /bookings/queue¶
Явно встать в очередь (если экземпляр недоступен).
Заголовки: Idempotency-Key (опционально) — защита от двойной постановки в очередь (команда AddToQueue).
Тело запроса:
{
"book_copy_id": 1
}
Перед постановкой в очередь и при продвижении очереди просроченные pending-брони на экземпляр отменяются автоматически (lazy expiration), без ожидания cron.
DELETE /bookings/queue/:bookCopyId¶
Выйти из очереди на экземпляр. Позиции оставшихся в очереди пересчитываются атомарно (блокировка строки book_copies).
GET /bookings/queue/position/:bookCopyId¶
Позиция текущего пользователя в очереди (null, если не в очереди).
GET /bookings/queue/book/:bookCopyId¶
Очередь на экземпляр (роли admin, librarian).
GET /bookings/queue/stats¶
Сводка: { "waiting_count": N } (роли admin, librarian).
POST /bookings/cleanup-expired¶
Отменить все просроченные бронирования и создать outbox-события (роли admin, librarian).
Администрирование (фрагмент)¶
GET /admin/backups¶
Список файлов резервных копий БД (роль admin, BACKUP_ENABLED=true).
GET /admin/backups/:filename/download¶
Скачать дамп.
POST /admin/backups/restore¶
Восстановить БД из загруженного .sql / .gz (роль admin).
Пользователи¶
GET /users¶
Получить список пользователей (требует роль admin или librarian).
Параметры запроса:
- search (string, optional) - Поиск по ФИО, логину, email
- role (string, optional) - Фильтр по роли
- categoryId (number, optional) - Фильтр по категории
- status (string, optional) - Фильтр по статусу: active, inactive
- dateFrom (date, optional)
- dateTo (date, optional)
GET /users/:id¶
Получить информацию о пользователе.
POST /users¶
Создать пользователя (требует роль admin).
Тело запроса:
{
"login": "username",
"password": "password123",
"firstName": "Имя",
"lastName": "Фамилия",
"middleName": "Отчество",
"email": "user@example.com",
"role": "reader",
"categoryId": 1
}
PUT /users/:id¶
Обновить пользователя (требует роль admin).
DELETE /users/:id¶
Удалить пользователя (требует роль admin).
PUT /users/:id/deactivate¶
Деактивировать пользователя (требует роль admin).
PUT /users/:id/activate¶
Активировать пользователя (требует роль admin).
Справочники¶
GET /authors¶
Получить список авторов.
POST /authors¶
Создать автора (требует роль admin или librarian).
GET /genres¶
Получить список жанров.
GET /publishers¶
Получить список издательств.
GET /shelves¶
Получить список полок.
GET /conditions¶
Получить список состояний книг.
GET /languages¶
Получить список языков.
GET /user-categories¶
Получить список категорий пользователей.
QR-коды¶
POST /qr/scan¶
Обработать отсканированный QR-код.
Тело запроса:
{
"qrData": "v1:copy:1001"
}
Ответ:
{
"success": true,
"data": {
"type": "book_copy",
"bookCopy": {
"id": 1,
"inventory_number": 1001,
"is_available": true,
"book_titles": {
"title": "Название книги",
"book_authors": [...]
}
},
"actions": ["loan", "return"]
}
}
GET /qr/user/:id¶
Получить QR-код пользователя.
GET /qr/book-copy/:id¶
Получить QR-код экземпляра книги.
Статистика¶
GET /users/stats/total¶
Получить общую статистику по пользователям (требует роль admin или librarian).
GET /operations/stats/active-loans¶
Получить статистику активных выдач.
GET /operations/stats/total-loans¶
Получить статистику всех выдач.
Обработка ошибок¶
Типичные ошибки¶
400 Bad Request¶
{
"success": false,
"error": "Ошибка валидации",
"code": "VALIDATION_ERROR",
"details": [
{
"field": "bookCopyId",
"message": "ID экземпляра обязателен"
}
]
}
401 Unauthorized¶
{
"success": false,
"error": "Не авторизован",
"code": "UNAUTHORIZED"
}
403 Forbidden¶
{
"success": false,
"error": "Недостаточно прав",
"code": "FORBIDDEN"
}
409 Conflict¶
{
"success": false,
"error": "Книга уже выдана другому читателю",
"code": "BOOK_ALREADY_LOANED"
}
Rate Limiting¶
API использует rate limiting для предотвращения злоупотреблений: - По умолчанию: 100 запросов в минуту на IP - Аутентификация: 5 попыток входа в минуту на IP
CSRF Protection¶
Для изменяющих запросов (POST, PUT, DELETE) требуется CSRF токен в заголовке:
X-CSRF-Token: <csrf_token>
Токен получается автоматически при первом запросе и сохраняется в cookies.
Версионирование API¶
API использует версионирование через префикс /v1 (опционально). Текущая версия: v1.
Swagger документация¶
Интерактивная документация доступна по адресу:
- Development: http://localhost:3001/api-docs
- Production: https://your-domain.com/api-docs