Перейти к содержанию

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