Как контент попадает из Strapi на витрину
Витрина никогда не ходит в Strapi напрямую. Между ними стоит снимок (snapshot) в базе Medusa. Это даёт две вещи: витрина не падает, если Strapi недоступен, и покупатель никогда не видит полуопубликованное состояние.
Путь публикации
- Редактор нажимает «Опубликовать» в Strapi.
- Strapi вызывает вебхук
POST /cms/webhookна backend. - Backend перечитывает опубликованный контент из Strapi (
STRAPI_INTERNAL_URL,STRAPI_READ_TOKEN), собирает целостный снимок и кладёт его в таблицу модуляcms-snapshot. - Витрина читает
GET /catalog/content/homepage— отдаёт снимок,Cache-Control: no-store.
Вебхук подписан: заголовки x-cms-timestamp и x-cms-signature, HMAC на STRAPI_WEBHOOK_SECRET,
сравнение постоянного времени. Неверная подпись — 401, снимок не обновляется.
Подстраховка раз в минуту
Задача cms-snapshot-refresh (apps/backend/src/jobs/cms-snapshot-refresh.ts) выполняется
каждую минуту и делает то же, что вебхук. Поэтому потерянный или не дошедший вебхук —
не авария: контент приедет в течение минуты. Вебхук нужен ради скорости, а не ради доставки.
Защита от отката и гонок
persistCmsSnapshot сравнивает не только ревизию, но и content_updated_at источника:
- ревизия совпала — ничего не пишем;
- снимок в базе новее приходящего контента — отказываемся записывать;
- запись идёт условным
updateпо текущей ревизии иcontent_updated_at <= source, при проигранной гонке операция повторяется один раз.
Смысл: минутная задача и вебхук постоянно работают параллельно, и более старый ответ Strapi не должен затирать более свежий снимок.
Битые ссылки в баннерах
Перед сохранением снимок прогоняется через filterInvalidCmsBanners: баннеры, ведущие на
несуществующие товары или категории, из снимка вычищаются. Редактор в Strapi при этом видит их
опубликованными — расхождение ожидаемое, витрина осознанно строже.
Предпросмотр
POST /cms/preview-link — вызывается самим Strapi, авторизуется общим секретом
CMS_PREVIEW_INTERNAL_SECRET в заголовке x-cms-internal-secret. В ответ — ссылка вида
{STOREFRONT_PUBLIC_URL}/preview?token=…, токен подписан CMS_PREVIEW_SIGNING_SECRET и живёт
10 минут. Витрина по такой ссылке показывает черновик, минуя снимок.
Переменные
| Переменная | Где нужна |
|---|---|
STRAPI_INTERNAL_URL | backend читает контент |
STRAPI_READ_TOKEN | токен чтения Strapi |
STRAPI_WEBHOOK_SECRET | подпись вебхука, одинаковый в Strapi и backend |
CMS_PREVIEW_INTERNAL_SECRET | Strapi → backend, запрос ссылки предпросмотра |
CMS_PREVIEW_SIGNING_SECRET | подпись самого токена предпросмотра |
STOREFRONT_PUBLIC_URL | база для ссылки предпросмотра |