Цены и калькуляторы из базы данных: правка без передеплоя, цена в HTML для AI-поиска, fail-open API
Услуги
- 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-константах. Изменить цену можно было только через коммит и деплой — медленно и требует разработчика. Плюс три жёстких требования:
- Редактировать коэффициенты из админки базы без передеплоя.
- Скрывать/показывать блок цен без передеплоя.
- Цена должна быть в статическом 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-тесте с авто-роллбэком.
Подход
- База как источник правды для цен — коэффициенты и видимость в Postgres, история через триггеры.
- Fail-open везде — API цен, cookie-consent, newsletter: ни один сбой внешней зависимости не ломает путь до формы.
- AEO в архитектуре — цена запекается в HTML на сборке, флаг видимости — рантайм без пересборки.
- Разные инструменты для разных задач — что меняется часто (видимость, коэффициенты) → рантайм; что редко (текст) → сборка.
Результат
- Правка коэффициентов/видимости из админки базы отражается на проде за ≤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 без пересборки сайта?
Что такое fail-open и почему это важно для API цен?
Зачем хранить коэффициенты в базе, а не в коде?
Зачем цена обязательно в статическом HTML?
Как это связано с dual-mode сайтом?
Похожая задача?
Расскажите контекст — подскажем, что и как делать.