МЕРА B2C: микроразметка schema.org и картинки превью ссылок #3438

Merged
lekss361 merged 1 commit from feat/mera-schema-og into main 2026-09-10 16:38:51 +00:00
Owner

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

Микроразметка

Конструкторы узлов вынесены в _lib/schema.ts — чистый модуль без React, по образцу соседнего _lib/analytics.ts. Все значения берутся из content.ts, ни одной строки не продублировано.

Узел Где
Organization + WebSite одним @graph layout поддерева, то есть все девять страниц
Service + Offer лэндинг
FAQPage блок возражений
BreadcrumbList статьи, статья, документы, для бизнеса
Article (был) переведён на publisher: { "@id": ... }

Разметка не обещает того, чего нет

Два места, где это стоило внимания:

  • Offer.availability вычисляется из PUBLIC_ESTIMATE_ENABLED. Флаг сейчас false, платёжного контура в коде нет, поэтому в разметку уходит PreOrder, а не InStock: цена опубликована, купить нельзя, и врать об этом поисковику нечего. Включится приём оплаты — значение сменится само.
  • FAQPage строится из того же массива ITEMS, который рисует <details>, а не из FAQ напрямую: в ITEMS часть вопросов заменена макетными формулировками, и разметка от сырого FAQ разошлась бы с видимым текстом молча.

Выдуманных дат, рейтингов и отзывов не добавлено нигде.

Картинки

Две штуки, и это не дубль: og-mera.png 1200×630 — превью ссылки, logo-mera.png 512×512 — логотип организации в разметке, который поисковик обрезает близко к квадрату и где баннер превратился бы в обрезок надписи. Исходники обеих лежат рядом в scripts/og/*.html вместе с командой перерисовки.

Готовые PNG, а не opengraph-image.tsx: satori внутри ImageResponse рисует только переданными ему байтами шрифта и кириллицу по умолчанию не покрывает, плюс не видит ни CSS Modules, ни наших --b2c-*. Разбор — в шапке scripts/og/og-mera.html.

images продублирован в twitter: Next не переносит их из openGraph, когда метаданные заданы объектом, и карточка обещала бы крупное превью без картинки.

Периметр

Обе картинки лежат в public/ и раздаются Next'ом по basePath-корню, куда rewrite @meraPages не достаёт — отсюда два отдельных handle по образцу robots.txt. Оба названы в ROOT_HANDLES_ALLOWED: корневой handle это вторая дверь в тот же периметр, и гейт обязан её видеть.

Смоук проверяет не только 200, но и Content-Type. Перепутанный rewrite отдаёт 200 с HTML, обходчик мессенджера молча его отбрасывает, и по логам приложения этого не видно: запрос туда не доходит.

Проверено

  • npx tsc --noEmit, npx eslint src/app/mera-public --max-warnings=0 — чисто
  • npx vitest run src/app/mera-public/__tests__ — 18 файлов, 215 тестов
  • node scripts/check-mera-public-isolation.mjs — 70 файлов, закрытый контур не найден
  • caddy validate на эффективном конфиге — Valid configuration
  • bash -n смоука — чисто
  • Прод-сборка с basePath=/trade-in и разбор отрендеренного HTML: JSON-LD парсится на всех пяти страницах, og:image и twitter:image на месте

Полный npm test даёт два падения в src/app/login/__tests__/LoginPage.test.tsx — известный локальный дефект среды, воспроизводится и на нетронутом дереве, к этим правкам отношения не имеет.

Ревью code-reviewer: approve, замечаний нет.

После деплоя (владельцу)

Разметку и превью имеет смысл проверить снаружи: валидатор структурированных данных Яндекса и Google Rich Results Test, плюс отправить ссылку самому себе в Телеграм и посмотреть картинку.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CiUFZ3rmTNpp3DRajUo8KQ

Сайт открыт для индексации 10.09.2026, но поисковику он представлялся девятью страницами без единого структурированного факта о том, кто их публикует и что продаёт, а ссылка на него в мессенджере разворачивалась голым текстом без картинки. ## Микроразметка Конструкторы узлов вынесены в `_lib/schema.ts` — чистый модуль без React, по образцу соседнего `_lib/analytics.ts`. Все значения берутся из `content.ts`, ни одной строки не продублировано. | Узел | Где | |---|---| | `Organization` + `WebSite` одним `@graph` | layout поддерева, то есть все девять страниц | | `Service` + `Offer` | лэндинг | | `FAQPage` | блок возражений | | `BreadcrumbList` | статьи, статья, документы, для бизнеса | | `Article` (был) | переведён на `publisher: { "@id": ... }` | ## Разметка не обещает того, чего нет Два места, где это стоило внимания: - **`Offer.availability`** вычисляется из `PUBLIC_ESTIMATE_ENABLED`. Флаг сейчас `false`, платёжного контура в коде нет, поэтому в разметку уходит `PreOrder`, а не `InStock`: цена опубликована, купить нельзя, и врать об этом поисковику нечего. Включится приём оплаты — значение сменится само. - **`FAQPage`** строится из того же массива `ITEMS`, который рисует `<details>`, а не из `FAQ` напрямую: в `ITEMS` часть вопросов заменена макетными формулировками, и разметка от сырого `FAQ` разошлась бы с видимым текстом молча. Выдуманных дат, рейтингов и отзывов не добавлено нигде. ## Картинки Две штуки, и это не дубль: `og-mera.png` 1200×630 — превью ссылки, `logo-mera.png` 512×512 — логотип организации в разметке, который поисковик обрезает близко к квадрату и где баннер превратился бы в обрезок надписи. Исходники обеих лежат рядом в `scripts/og/*.html` вместе с командой перерисовки. Готовые PNG, а не `opengraph-image.tsx`: satori внутри `ImageResponse` рисует только переданными ему байтами шрифта и кириллицу по умолчанию не покрывает, плюс не видит ни CSS Modules, ни наших `--b2c-*`. Разбор — в шапке `scripts/og/og-mera.html`. `images` продублирован в `twitter`: Next не переносит их из `openGraph`, когда метаданные заданы объектом, и карточка обещала бы крупное превью без картинки. ## Периметр Обе картинки лежат в `public/` и раздаются Next'ом по basePath-корню, куда rewrite `@meraPages` не достаёт — отсюда два отдельных `handle` по образцу robots.txt. Оба названы в `ROOT_HANDLES_ALLOWED`: корневой `handle` это вторая дверь в тот же периметр, и гейт обязан её видеть. Смоук проверяет не только 200, но и Content-Type. Перепутанный rewrite отдаёт 200 с HTML, обходчик мессенджера молча его отбрасывает, и по логам приложения этого не видно: запрос туда не доходит. ## Проверено - `npx tsc --noEmit`, `npx eslint src/app/mera-public --max-warnings=0` — чисто - `npx vitest run src/app/mera-public/__tests__` — 18 файлов, 215 тестов - `node scripts/check-mera-public-isolation.mjs` — 70 файлов, закрытый контур не найден - `caddy validate` на эффективном конфиге — Valid configuration - `bash -n` смоука — чисто - Прод-сборка с `basePath=/trade-in` и разбор отрендеренного HTML: JSON-LD парсится на всех пяти страницах, `og:image` и `twitter:image` на месте Полный `npm test` даёт два падения в `src/app/login/__tests__/LoginPage.test.tsx` — известный локальный дефект среды, воспроизводится и на нетронутом дереве, к этим правкам отношения не имеет. Ревью `code-reviewer`: approve, замечаний нет. ## После деплоя (владельцу) Разметку и превью имеет смысл проверить снаружи: валидатор структурированных данных Яндекса и Google Rich Results Test, плюс отправить ссылку самому себе в Телеграм и посмотреть картинку. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01CiUFZ3rmTNpp3DRajUo8KQ
lekss361 added 1 commit 2026-09-10 16:07:38 +00:00
feat(mera-b2c): микроразметка schema.org и картинки превью для публичного сайта
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m5s
a82382f47a
Сайт открыт для индексации 10.09.2026, но поисковику он до сих пор
представлялся девятью страницами без единого структурированного факта о
том, кто их публикует и что продаёт, а ссылка на него в мессенджере
разворачивалась голым текстом без картинки.

МИКРОРАЗМЕТКА. Конструкторы узлов вынесены в `_lib/schema.ts` — чистый
модуль без React, по образцу соседнего `_lib/analytics.ts`. Все значения
берутся из `content.ts`, ни одной строки не продублировано:

- `Organization` + `WebSite` одним `@graph` в layout поддерева, то есть на
  всех девяти страницах сразу. Остальные узлы ссылаются на организацию
  через `@id`, а не повторяют реквизиты у себя.
- `Service` с `Offer` на лэндинге.
- `FAQPage` в блоке возражений.
- `BreadcrumbList` на статьях, статье, документах и странице для бизнеса —
  ровно по тем крошкам, что видны на экране.
- Существующий `Article` переведён на ссылку `publisher: { "@id": ... }`.

РАЗМЕТКА НЕ ОБЕЩАЕТ ТОГО, ЧЕГО НЕТ. Два места, где это стоило внимания:

- `Offer.availability` вычисляется из `PUBLIC_ESTIMATE_ENABLED`. Флаг сейчас
  `false` (платёжного контура в коде нет), поэтому в разметку уходит
  `PreOrder`, а не `InStock`: цена опубликована, купить нельзя, и врать об
  этом поисковику нечего. Включится приём оплаты — значение сменится само.
- `FAQPage` строится из того же массива `ITEMS`, который рисует `<details>`,
  а не из `FAQ` напрямую: в `ITEMS` часть вопросов заменена макетными
  формулировками, и разметка от сырого `FAQ` разошлась бы с видимым текстом
  молча. Выдуманных дат, рейтингов и отзывов не добавлено нигде.

КАРТИНКИ. Две штуки, и это не дубль: `og-mera.png` 1200×630 — превью ссылки,
`logo-mera.png` 512×512 — логотип организации в разметке, который поисковик
обрезает близко к квадрату и где баннер превратился бы в обрезок надписи.
Исходники обеих лежат рядом в `scripts/og/*.html` вместе с командой
перерисовки: картинка должна оставаться правимой, а не только
переоткрываемой в графическом редакторе.

Готовые PNG, а не `opengraph-image.tsx`: satori внутри `ImageResponse` рисует
только переданными ему байтами шрифта и кириллицу по умолчанию не покрывает,
плюс не видит ни CSS Modules, ни наших `--b2c-*`. Разбор — в шапке
`scripts/og/og-mera.html`.

`images` продублирован в `twitter`: Next не переносит их из `openGraph`, когда
метаданные заданы объектом, и карточка обещала бы крупное превью без картинки.

ПЕРИМЕТР. Обе картинки лежат в `public/` и раздаются Next'ом по
basePath-корню, куда rewrite `@meraPages` не достаёт, — отсюда два отдельных
`handle` по образцу robots.txt. Оба названы в `ROOT_HANDLES_ALLOWED`, иначе
двусторонний гейт периметра покраснел бы, и это правильно: корневой `handle`
— вторая дверь в тот же периметр.

Смоук проверяет не только 200, но и Content-Type: перепутанный rewrite отдаёт
200 с HTML, обходчик мессенджера молча его отбрасывает, и по логам приложения
этого не видно — запрос туда не доходит.

Проверено: tsc, eslint, 215 тестов mera-public, изоляция B2C-дерева, caddy
validate, bash -n смоука, прод-сборка с basePath и разбор отрендеренного
HTML — JSON-LD парсится, og:image и twitter:image на месте.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CiUFZ3rmTNpp3DRajUo8KQ
lekss361 merged commit a659b18771 into main 2026-09-10 16:38:51 +00:00
lekss361 deleted branch feat/mera-schema-og 2026-09-10 16:38:51 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3438
No description provided.