BOOK-LIBRARY: архитектура

BOOK-LIBRARY сделан как классическое клиент-серверное приложение. Frontend живёт отдельно в frontend/, backend — в backend/, а документация проекта вынесена в docs/. Такая структура проста, но для библиотечного каталога её достаточно: интерфейс работает с API, API хранит метаданные и управляет файлами.

Frontend

Frontend собран на SvelteKit, Svelte 5, TypeScript и Vite. По фактическим маршрутам видно несколько пользовательских зон:

  • / — главная страница с категориями;
  • /categories/[id] — список книг внутри категории;
  • /admin/login — вход в админку;
  • /admin — админский дашборд с категориями и пакетной загрузкой;
  • /admin/add-book — форма добавления одной книги;
  • /admin/categories/[id] — управление книгами внутри категории.

Публичные страницы используют обычные fetch-запросы к API. Админские страницы передают credentials: 'include', потому что авторизация держится на session cookie.

Интерфейс довольно прямой: нет отдельного глобального store для каталога, сложной клиентской синхронизации или офлайн-режима. Это соответствует задаче проекта: показать каталог, дать скачать книгу и дать администратору управлять содержимым.

Backend

Backend работает на Node.js и Express. Точка входа — backend/server.js. Там подключаются:

  • helmet для базовых HTTP-заголовков;
  • cors с набором локальных разрешённых origins;
  • morgan для логирования запросов;
  • express-session для админской авторизации;
  • статическая раздача обложек из /uploads/covers;
  • legacy-роуты, public API, download-роуты и admin API.

Маршруты разделены по смыслу:

  • backend/routes/public.js отдаёт категории, книги категории и создаёт download-токен;
  • backend/routes/admin.js принимает логин, создание категорий, загрузку книг, batch upload, редактирование и удаление;
  • backend/routes/download.js валидирует временный токен и отдаёт файл;
  • backend/routes/legacy.js обслуживает HTML-версию на EJS.

Такое разделение удобно читать: публичные действия, админские действия и скачивание не смешаны в одном файле.

Данные и файлы

Метаданные хранятся в SQLite. База создаётся в backend/uploads/library.db, а таблицы инициализируются при старте в backend/db/database.js.

Схема сейчас компактная:

  • categories: название, описание и emoji;
  • books: название, автор, описание, category_id, расширение файла и дата создания;
  • temp_tokens: временный token, book_id, expires_at и created_at.

Сами книги лежат в backend/uploads/books и называются по id записи: например 42.fb2 или 106.pdf. Обложки лежат в backend/uploads/covers и приводятся к имени <id>.webp. Это простой и понятный вариант для локальной библиотеки: база отвечает за карточку, файловая система — за сами тяжёлые файлы.

Загрузка и обработка файлов

Для одиночной загрузки используется Multer. Разрешены форматы книг .pdf, .epub, .mobi, .fb2, .djvu, а для обложек — .jpg, .jpeg, .png, .webp. Размер ограничен 500 МБ.

Обложки проходят через Sharp в processCover: проект сохраняет их как WebP, чтобы карточки каталога получали единый формат изображения.

Для ZIP-импорта есть отдельный upload middleware и batchController. Он распаковывает архив, ищет books.json или сам собирает список файлов с разрешёнными расширениями, затем обрабатывает каждый файл и пишет прогресс как NDJSON.

Временные ссылки на скачивание

Вместо прямой ссылки на файл frontend сначала просит backend создать временный токен. Логика находится в backend/utils/tempTokens.js: token генерируется через crypto.randomBytes, сохраняется в SQLite и живёт 5 минут.

Периодическая очистка запускается в backend/server.js через setInterval(() => cleanupExpiredTokens(), 60 * 1000).unref(). Это небольшой, но полезный элемент архитектуры: устаревшие токены не должны бесконечно копиться в базе.

Legacy-интерфейс

Кроме SvelteKit-интерфейса, в backend есть views/legacy и маршрут /legacy. Это простая EJS-версия каталога. Она может быть полезна для старых устройств или ситуаций, где нужен более простой HTML без frontend-сборки.

Для портфолио это хорошая деталь: проект не ограничивается одной SPA-страницей, а держит альтернативный способ доступа к каталогу.

Ограничения текущей архитектуры

Сейчас проект выглядит как локальная или небольшая частная библиотека. Это важно честно зафиксировать:

  • SQLite и файловое хранилище подходят для простого развёртывания, но требуют аккуратного бэкапа;
  • миграции схемы отдельно не оформлены;
  • часть security-документации описывает риски и рекомендации, а не полностью закрытое production-состояние;
  • CORS настроен под локальные адреса;
  • роли пользователей не разделены: есть админ и публичный пользователь.

Эти пункты не делают проект слабым. Они показывают нормальную стадию развития: рабочий каталог уже есть, а production-усиления можно выносить в roadmap.