Цены и калькуляторы из базы данных: правка без передеплоя, цена в HTML для AI-поиска, fail-open API

Веб Штурм (собственный продукт) Веб-разработка / AEO 2026-06 — наст. время
≤60 сек
правка цен на проде без передеплоя
fail-open
сбой БД не ломает калькулятор
AEO
цена в статическом HTML — видна AI-ботам

Услуги

  • complex-systems
  • seo-ai-search

Стек

  • Astro SSR
  • Supabase Postgres 17
  • PostgREST
  • remark-directive
  • Svelte 5 runes
  • vitest

Веб Штурм — веб-разработка и AEO. Калькуляторы услуг хранили коэффициенты в коде: любая правка цены — через коммит и деплой. Мы вынесли цены и видимость блоков в базу данных: правка отражается на проде за ≤60 секунд без передеплоя, при этом цена запечена в статический HTML и видна AI-поиску, а API работает fail-open — сбой базы не ломает калькулятор. Это наше собственное внедрение на webshturm.ru, часть перестройки сайта в dual-mode.

Кейс описывает собственное решение Веб Штурм. Метрики качественные (характер архитектуры), без выдуманных клиентских цифр.

Контекст

Калькуляторы (оценка SEO-аудита, Pentest, Legacy-миграции) хранили коэффициенты в TypeScript-константах. Изменить цену можно было только через коммит и деплой — медленно и требует разработчика. Плюс три жёстких требования:

  1. Редактировать коэффициенты из админки базы без передеплоя.
  2. Скрывать/показывать блок цен без передеплоя.
  3. Цена должна быть в статическом HTML — видна поисковым и AI-ботам без JS (AEO).

Какие вызовы решены

Как редактировать цены без передеплоя? Схема в Supabase (calc_configs — конфиги калькуляторов, price_blocks — флаги видимости) + *_versions с BEFORE UPDATE триггерами для истории правок. RLS включён, GRANT только service_role (anon/authenticated доступа не имеют). Правка в админке базы отражается на проде через ≤60 секунд.

Как сделать так, чтобы сбой базы не ломал страницу? Fail-open API (/api/pricing/calculators, /api/pricing/visibility, prerender=false): in-memory кэш 60 секунд, при ошибке БД — 200 + {}, а не 500. Калькулятор-остров видит пустой конфиг и переключается на DEFAULT-значения. Пользователь видит корректный расчёт, заявка приходит. Fail-closed (500) сломал бы страницу и потерял лид.

Как положить цену в статический HTML для AEO? remark-директива: :::price{service=X} разворачивается в <section data-price-block="X"> при сборке — содержимое статический HTML с ценой. PriceVisibility.svelte (client:idle) читает visibility-API и прячет секцию при is_visible===false. Краулер и AI-боты обходят сайт без JS — видят цену. Компромисс: текст цены зафиксирован на сборке (изменение текста → пересборка), а флаг видимости — без пересборки.

Как не положить формы тихим 403 после переноса базы? При cutover схему залили дампом — таблицы, данные и RLS восстановились, но GRANT слетели (restore под другим суперпользователем меняет владельца). PostgREST вернул 403 на service_role-endpoint’ах. Урок: после любого переноса схемы — первая проверка has_schema_privilege('service_role','<schema>','USAGE'), и GRANT до запуска сайта.

Как не оставить SSR-роуты на старом бандле? Сайт на Astro node-adapter: статика в dist/client, SSR-бандл в dist/server. CI исторически синкал только dist/client — для статики хватало. Новые /api/pricing/* (prerender=false) живут в dist/server и давали 404 при «зелёном» CI. Фикс: деплой всего dist/ + SSR-маршрут в smoke-тесте с авто-роллбэком.

Подход

  1. База как источник правды для цен — коэффициенты и видимость в Postgres, история через триггеры.
  2. Fail-open вездеAPI цен, cookie-consent, newsletter: ни один сбой внешней зависимости не ломает путь до формы.
  3. AEO в архитектуре — цена запекается в HTML на сборке, флаг видимости — рантайм без пересборки.
  4. Разные инструменты для разных задач — что меняется часто (видимость, коэффициенты) → рантайм; что редко (текст) → сборка.

Результат

  • Правка коэффициентов/видимости из админки базы отражается на проде за ≤60 секунд без передеплоя.
  • Цена запечена в статический HTML — видна поисковым и AI-ботам (AEO).
  • Fail-open: сбой Supabase не ломает калькулятор, пользователь видит DEFAULT-расчёт.
  • История правок цен — через версионные таблицы и триггеры, без отдельного audit log.
  • Два инфра-урока закрыты: проверка грантов после переноса схемы и деплой всего dist/ с SSR-smoke-тестом.

Что использовали

Astro SSR-роуты (/api/pricing/*, prerender=false, fail-open, кэш 60с). Supabase Postgres 17 + PostgREST + RLS (calc_configs, price_blocks, версионные таблицы, триггеры). remark-directive (:::price<section data-price-block>). Svelte 5 runes (PriceVisibility.svelte, client:idle). vitest для unit/интеграционных тестов (функции калькуляторов принимают config аргументом — тестируемы без БД).

Что мы можем сделать у вас

Если вам нужно управлять ценами и калькуляторами без привлечения разработчика, а цены должны попадать в AI-поиск — реализуем аналогичную архитектуру: коэффициенты в базе, правка без передеплоя, цена в HTML, fail-open отказоустойчивость. Обсудить →

Часто задаваемые вопросы

Как цена оказывается в HTML без пересборки сайта?
Цена запекается при сборке через remark-директиву :::price{service=X} →
. Статический HTML содержит цену — поисковый и AI-бот видит её без JS (AEO). На клиенте PriceVisibility.svelte (client:idle) делает fetch /api/pricing/visibility: если is_visible===false — прячет блок. Сама правка коэффициентов в базе отражается на проде через ≤60 секунд без пересборки; изменение текста цены требует пересборки HTML.
Что такое fail-open и почему это важно для API цен?
Fail-open означает: при ошибке базы данных API возвращает 200 + пустой объект {}, а не 500. Калькулятор-остров видит пустой конфиг и переключается на жёстко заданные DEFAULT-значения. Сайт продолжает работать, пользователь видит корректный расчёт. Альтернатива (fail-closed, 500) — сломанная страница и потерянный лид.
Зачем хранить коэффициенты в базе, а не в коде?
Чтобы менять цены и видимость блоков без участия разработчика и цикла сборки/деплоя. Менеджер правит коэффициент в админке базы — изменение на проде через минуту. Раньше любая правка цены требовала коммита и деплоя. Плюс история изменений через триггеры — снапшот предыдущей версии при каждом обновлении.
Зачем цена обязательно в статическом HTML?
Для AEO (Answer Engine Optimization): поисковые и AI-боты (Яндекс.Нейро, Google AI Overviews, ChatGPT, Perplexity) обходят страницы и цитируют контент без выполнения JavaScript. Если цена подгружается только скриптом — бот её не увидит. Запечённая в HTML цена попадает в ответы AI-поиска и сниппеты.
Как это связано с dual-mode сайтом?
Цены/калькуляторы — часть более крупной перестройки webshturm.ru в dual-mode «Разработчику/Руководителю» (отдельный кейс). Параллельные коллекции дают два индексируемых URL-дерева; директива цен и калькуляторы работают одинаково в обоих контурах, конфиг — единый источник правды в базе.

Похожая задача?

Расскажите контекст — подскажем, что и как делать.