У многих дома лежит папка с сотнями книг в FB2 — и почти ни одного удобного способа читать их где угодно. Десктопные читалки не синхронизируют прогресс, онлайн-сервисы требуют подписку и не принимают «свои» файлы, а связка Calibre с облаком — конструктор для энтузиаста. Рассказываем, как эту проблему решили браузером, PostgreSQL и открытым кодом — и почему в итоге всё свелось к скучному и предсказуемому стеку.

Но обо всём по порядку.


Боль, знакомая каждому владельцу FB2-коллекции

Формат FB2 живее всех живых: его поддерживают библиотеки, самиздат и привычные читалки. И с ним регулярно возникает одна и та же история.

Сценарий первый: домашняя коллекция в FB2, читалка — десктопная программа. Всё хорошо, пока книга не понадобилась в метро. Прогресс чтения на телефоне и ноутбуке живёт отдельными жизнями.

Сценарий второй: сервисы-читалки с подпиской. Комфортно, синхронно — но только со «своими» книгами из их магазина. Загрузить личный FB2-файл часто нельзя вовсе, а если можно — файл остаётся в стенах сервиса. Подписка кончилась — библиотека под замком. Всё это следствие DRM (Digital Rights Management, системы управления цифровыми правами), которая решает, где и как долго вы владеете купленной книгой.

Сценарий третий: Calibre + облачная синхронизация папки. Мощно, но это конструктор для энтузиаста: сервер, конвертация, клиент на каждом устройстве.

Readora родилась из этой боли как попытка сделать четвёртый вариант: свои файлы, свой доступ, любой браузер.

Что это такое

Readora — веб-приложение: личная библиотека и читалка в одном окне. Загружаешь FB2 или EPUB — сервис сам вытаскивает метаданные, обложку и оглавление. Дальше книга живёт в приватной библиотеке: никто, кроме владельца аккаунта, её не видит и не скачивает.

Отображение книг в персональной библиотеке
Отображение книг в персональной библиотеке

Что умеет:

  • Ридер в браузере — настройки шрифта, размера, межстрочного интервала, ширины колонки и три темы: светлая, сепия, тёмная. Прогресс чтения сохраняется автоматически — открыл книгу с телефона, продолжил с ноутбука с того же места.

  • Организация — жанры, статусы чтения («читаю», «прочитано», «в планах»), полнотекстовый поиск по своей библиотеке.

  • Циклы и серии — книги объединяются в серии с порядковыми номерами. Для поклонников длинных саг — must have.

  • PWA (Progressive Web App, прогрессивное веб-приложение) — на телефоне добавляется на главный экран и работает почти как нативное приложение.

  • Приватность как принцип — файлы книг хранятся в серверном хранилище и отдаются только владельцу; сессии — в PostgreSQL, пароли — bcrypt.

Ридер с панелью настроек
Ридер с панелью настроек

Никаких подписок, рекламы и платных «премиум-читалок»: это открытый проект под лицензией MIT.

Почему браузер, а не очередное мобильное приложение

Потому что браузер есть везде. Не нужно собирать и поддерживать три платформы, проходить ревью в сторях и объяснять, почему приложению нужен доступ к контактам. Открыл ссылку — читаешь. Для PWA-сценария на телефоне это работает почти как нативное приложение, но обновляется мгновенно у всех сразу.

Это же определяет и аудиторию: люди, у которых уже есть коллекция файлов и нет желания отдавать её стриминговому сервису.

Что под капотом

Проект сознательно собран на скучном, предсказуемом стеке:

  • Frontend — React 19, Vite 7, TanStack Query, wouter, Tailwind CSS v4, shadcn/ui;

  • Backend — Node.js 24, Express 5, express-session, multer, nodemailer, sharp;

  • База данных — PostgreSQL 16, Drizzle ORM, версионированные миграции;

  • Контракт API — OpenAPI 3.1 + Orval, из спецификации генерируются React Query-хуки и Zod-схемы валидации;

  • Парсинг книг — fast-xml-parser (FB2), adm-zip + cheerio (EPUB).

Пара решений, которыми проект особенно доволен:

OpenAPI как источник истины. Спецификация — не документация «для галочки», а рабочий артефакт: из неё генерируются React Query-хуки на фронтенде и Zod-схемы валидации. Контракт изменился — клиент и валидация обновились, рассинхрона «сервер уже отдаёт новое поле, а клиент о нём не знает» не бывает в принципе.

Миграции, которые не стреляют в ногу. Собственный оркестратор миграций с журналом, checksum для каждой миграции и advisory lock в PostgreSQL: два одновременно стартующих контейнера не выполнят одну миграцию параллельно, а уже применённые молча пропустятся. CI (continuous integration, непрерывная интеграция) проверяет миграции на чистой базе и повторным запуском — на идемпотентность. Для self-hosted продукта, который люди разворачивают сами, это не перестраховка, а гигиена.

Парсинг без тяжёлых зависимостей. FB2 — это XML, EPUB — zip с XHTML внутри. Оба формата разбираются лёгкими библиотеками без headless-браузеров и внешних сервисов: метаданные, обложка, оглавление и текст извлекаются на сервере за доли секунды.

Self-hosting за пять минут

Весь проект — один Node-контейнер и PostgreSQL. Классический путь:

git clone https://github.com/leszavr/readora.git
cd readora
pnpm install
cp .env.example .env        # и сгенерировать SESSION_SECRET: openssl rand -hex 32
docker compose up -d postgres
pnpm run db:migrate
pnpm run dev:server         # API
pnpm run dev:client         # фронтенд

Миграции применяются при старте контейнера автоматически, отдельного CI-доступа к базе не требуется. Для production подойдёт любой Docker-хостинг — от VPS (virtual private server, виртуального выделенного сервера) до CapRover.

Что дальше

Проект молодой и развивается по обратной связи: в планах — удобные шеринг-карточки цитат (чтобы делиться любимыми строками, не раскрывая свою библиотеку), улучшения ридера и рост публичной витрины книг.

Попробовать можно тремя способами:

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

Комментарии (0)