Своя инсталляция

Lectern — свободное программное обеспечение под лицензией GPL. Исходный код, инструкции по установке и трекер задач — github.com/duckhawk/lectern.

Дальше — конспект для того, кто администрирует инсталляцию, а не замена README в репозитории.

Из чего это состоит

Приложение на Django, PostgreSQL для каталога, Redis для кэша страниц и каталог с файлами книг, в который приложение никогда не пишет. Книги могут лежать россыпью, внутри zip-архивов или быть перечислены в индексе INPX — сканер умеет все три варианта.

Всё, что настраивается во время работы, лежит в админке Django в разделе Constance: менять эти параметры можно без перезапуска и передеплоя.

Сканирование

python3 manage.py sopds_scanner start запускает сканер по расписанию, заданному в админке (SOPDS_SCAN_SHED_*, синтаксис cron), а scan/rescan — однократно. Сканер вычитывает метаданные из каждой книги и строит индекс дайджестов, по которому прогресс из KOReader находит нужную книгу.

SOPDS_ROOT_LIB — каталог, который он обходит, SOPDS_BOOK_EXTENSIONS — какие файлы считать книгами.

Конвертеры

Необязательные, все настраиваются в админке:

  • SOPDS_FB2TOEPUB, SOPDS_FB2TOMOBI — внешние программы; если заданы, книги в FB2 дополнительно предлагаются в этих форматах.
  • SOPDS_DJVUTOPDFddjvu из djvulibre, именно он делает DjVu читаемым в браузере. Ключ -quality из команды по умолчанию убирать не следует: без него скан с фотографиями конвертируется в растр без потерь, и сотня страниц может дать гигабайт.

Учётные записи и почта

SOPDS_AUTH решает, нужен ли для каталога вход вообще, SOPDS_ALLOW_REGISTRATION — могут ли посетители заводить себе учётные записи сами. Заполненные SOPDS_SMTP_* включают восстановление пароля и отправку книг на устройство. SOPDS_OIDC_* добавляет на страницу входа кнопку Keycloak, а SOPDS_LOGIN_NOTICE_EN и SOPDS_LOGIN_NOTICE_RU — вашу собственную строку над формой входа: у кого просить учётную запись или под чем входить в публичное демо. По строке на язык; если заполнена только одна, показывается она.

Первая учётная запись должна откуда-то взяться:

python3 manage.py sopds_util ensureuser <имя> <пароль> --superuser

В отличие от createsuperuser из Django, эта команда идемпотентна — она создаёт учётную запись или сбрасывает ей пароль, — поэтому её может выполнять деплой при каждом выкате. Пароль передаётся в командной строке, где его видно в списке процессов, так что для всего, кроме демо, лучше завести учётную запись один раз и сменить пароль через веб-интерфейс.

Язык интерфейса

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

Переезд на другой домен

OPDS-каталог, сохранённый на читалке, закладка, настройки синхронизации в KOReader — ничего из этого отсюда не поправить, и всё это ломается при смене имени хоста. Поэтому оставьте старое имя в ALLOWED_HOSTS, а переменной окружения CANONICAL_HOST задайте новое. Тогда любой запрос, пришедший на другое имя, получает постоянный редирект с сохранением пути и параметров, и сохранённые каталоги продолжают работать.

Резервные копии

Файлы книг лежат на диске, и повторное сканирование восстановит каталог из них, но оно не восстановит того, что накопили читатели: полки, статусы, оценки, позиции чтения, теги, списки, счётчики и метаданные, которые дособрал sopds_enrich.

python3 manage.py sopds_userdata_export backup.json
python3 manage.py sopds_userdata_import backup.json

Выгрузка опирается на признаки, которые переживают пересборку каталога, — путь и имя файла, по которым книгу узнаёт сам сканер, с дайджестом содержимого в качестве запасного варианта. Идентификаторы книг при пересборке меняются, и восстановление по ним назвало бы не те книги.

Мониторинг

/healthz и /readyz для проб, /metrics в формате Prometheus при включённом SOPDS_METRICS_ENABLE (при желании — за bearer-токеном). Метрики считаются из базы, поэтому все воркеры отдают одинаковые числа.