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.