BOOK-LIBRARY и Quartz

Эта заметка — про границу между двумя частями одного проекта. BOOK-LIBRARY отвечает за рабочую библиотеку: книги, обложки, категории, переводы, админские задачи и временные ссылки на скачивание. Quartz отвечает за публичный слой: заметки, проектные страницы, подборки и навигацию вокруг материалов.

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

Такое разделение важно. Если просто скопировать книги в статический сайт, быстро появятся проблемы: где обновлять метаданные, как отзывать файл, что делать с обложками, как не публиковать черновые переводы и как не смешивать заметки с файловым архивом. Поэтому интеграция сделана аккуратнее: Quartz показывает материалы в контексте, а BOOK-LIBRARY продолжает управлять тем, что именно доступно читателю.

Что уже есть на стороне Quartz

В Quartz уже есть несколько частей, связанных с библиотекой:

  • content/library.md;
  • quartz/components/LibraryPage.tsx;
  • quartz/components/scripts/libraryPage.inline.ts;
  • quartz/static/data/bookshelf.json;
  • generated catalog quartz/static/generated/bookshelf-catalog.json;
  • mirrored covers через quartz/util/bookshelfCatalog.ts.

Вместе эти элементы делают библиотеку видимой на сайте: читатель видит отобранный каталог и обложки рядом с заметками, а Quartz при этом не превращается в backend для книг.

Public API вместо прямого доступа к файлам

BOOK-LIBRARY отдаёт публичные данные через public API. Для Quartz это правильная граница: сайту нужны карточки, категории и обложки, но ему не нужно знать внутреннее устройство uploads или SQLite.

Для читателя это выглядит просто: на статическом сайте появляется понятная карточка книги и путь к материалу. Для системы граница остаётся строгой: файлы книг остаются за backend. Скачивание проходит через temp-token flow — сначала запрашивается временная ссылка, потом backend отдаёт файл по token. Это сохраняет контроль на стороне библиотеки и не раскрывает постоянные URL на книги.

Quartz в этой схеме не становится посредником скачивания. Он может быть витриной, навигацией и контекстом, но не файловым сервером.

Static catalog и live API

На странице библиотеки клиентский скрипт сначала пытается работать с живым API BOOK-LIBRARY. Он загружает категории из /api/categories, а книги категории — из /api/categories/:id/books с limit и offset. Так витрина может показывать актуальные публичные данные, когда backend доступен.

Если live API недоступен, включается static fallback. Скрипт читает catalogPath из bookshelf.json, загружает bookshelf-catalog.json и берёт категории и книги оттуда. Для читателя это важная деталь: Quartz не превращается в хрупкую страницу, которая ломается при каждом сбое backend или cross-origin-запроса, а остаётся полезной статической витриной.

В текущем конфиге:

{
  "backendBaseUrl": "https://book.slavx.ru",
  "catalogPath": "/static/generated/bookshelf-catalog.json",
  "previewDescriptionLength": 260
}

Rails, hover preview и modal

LibraryPage показывает книги не обычным списком, а как полку. Каждая категория становится горизонтальным rail: читатель пролистывает обложки, а на десктопе может навести курсор и увидеть hover preview с названием, автором, форматом и коротким описанием.

Клик по книге открывает modal. В нём показываются обложка, категория, номер, формат, автор и полное описание. Если обложка не загрузилась, интерфейс показывает fallback с первой буквой названия. Это небольшая деталь, но она поддерживает ощущение аккуратной витрины: вместо пустого битого image-box читатель всё равно видит понятную карточку.

Build-time generation

generateBookshelfStaticAssets в bookshelfCatalog.ts делает эту витрину устойчивой ещё на build-time: ходит в BOOK-LIBRARY, получает категории, проходит по книгам, скачивает обложки и записывает:

  • JSON-каталог в quartz/static/generated/bookshelf-catalog.json;
  • mirrored covers в quartz/static/generated/book-library-covers.

Это удобно для статического fallback, но важно не перепутать роли. Generated-файлы — не ручной источник истины, а снимок публичной части библиотеки на момент сборки. Если в generated JSON что-то «поправить руками», следующая сборка может перезаписать изменение.

Mirrored covers

Обложки можно зеркалировать в Quartz как статические файлы. Это удобно для карточек: сайт не зависит от внешних URL обложек и может показывать изображения рядом с заметками.

Но mirrored covers — это не перенос всей библиотеки. Обложка помогает читателю узнать книгу и связать её с заметкой, а файл книги остаётся в BOOK-LIBRARY. Такое разделение хорошо ложится на формат сайта: Quartz показывает контекст, BOOK-LIBRARY обслуживает библиотечные операции.

CORS, CORP и public origins

Для такой интеграции важны не только данные, но и браузерные границы. В BOOK-LIBRARY разделены public и admin origins. Публичная часть может быть доступна Quartz-сайту, а админские API не должны случайно открываться тем же способом.

Для обложек используется public CORP. Это помогает корректно показывать изображения в публичных контекстах и не смешивать правила доступа к covers с правилами доступа к admin API.

Здесь нет цели сделать сложную security-схему ради самой схемы. Смысл проще: Quartz получает только то, что должно быть публичным, а операции управления остаются в React-админке BOOK-LIBRARY.

Чем это отличается от прямой публикации книг

Прямая публикация книг на Quartz выглядела бы проще технически: положить файлы в static-директорию и дать ссылки. На первый взгляд это самый короткий путь, но для этого проекта такой вариант хуже.

Он ломает несколько важных границ:

  • скачивание перестаёт идти через временные токены;
  • сложнее скрывать черновые или снятые с публикации переводы;
  • Quartz-репозиторий начинает разрастаться файлами книг;
  • админка теряет контроль над тем, что реально доступно читателю;
  • становится труднее поддерживать одну версию карточки и обложки.

Поэтому Quartz лучше использовать как витрину и навигационный слой. Он помогает объяснить, зачем материал здесь, с какими заметками он связан и почему его стоит открыть. А сами файлы, import jobs, translation jobs и публикация artifacts должны оставаться в BOOK-LIBRARY.

Curated-подборки вместо полного зеркала

Самый полезный следующий шаг — не показывать весь каталог подряд, а делать curated-подборки. Например: книги по теме проекта, подборка по разработке, художественная полка, книги рядом с конкретной заметкой.

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

Markdown-карточки и обратные ссылки

Для отдельных книг можно генерировать Markdown-карточки. Не обязательно для всей библиотеки — только для тех записей, которые действительно нужны на сайте.

Возможная карточка может содержать:

  • название и автора;
  • категорию или подборку;
  • короткое описание;
  • обложку;
  • связь с заметками на сайте;
  • ссылку на карточку или скачивание через BOOK-LIBRARY.

Интереснее всего здесь обратные ссылки. Если по книге есть заметка, обзор или конспект, Quartz может вести к книге, а BOOK-LIBRARY — обратно к заметке. Тогда библиотека перестаёт быть отдельным складом файлов и становится частью личной базы знаний: книга, карточка и мысль из заметки оказываются рядом.

Где проходит граница

После всех деталей граница получается простой:

  • BOOK-LIBRARY хранит книги, метаданные, обложки, переводы, jobs и download flow;
  • React-админка управляет импортом, переводами, провайдерами и безопасностью;
  • публичная витрина BOOK-LIBRARY может показывать живой каталог;
  • Quartz показывает отобранный статический слой: подборки, карточки, заметки, rails и mirrored covers.

Такое разделение спокойнее, чем попытка сделать один инструмент для всего. Quartz остаётся хорошим сайтом для текста, связей и публичных подборок. BOOK-LIBRARY остаётся рабочей библиотечной системой, где управляются файлы, публикация и операции вокруг книг.

Связанные заметки

  • Обзор и сценарии — где Quartz находится среди пользовательских сценариев.
  • Архитектура — почему backend остаётся источником истины для файлов и токенов.
  • Roadmap — какие шаги по Quartz-интеграции стоит развивать дальше.
  • Книжная полка в Quartz — подробный разбор стороны Quartz.