
У многих дома лежит папка с сотнями книг в 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.
Что дальше
Проект молодой и развивается по обратной связи: в планах — удобные шеринг-карточки цитат (чтобы делиться любимыми строками, не раскрывая свою библиотеку), улучшения ридера и рост публичной витрины книг.
Попробовать можно тремя способами:
Онлайн: readora.ru — бесплатно, без подписок;
Исходники: github.com/leszavr/readora — MIT, issues приветствуются;
Новости и обновления: Telegram-канал @readora_club.
Подписки заканчиваются, сервисы закрываются, а папка с книгами остаётся. Теперь у неё есть шанс стать библиотекой.