18 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 49652f4413 |
fix(mera/b2c): вернуть потерянные вопросы FAQ, починить адрес превью и честность двух комментариев
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
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 / frontend-checks (pull_request) Successful in 1m0s
Правки по трём ревью ветки «v3 становится корнем».
FAQ. Из шести вопросов `content.ts` возражения v3 брали два, остальные
четыре (`how-accurate`, `vs-marketplace`, `personal-data`, `bank-report`)
не рендерились нигде: `Faq.tsx`, который выводил все шесть, удалён вместе
с лэндингом v1. Макетные возражения их тематически закрывали, но
пересказом — а оригиналы проходили юр-ревью. Содержательно пропали два
утверждения: «Имя, паспорт и документы на квартиру мы не спрашиваем» с
отсылкой к странице ПДн (152-ФЗ) и «показываем диапазон, а не одно число»
как прямой ответ про точность (рамка диапазона осталась только в
`AccuracyV3`, рядом с плейсхолдерными числами).
Теперь в секции один порядок чтения: все шесть вопросов из `FAQ` по id
плюс три макетных возражения, которым в `FAQ` пары нет («факт сделки»,
«почему так дёшево», «подписка»). Три макетных пересказа удалены в пользу
оригиналов; у «почему так дёшево» убран второй абзац — почти дословный
повтор второго абзаца `how-accurate`, который теперь стоит следующим.
Новых формулировок не добавлено. Отсылка к странице ПДн стала живой
ссылкой через `PublicLink` (обычный `<a>`, basePath не течёт); текст
ссылки — слова самого `content.ts`, найденные по вхождению.
Гейт стал двусторонним: тест перебирает `FAQ` и требует, чтобы КАЖДЫЙ id
был отрисован. Прежняя версия перечисляла два id руками и потому молчала
ровно в тот момент, когда четыре ответа исчезли со страницы. Проверено
мутацией: снятие вопроса, переименование id и переформулировка фразы про
ПДн красят тест по отдельности.
CADDY. `/trade-in/mera-public/v3` выбыл из @meraLongPages вместе со
страницей и никуда не попал — длинный адрес превью падал в catch-all 404,
хотя раньше вёл на страницу. Обе его формы добавлены в @meraV3Gone. Там же
редирект перестал терять query: было `redir * /`, стало `redir * {uri}`
со срезанием пути — как у @meraLongPages, где перенос UTM и обоснован.
Проверено на живом Caddy 2.11 по настоящему site-блоку: все четыре формы
дают 301 на `/` с сохранённой query, без параметров — чистый `/` без
хвоста `?`, соседние матчеры не задеты, лишний сегмент по-прежнему 404.
КОММЕНТАРИИ, ОПИСЫВАЮЩИЕ НЕСУЩЕСТВУЮЩЕЕ. Строка `page.tsx` в гейте
плейсхолдеров убрана: комментарий над ней утверждал, что устаревший путь
«перестал бы что-либо сканировать», но ни в корневом `page.tsx`, ни в
прежнем `v3/page.tsx` нет ни одного имени из `PLACEHOLDER_EXPORTS` —
страница только собирает секции. Замер подтверждает: множество найденных
плейсхолдеров со строкой и без неё совпадает, а сама по себе она не даёт
ничего. Держать её значило держать ложное ощущение охвата.
Дескриптор «Оценка вторичного жилья по рыночным данным · <регион>» на
внутренние страницы не возвращается — причина записана в докстринге
`InnerHeader`: эту шапку носят и `/estimate`, `/articles`, `/docs`, где
дескриптора не было никогда; требование юриста было про первый экран
лэндинга и там выполняется `HeroV3`; на самих юр-страницах то же сказано
сильнее — в тексте оферты (п. 1.3, 6.2) и в `FooterV3` на каждой странице.
|
|||
| 6976ca8915 |
feat(mera/b2c): лэндинг v3 становится корнем публичного домена
Вариант дизайна выбран — превью с /mera-public/v3 переехало в корень целиком, вместе с блоками, перенесёнными из v1 предыдущим шагом. Каталог v3/ и компоненты v1 (Hero / HowItWorks / WhatYouGet / DataSources / Faq / AddressForm / SiteHeader / SiteFooter) удалены: их данные живут в content.ts и уже читаются секциями v3, поэтому ушла только вёрстка. Юридические страницы (оферта, возврат, ПДн) держались на SiteHeader/ SiteFooter — единственные, кто их ещё импортировал. Переведены на InnerHeader + FooterV3, тем же способом, что /estimate, /articles и /docs. ПЕРИМЕТР. /v3 убран из @meraPages, @meraShortSlash и @meraLongPages, но не выброшен: адрес две недели был открыт наружу и разошёлся по закладкам, поэтому вместо тишины белого списка — 301 на корень (@meraV3Gone, обе формы). На это заведена проверка в public-perimeter.test.ts по образцу соседних; PREVIEW_ONLY стал пустым, а не удалённым — он держит проверку «наружу не открыто ничего сверх известных страниц» двусторонней. Из PUBLIC_SHORT_PATHS гварда /v3 убран: редирект отрабатывает в Caddy, до приложения запрос не доходит, а недостижимая строка в списке публичных путей сделала бы публичной будущую страницу с тем же адресом. Гейт Caddy→гвард односторонний и сам бы этого не поймал — обоснование в коде. NOINDEX ОСТАВЛЕН. Он держится за плейсхолдерные числа marketing-v3.ts (#2904), а не за то, какой вариант дизайна стоит в корне. Путь в гейте обновлён на page.tsx: placeholdersInUse() молча пропускает ненайденный файл, поэтому устаревшая строка не покраснела бы, а перестала бы сканировать. |
|||
| 2d8edd2c4f |
feat(mera/b2c): раздел статей и хаб документов (макет 29.08) + числа статьи заменены на замер
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 58s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Последняя часть макета Макса «B2C модуль для МЕРА»: /articles, /articles/kak-ocenit-kvartiru, /docs. Проводка ссылок доведена до конца — «Статьи» и «Проверьте себя» в шапке и подвале перестали быть заглушками, EstimateHeader переименован в InnerHeader (он теперь стоит на трёх страницах, а не только на проверке). ЧИСЛА СТАТЬИ — НЕ ИЗ МАКЕТА. Макет утверждал «по нашим данным, средний разрыв между первой ценой в объявлении и ценой сделки — 4,1%, а у провисевших дольше трёх месяцев доходит до 9%». Прод-замер (poincare, 29.08) показал, что величины такого рода у нас нет вовсе: связки листинга со сделкой в данных не существует (cadastral_number пуст у всех 108 623 сделок и 111 693 объявлений). Заменено на то, что измеряется парно и честно: по 6202 объявлениям Домклик в ЕКБ (срез 18.07.2026) цену снижали 44%, а среди провисевших дольше трёх месяцев — 62% в медиане на 4,9%. Границы замера названы в самом тексте: это движение цены В ОБЪЯВЛЕНИИ, а не скидка на сделке, и объявлений моложе трёх недель в выборке нет (прогон обогащения их не захватил), поэтому по всему живому рынку доля ниже. Заодно сняты остальные непроверяемые утверждения: «расхождение с кадастровой до 40%» (кадастровой стоимости ПОМЕЩЕНИЙ нет ни в одной базе — проверить нечем), поправки §3 помечены как рыночная практика, а не наш замер, пороги фильтра §2 приведены к тем, что реально стоят в оценщике (радиус 1 км, площадь ±15%, минимум 5 аналогов + каскад послаблений). Плейсхолдеров marketing-v3 в статье не осталось. Периметр закрыт во всех трёх местах: @meraPages / @meraShortSlash / @meraLongPages в apps.caddy, PUBLIC_ROUTES и PUBLIC_SHORT_PATHS в гварде. @meraShortSlash до сих пор не проверялся ничем — добавлен гейт в public-perimeter.test.ts (фальсифицирован: снятие /docs из альтернации красит тест). Гейт #2904 теперь видит и статью (articles-content.ts в V3_SOURCES) — фальсифицировано отдельно: с одним этим файлом в списке снятие noindex красит гейт. Честные отличия от макета — в докстрингах страниц: масок реквизитов и макетной редакции оферты нет (источник правды — /oferta, /privacy, /refund от 13.08), регламент ответа поддержки «9:00-19:00» снят (никто его не устанавливал), фильтр рубрик и обещание «новые статьи каждую неделю» не рендерятся. Проверено: tsc, eslint, vitest 69/69, изоляция mera-public, скриншоты трёх страниц на 1180/375/320px без горизонтального переполнения. |
|||
| d0547ddb9a |
Merge pull request 'feat(observability): кнопка «Принял в работу» под клиентским инцидентом (#3078)' (#3136) from feat/3078-alert-ack-button into main
All checks were successful
Deploy Infra Host / sync-infra-host (push) Successful in 5s
Deploy / changes (push) Successful in 8s
Deploy / deploy-caddy (push) Has been skipped
Deploy Metrics / server (push) Successful in 16s
Deploy Metrics / agent-apps (push) Successful in 24s
Deploy Metrics / agent-infra (push) Successful in 29s
Deploy / build-backend (push) Successful in 37s
Deploy / build-worker (push) Successful in 39s
Deploy / build-frontend (push) Successful in 39s
Deploy / deploy (push) Successful in 1m4s
Deploy / deploy-status (push) Successful in 2s
Deploy / perimeter-smoke (push) Successful in 11s
|
|||
|
|
031b4559b9 |
fix(mera/perimeter): www-формы доменов МЕРА обрывали TLS вместо редиректа
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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
A-записи www.meraocenka.ru / www.merahome.ru / www.meraotsenka.ru заведены и указывают на прод, но site-блоков под них в Caddy не было. Caddy матчит строго по имени хоста и на неизвестное имя сертификата не выпускает, поэтому клиент, набравший привычное «www.», получал обрыв рукопожатия (TLS alert 80) — для браузера это «сайт не открывается». В логах доступа при этом ни строки: до HTTP-слоя запрос не доходил, так что молчали и метрики. www.gendsgn.ru такой блок имел с самого начала и потому работал — здесь ровно тот же приём, три отдельных блока с 301 на канонический meraocenka.ru. Найдено сквозным аудитом периметра 27.08. Проверено: три www-имени резолвятся в 188.124.37.140, curl до правки возвращал пустой код (000) на всех трёх, www.gendsgn.ru — 301. Смоук периметра дополнен проверкой 5b: отсутствие блока проявляется НЕ как 404, а как пустой код ответа, и обычная проверка «спутники отдают 301» такой регресс не ловила. |
||
|
|
053a5fb75c |
feat(observability): кнопка «Принял в работу» под клиентским инцидентом (#3078)
Some checks failed
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Failing after 1m12s
CI / openapi-codegen-check (pull_request) Successful in 1m55s
Alertmanager инлайн-клавиатуру не поддерживает, а без кнопки нет обратной связи «человек увидел и взял в работу»: 27.08 продукты лежали 10 часов, и вопрос «а кто-нибудь это читает» было не к кому адресовать. ГДЕ ЖИВЁТ. Рядом с Alertmanager, на инфраструктурной машине. У бота МЕРЫ уже есть приём обновлений, и повесить обработку туда было бы дешевле, но он работает на продуктовом хосте: при падении продукта кнопка оказалась бы мёртвой ровно тогда, когда нужна. ССЫЛКА, А НЕ CALLBACK. Callback требует читателя обновлений бота. Бот один, и его обновления уже читает МЕРА — второй читатель получил бы 409 Conflict и отобрал бы сообщения у поддержки. БЕЗ ПАРОЛЯ НА /ack/*, ОСОЗНАННО. Кнопку жмут ночью с телефона, когда лежит прод; требование пароля даст ноль нажатий. Защита — 128-битный токен под конкретное сообщение, живущий сутки; максимум, чего добьётся угадавший, — ложная отметка в чате, где сразу видно, что её поставил не человек. ТОЛЬКО КЛИЕНТСКИЙ МАРШРУТ идёт через сервис. Прочие алерты сохраняют прямой путь в Telegram: чем меньше звеньев, тем надёжнее. Если сервис лёг, Alertmanager повторяет доставку и переуведомляет каждые 30 минут — алерт задерживается, но не теряется. Дублировать вторым прямым каналом не стали: шум в канале тревог опаснее задержки. Девять тестов дёргают настоящие функции, подменяя один шов — вызов Bot API. Важнейший: при отказе отправки с клавиатурой сообщение уходит БЕЗ неё — алерт важнее кнопки. |
||
|
|
efaab2efda |
chore(ops): адрес Poincare — 188.124.37.140 вместо заменённого 188.246.224.93 (#3110)
All checks were successful
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI / openapi-codegen-check (pull_request) Successful in 1m58s
CI / backend-tests (pull_request) Successful in 17m16s
Selectel заменил адрес сервера в ночь на 27.08 по нашей заявке: старый 188.246.224.93 фильтровался российскими операторами и не открывался ни с домашнего интернета, ни с мобильного (#3110). Новый адрес фильтрации не имеет — проверено с машины владельца после переключения DNS. Замена уронила сервер на 10 часов: адрес на порте сменился, а в ОС остался прежний (разбор и восстановление — #3119). Здесь только то, что осталось в репозитории. Единственное функциональное вхождение — дефолт HOST_IP в ops/selectel-ci-access.sh: скрипт открывает раннеру доступ на прод-хост, и с прежним значением он молча настроил бы доступ на адрес, которого у нас больше нет. Остальные семь — тексты комментариев в Caddyfile-секциях, compose, bootstrap-скриптах и раннбуке крона; они не исполняются, но именно по ним сверяются при переезде, и разошедшийся адрес в них дороже, чем кажется. |
||
|
|
f893662ef1 |
fix(observability): у Grafana остаётся один вход (#3078)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Внешний basic_auth Caddy снят с витрины по решению владельца: два запроса пароля подряд мешали работе, а у Grafana есть собственная аутентификация с ролями и GF_USERS_ALLOW_SIGN_UP=false. Что теряется, чтобы решение было осознанным: basic_auth отсекал сканеры до Grafana и прикрыл бы её собственную будущую уязвимость. Теперь страница входа видна из интернета напрямую. Это записано комментарием прямо в конфиге, чтобы через полгода не выглядело недосмотром. Приём метрик НАРОЧНО остаётся под basic_auth: туда ходит агент по паролю, который лежит в открытом виде в окружении продуктового хоста, и отдельная учётка там ограничивает ущерб записью. Снят ровно один слой и ровно с витрины. Файл metrics-ui.caddy.snippet и его bind-mount оставлены на месте: диффу так меньше, вернуть слой можно одной строкой. Гард импортов (#3104) проверяет обратное направление - что каждый import покрыт маунтом, - поэтому лишний маунт его не трогает. Проверено: scripts/check-caddy-snippet-mounts.py зелёный (6 конфигов, 7 маунтов); конфиг с новой версией infra.caddy проходит caddy validate в одноразовом контейнере на Beget - Valid configuration. |
||
|
|
124cfb3d5d |
feat(observability): /metrics в обоих бэкендах — счётчики, задержка, дашборд
All checks were successful
CI / backend-tests (pull_request) Successful in 17m30s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m22s
CI Trade-In / backend-tests (pull_request) Successful in 4m51s
Третья часть #3078 и единственная, трогающая прод-код. До неё числовых рядов у приложений не было вовсе: только логи и исключения в GlitchTip. Класс отказов «отвечает, но медленно» и «отдаёт 401 потоком» в такой картине невидим — исключения нет, строка в логе выглядит обычной, а продукт при этом не работает. Метка route — ШАБЛОН маршрута, а не путь запроса. Это несущее решение, а не деталь: кадастровый номер или идентификатор заявки в метке даёт новый временной ряд на каждую сущность, а ряд у Prometheus стоит памяти постоянно, а не в момент запроса. Самый известный способ уронить мониторинг тем самым мониторингом. Незаматченные пути (404, сканеры) сведены в одну метку, иначе тот же взрыв устроит любой бот, перебирающий адреса. Оба свойства сторожатся тестами, а не комментарием: тест бьёт тремя разными идентификаторами и требует ОДИН ряд. Слой регистрируется последним и потому оказывается самым внешним. Изнутри RBAC-гварда не видно ни отказов авторизации, ни времени, которое он тратит на резолв сессии в БД auth, — а именно этот путь уже давал инцидент с блокирующим I/O в middleware (#1202). Упавший исключением запрос считается как 500 в finally: без этого он просто отсутствовал бы в счётчике, то есть ровно тогда, когда метрики нужнее всего. Путь публичен ВНУТРИ и закрыт СНАРУЖИ — это два разных периметра. Скрейп идёт из docker-сети, где заголовка X-Authenticated-User нет ни у кого, поэтому /metrics внесён в _PUBLIC_PATHS обоих бэкендов; иначе агент получал бы 401 и метрик не было бы вовсе. Наружу путь не открывается ни через gendsgn.ru, ни через meraocenka.ru, и вдобавок закрыт явным respond 404 в обоих site-блоках — чтобы закрытость осталась решением, а не следствием текущего порядка директив. Ограничитель частоты и аудит «Меры» не трогались: оба смотрят только на пути под /api/, скрейп под них не попадает. Проверено тестом, а не чтением. Прод-поведение не меняется ничем, кроме нового публичного пути: ни один существующий обработчик, гвард или маршрут не тронут. Refs #3078 |
||
|
|
309d273f3f |
feat(observability): стек метрик и логов — Prometheus, Loki, Grafana, агенты на обоих хостах
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Метрик в проекте не было ни одной: ни экспортеров, ни /metrics в бэкендах, единственный канал наблюдения — journald, единственный сигнал об аварии — исключение в GlitchTip. Из-за этого целый класс отказов невидим в принципе: задача рапортует done, строк ноль, исключения нет. Так протухли данные на семь месяцев (#2998), 34 дня был мёртв house_imv_backfill (#2698), 8 суток писал ноль newbuilding_enrich (#2767), 91 день копилось раздутие listings (#2992). Grafana не заменяет GlitchTip: ошибки остаются там. Grafana OSS не принимает Sentry DSN ни одним компонентом, а скрубберы в before_send — требование 152-ФЗ. Здесь появляется другой класс данных: ряды и алерты по трендам. Наблюдатель поставлен у ДРУГОГО провайдера, чем наблюдаемое: серверная сторона на Beget, рядом с GlitchTip. Если ляжет Poincare, мониторинг должен об этом сказать, а не лечь вместе с ним. Транспорт push, а не pull: агент на Poincare шлёт remote_write и логи исходящим HTTPS, поэтому там не открывается ни одного входящего порта сверх 22/80/443. При обрыве канала Alloy копит в WAL и досылает — pull-скрейп в той же ситуации терял бы точки именно в аварии, ради которой мониторинг и нужен. Два контура доступа с разными учётками. Пароль приёмника по построению лежит открытым на продуктовом хосте, значит его компрометация неизбежна вместе с хостом; будь это учётка витрины, утёк бы и доступ к дашбордам. GlitchTip читается прямым SQL, а не Sentry-плагином: у плагина на 6.1.6 stats_v2 отдаёт 500 (баг GlitchTip #381), Events/Discover — 404 (#416), а в grafana/sentry-datasource слово glitchtip не встречается ни разу. Схема сверена на живой базе: колонка времени называется timestamp, а не received, и отдельной таблицы IssueIndex не существует — агрегаты лежат на самой issue_events_issue. Алерты за профилем alerts: канал доставки — открытый вопрос #3078, и стек не должен на нём стоять. Деплой предупреждает, что уведомлять пока некому. Каждая настройка, способная отказать молча, закрыта явно: ретенция Prometheus задана и по времени и по размеру, retention_enabled у компактора Loki (без него retention_period не работает вовсе), путь к журналу и запуск Alloy от root (иначе агент читает ноль записей без ошибки), проверка Caddy до перезагрузки (на этом хосте тот же Caddy держит git, errors и obsidian). Refs #3078 |
||
| 60cb510ef2 |
refactor(caddy): разделить site-блоки по хостам под переезд (#3062)
Some checks failed
Deploy / changes (push) Successful in 8s
Deploy Trade-In / changes (push) Successful in 12s
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy / build-worker (push) Successful in 48s
Deploy / build-backend (push) Successful in 49s
Deploy / build-frontend (push) Successful in 49s
Deploy / deploy (push) Successful in 1m37s
Deploy / deploy-status (push) Successful in 1s
Deploy Trade-In / build-frontend (push) Successful in 2m34s
Deploy / perimeter-smoke (push) Successful in 10s
Deploy Trade-In / test (push) Failing after 3m56s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / deploy (push) Has been skipped
Deploy Trade-In / perimeter-smoke (push) Has been skipped
Deploy Trade-In / deploy-status (push) Failing after 1s
|
|||
|
|
f30820bb9a |
fix(infra/caddy): site-блоки с секретами переживают деплой — import caddy/local/*.caddy
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
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 / frontend-checks (pull_request) Successful in 59s
Деплой делает `git reset --hard origin/main`, поэтому любой site-блок, вписанный руками в /opt/gendesign/Caddyfile, живёт до первого пуша в main. 16.08 так и вышло с garmin.gendsgn.ru: контейнер остался healthy, а хост исчез из конфига — TLS-хендшейк стал падать `alert internal error`, потому что сертификат для него Caddy больше не держит. Класть такой блок в git нельзя: у него единственный рубеж аутентификации — секрет в пути URL (апстрим-MCP аутентификации не имеет, а claude.ai connector кастомные заголовки не шлёт). Развязка: в репозитории только `import caddy/local/*.caddy` и сам каталог (держится .gitignore'ом), файлы с секретами лежат на VPS untracked — reset --hard их не трогает. Пустой glob для Caddy не ошибка, `caddy validate` проходит, на машинах без локальных блоков import — no-op. Проверено `caddy validate` 2.11.4 на VPS: и с пустым каталогом, и с реальным блоком garmin — Valid configuration. |
||
|
|
a42ca6a014 |
chore(auth): regenerate praktika pilot password (#635/#659)
Сброшен пароль praktika (Практика, pilot) — plaintext не был зафиксирован в vault, не верифицируем как «выдан клиенту». Новый base64(bcrypt) hash; plaintext записан в vault meta/00_credentials.md (Caddy basic_auth таблица). Применится deploy git-reset /opt/gendesign + restart gendesign-caddy-1. Refs #659. |
||
| bd3e5c8216 |
feat(tradein): аккаунт praktika + лимит 15 оценок/мес, чистка PRINZIP в UI (#635)
All checks were successful
Deploy / changes (push) Successful in 5s
Deploy Trade-In / changes (push) Successful in 7s
Deploy Trade-In / build-backend (push) Successful in 46s
Deploy / build-backend (push) Successful in 29s
Deploy / build-frontend (push) Successful in 28s
Deploy / build-worker (push) Successful in 29s
Deploy / deploy (push) Successful in 58s
Deploy Trade-In / build-frontend (push) Successful in 1m40s
Deploy Trade-In / deploy (push) Successful in 39s
|
|||
| 252bc2de76 | chore(security): sync admin hash в users.caddy.snippet (#588) | |||
| c08e4d7811 |
feat(rbac): pilot scope → только /trade-in/** (#587)
All checks were successful
Deploy Trade-In / changes (push) Successful in 6s
Deploy / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 26s
Deploy / build-backend (push) Successful in 28s
Deploy / build-worker (push) Successful in 28s
Deploy / build-frontend (push) Successful in 27s
Deploy Trade-In / deploy (push) Successful in 36s
Deploy / deploy (push) Successful in 55s
Decision 2026-05-26: pilot не видит landing/analytics/site-finder/concept, только Trade-In. |
|||
| de1652a6d8 | feat(security): добавить юзера kopylov в basic_auth gate (#433) | |||
| cc2d148967 |
feat(security): закрыть gendsgn.ru basic_auth gate (multi-user, 11 users) (#426)
Pilot demo 28.05.2026: closed gendsgn.ru/* (включая trade-in/) basic_auth gate. /health и /preview/* остаются public через route { } wrapper (Caddy 2.x directive ordering fix).
11 users (admin + user1..user10), bcrypt cost=14, snippet в git (audit trail). Scripts: scripts/auth/{add,remove,list}_user.sh.
Fixup PR #426 (route{} wrap + log rotation + base64 busybox-compat + runbook audit log fix).
|