Web-читалка (аналог ReadEra)

Сервисы

  • reader-reader-adaoicapprunning
  • reader-readerdb-qxly7qapprunning

Репо и домены

Репозиторий
github.com/ndport/reader-web
Ветки
staging: dev · prod: main
Домены
reader.nodeport.ru

Деплои

  • runningreaderfeat(formats): волна 3 — DOCX, RTF, ODT Общий санитайзер lib/htmlflow.ts (выделен из mobi.ts): белый список тегов, data-p на листовых блоках, только blob:/data: картинки. DOCX: mammoth (BSD-2, семантический HTML) → htmlflow; TOC по заголовкам. RTF: свой мини-парсер подмножества (~200 строк) вместо rtf.js (2.1 МБ, мёртв с 2023): группы/\par/\b/\i/\ul, \ansicpgN через TextDecoder, \uN c \uc-скипом, destination-группы пропускаются. ODT: свой парсер (zip + content.xml): text:h И text:p со стилем Heading_20_N (так пишет LibreOffice), резолв автостилей (жирный/курсив), списки, таблицы, картинки из Pictures/ → data URI, метаданные из meta.xml. DOC (бинарный) честно в бэклоге — подсказка «пересохрани в .docx». Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: 0f07c024d689bcb1dff2ac8c7866eaf184181d0fлоги деплоя →
  • runningreaderfix(scan): линт — loading как производное состояние, cacheRef в переменную Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: e07450d584c86b4aa010b61639d6fb09451b8be0логи деплоя →
  • runningreaderfeat(formats): волна 2 — PDF и DJVU (скан-режим) ScanReader: постраничный просмотр картинкой (канвас→PNG object URL), свайпы/ стрелки/зоны по краям, слайдер страниц, иммерсив по тапу в центр, кэш 5 страниц с прелоадом соседних; позиция = номер страницы, синкается как у остальных. PDF: pdfjs-dist v6 (Apache-2.0), воркер вендорен в /vendor. DJVU: djvu.js L.0.5.4 (GPL-2.0) — строго отдельным скриптом /vendor/djvu.js в Web Worker, в бандл не линкуется. Обложка при импорте = рендер первой страницы. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: 9364857c52a0affc1a83cb501539a086a5aae81eлоги деплоя →
  • runningreaderfeat(formats): волна 1 — MOBI, AZW3 (KF8), TXT MOBI/AZW3: вендоренный парсер mobi.js из foliate-js (MIT, пин коммита в шапке файла; npm-пакет foliate-js чужой и протухший — не ставить) + fflate для KF8-шрифтов. Секции конвертируются в единый HTML-поток с data-p и белым списком тегов (script/style/внешние src режутся) → рендерит тот же Fb2Reader: позиции, закладки, цитаты и синк работают из коробки. TOC — из filepos-якорей книги (сырая строка, с ведущими нулями!). Картинки — blob: из mobi.js. DRM-ные Kindle честно не открываем (ошибка импорта). TXT: свой парсер — BOM → строгий utf-8 → chardet → windows-1251; абзацы по пустым строкам (или построчно), title из имени файла. Диспетчер lib/flow.ts, +форматы в детект/архивы/accept/whitelist синка. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: f7922f311751f6bc77854aa7184e78c05d3c9ee3логи деплоя →
  • runningreaderfeat(library): навигация библиотеки — статусы, избранное, коллекции, корзина Сайдбар (мобайл-фёрст drawer / десктоп-колонка) со счётчиками: Читаю сейчас, Книги, Избранное, Хочу прочитать, Прочитанные, Авторы, Коллекции, Форматы, Корзина. Всё sync-first: books += status/favorite/trashed_at (LWW записи книги, старый клиент без полей их не стирает — COALESCE по NULL), новые сущности collections/collection_items синкаются как закладки. Автостатус «читаю» при открытии, предложение «прочитано» на закрытии дочитанной. Корзина: файл не удаляется до purge (авточистка 30 дней), из корзины — восстановление/навсегда. IDB v3, идемпотентная миграция PG. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: ebb8e342d603fc1504370124fed77d0e91e79d94логи деплоя →
  • runningreaderfeat(pwa): новая иконка — закладка из облака (выбор Ивана, вариант B) SVG-источники + генератор scripts/make-icons.mjs (sharp); полный набор: 192/512, maskable (безопасная зона 80%), favicon.ico 32+16, apple-icon 180. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: b5e17b19381ef7d7f8edda42dca1b87e4244b75fлоги деплоя →
  • runningreaderfeat(quotes): цитаты — выделил текст → сохранил, синк, экспорт - выделение текста в книге показывает панельку «Сохранить цитату» (FB2 — mouseup/touchend + позиция-блок; EPUB — rendition selected + CFI-диапазон) - вкладка «Цитаты» в боковой панели: список, переход к месту, удаление - экспорт: скопировать все в буфер / скачать .md - синк между устройствами: store quotes в IndexedDB (БД v2), таблица quotes, push/pull в /api/sync (LWW + tombstones), удаление книги хоронит её цитаты - тап-иммерсив не срабатывает поверх живого выделения Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: 0d9aab03017a6eded9098c580c3306618a32acf2логи деплоя →
  • runningreaderfeat(m7): оплата Pro через ЮKassa — 500 ₽/год - /api/pay/create: платёж (redirect-сценарий), Idempotence-Key - /api/pay/webhook: телу не верим — платёж перечитывается из API ЮKassa (уведомления не подписаны); фейковые id игнорируются - /api/pay/status + страница /pay/result с поллингом — fallback, если webhook не дошёл - users.pro_until: срок подписки, продление от текущего срока (+365 дней), NULL = бессрочный (ручной pro); эффективный план считается в getSessionUser, истёкший pro честно становится free (файлы из облака остаются доступны) - таблица payments; кнопка «Оформить Pro» в панели аккаунта, срок подписки - без YOOKASSA_* env кнопка отвечает «оплата скоро появится» (503) - dev: restart unless-stopped для reader-db/reader-s3 Проверено мок-ЮKassa: оплата → pro на год; продление → +2 года; отмена; фейковый webhook; экспирация → 402 на аплоад. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: 1518e352c0616ae674ab3243072d1aefef7f9406логи деплоя →
  • runningreaderfeat(ux): иммерсивное чтение + инструменты библиотеки - тап/клик по центральной трети прячет шапку и футер (FB2 и EPUB; бары absolute — геометрия книги не меняется, без re-layout); Esc возвращает; тап по краям листает как раньше - EPUB: координата клика из iframe переводится через frameElement (iframe epub.js шире экрана — пре-рендер разворотов) - библиотека: живой поиск по названию/автору, полки Все/Читаю/Прочитано, сортировка (недавние/новые/название/автор, русская коллация) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: 99206e328d90b304516008e928f1a41cc200bb37логи деплоя →
  • runningreaderfeat(m6): pro-сторидж — файлы книг в S3, сплит free/pro v2 - S3-хранилище (Timeweb / MinIO локально): PUT/GET/DELETE /api/files/[bookId], ключ userId/bookId, приватный доступ, лимит 100 МБ - гейтинг: загрузка — только pro (402 с ценой), скачивание — любой залогиненный (даунгрейд не берёт книги в заложники) - сплит v2: free без лимита на метаданные/прогресс/закладки; pro — файлы в облаке; лимит «20 книг» удалён - books.has_file (ставит файловое API, синк не перетирает), чистка S3 при удалении книги, upsert-заглушка против гонки аплоад-до-синка - клиент: авто-аплоад при импорте у pro, докачка из облака при открытии на новом устройстве, бейдж «☁ в облаке», кнопка «отправить книги в облако (N)», тексты сплита при регистрации, промо pro 500 ₽/год - dev: MinIO в docker-compose.dev.yml (порт 9010) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> — Commit: c5d086b79fec320e157a12894446a28bf6d372dfлоги деплоя →