gendesign/tradein-mvp/docker-compose.yml
bot-backend 671fef758e
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
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 / frontend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m19s
CI / openapi-codegen-check (pull_request) Successful in 2m6s
CI / backend-tests (pull_request) Successful in 17m40s
feat(mera): Метрика и GA4 на публичном контуре + открытие сайта для индексации
ЗАЧЕМ. Статьи МЕРЫ публикуются с UTM-метками, но посмотреть, приходил ли по
ним кто-нибудь, было физически нечем: веб-аналитики на публичном контуре не
было вовсе. Заодно вскрылось, что «толкнуть в выдаче» тоже нельзя — всё
дерево mera-public отдавало `robots: noindex, nofollow`.

СЧЁТЧИКИ. Яндекс.Метрика и GA4 подключаются ТОЛЬКО в `mera-public/layout.tsx`
и никогда в корневом `app/layout.tsx` — иначе счётчик уехал бы в закрытый
контур (/v2, /admin, /scrapers, /history), где анонимных посетителей нет, а
приватные маршруты сотрудников есть. Идентификаторы приходят build-time
(`NEXT_PUBLIC_YM_ID` / `NEXT_PUBLIC_GA_ID`) — канон Dockerfile'а этого
проекта: Next инлайнит NEXT_PUBLIC_* на сборке, runtime env их не подхватит.
Пустое значение = тег не рендерится вовсе, никаких `ym(undefined)`. Оба
build-arg'а прописаны в ОБОИХ блоках CI, включая retry-сборку без кеша.

Вебвизор выключен намеренно. Он пишет ввод в поля, а на `/estimate` человек
вводит адрес своей квартиры; раздел 9 политики этого не раскрывает. Включать
следует одним заходом с правкой политики и маскировкой полей — в коде рядом
записано, что именно понадобится.

ЦЕЛИ ВОРОНКИ. Десять целей: клик по CTA, начало ввода адреса, адрес выбран,
результат с разбивкой по вердикту (ok/thin/none), ошибка расчёта, ошибка
валидации, отказ подсказок, показ платного тизера. Кнопок «Проверить
квартиру» восемь штук в разных компонентах, все — обычные `<a>` через
PublicLink, поэтому вместо восьми копий onClick один делегированный
слушатель на document: девятая кнопка подключится сама. Цель «оплата
успешна» НЕ заведена — вызова checkout во фронте нет вовсе, PAYMENTS_ENABLED
выключен, страницы возврата не существует; вешать её пока не на что.

ИНДЕКСАЦИЯ. Снят noindex со всех публичных страниц, добавлены `app/robots.ts`
и `app/mera-public/sitemap.ts`, metadataBase, canonical на КОРОТКИЕ адреса,
openGraph и JSON-LD Article на главной статье. robots.txt и sitemap.xml
разведены по двум разным handle в Caddy не от хорошей жизни: у Next
robots.txt — конвенция корня app/, а sitemap живёт в сегменте маршрута, и
формы путей не совпадают.

152-ФЗ. Раздел 9 «Файлы cookie и веб-аналитика» в политике (обработчики
названы поимённо — этого требует ч. 3 ст. 6) + уведомляющий, не блокирующий
баннер. Гейт «названий площадок в публичной копии быть не должно» получил
узкое исключение ровно на аналитические словосочетания в политике; голое
«Яндекс» как площадка остаётся запрещённым и там.

ПОПУТНЫЙ БАГ (замер на живом проде 10.09.2026). `meraocenka.ru/articles/`
с UTM-метками отдавал 301 на адрес БЕЗ query — матчер @meraShortSlash
собирал цель из regex-захвата пути и терял параметры. Код ответа при этом
оставался 301, поэтому смоук проблему не видел. Мессенджеры и
автолинкификаторы дописывают слэш сами, то есть атрибуция терялась именно на
трафике по опубликованной ссылке. Починено тем же приёмом, что у соседних
матчеров; в смоук добавлена проверка буквального Location.

ГЕЙТЫ. `isPublicPath` и периметр-тест узнали про новые машинные адреса;
noindex-гейт развёрнут (падает, если флаг вернулся) и расширен на строковую
форму `robots: "noindex"`; заведена проверка, что корневых `handle` в
site-блоке не появляется без объявления — раньше эту дверь гейт не видел.

Проверено: tsc и eslint чисто, `npm run build` проходит, robots.txt и
sitemap.xml отдаются по нужным адресам, при пустых ID в HTML нет ни одного
обращения к mc.yandex.ru и googletagmanager, `caddy validate` валиден,
изоляция mera-public от B2B не нарушена. Два теста LoginPage падают и на
нетронутом дереве — не наши.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CiUFZ3rmTNpp3DRajUo8KQ
2026-09-10 16:52:31 +03:00

100 lines
3.7 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Trade-In MVP — локальный стек для разработки.
#
# Архитектура зеркалит прод gendesign (Caddy -> frontend / backend / postgres),
# но всё локально без TLS — открывать http://localhost:8080
#
# Запуск:
# docker compose up -d --build
#
# Проверки:
# curl http://localhost:8080/health # backend health через Caddy
# open http://localhost:8080 # frontend (auto-redirect → /trade-in)
# open http://localhost:8080/preview/ # tradein.html mockup для сравнения
name: tradein-mvp
services:
postgres:
image: postgis/postgis:16-3.4-alpine
container_name: tradein-postgres
restart: unless-stopped
environment:
POSTGRES_USER: tradein
POSTGRES_PASSWORD: tradein
POSTGRES_DB: tradein
volumes:
- postgres-data:/var/lib/postgresql/data
# SQL init scripts: 001_trade_in_estimates.sql создаст таблицу при первом старте.
- ./backend/data/sql:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U tradein -d tradein"]
interval: 5s
timeout: 3s
retries: 10
ports:
- "127.0.0.1:5433:5432" # local port 5433, чтобы не конфликтовать с прод-туннелем 15432
backend:
build:
# Context = tradein-mvp root (uv workspace): образу нужен packages/scraper-kit
# для editable install (#2137). Dockerfile лежит в backend/.
context: .
dockerfile: backend/Dockerfile
container_name: tradein-backend
restart: unless-stopped
environment:
DATABASE_URL: postgresql+psycopg://tradein:tradein@postgres:5432/tradein
CORS_ORIGINS: '["http://localhost:8080","http://localhost:3000"]'
ENVIRONMENT: dev
depends_on:
postgres:
condition: service_healthy
volumes:
# Hot-reload: монтируем app/ в runner, uvicorn --reload подхватит изменения.
- ./backend/app:/app/app:ro
ports:
- "127.0.0.1:8000:8000" # для прямого debug-доступа в обход Caddy
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
# Паритет с prod build-args (.forgejo/workflows/deploy-tradein.yml,
# job build-frontend): те же 4 счётчика/SEO-переменные, тот же пустой
# дефолт (= "счётчик не подключается", см. frontend/Dockerfile).
# Локально включить: экспортировать переменную в shell ДО
# `docker compose build` — Compose подставит её сюда вместо "".
args:
NEXT_PUBLIC_YM_ID: "${NEXT_PUBLIC_YM_ID:-}"
NEXT_PUBLIC_GA_ID: "${NEXT_PUBLIC_GA_ID:-}"
NEXT_PUBLIC_YANDEX_VERIFICATION: "${NEXT_PUBLIC_YANDEX_VERIFICATION:-}"
NEXT_PUBLIC_GOOGLE_VERIFICATION: "${NEXT_PUBLIC_GOOGLE_VERIFICATION:-}"
container_name: tradein-frontend
restart: unless-stopped
environment:
BACKEND_URL: http://backend:8000
NODE_ENV: production
depends_on:
- backend
ports:
- "127.0.0.1:3000:3000" # для прямого debug-доступа в обход Caddy
caddy:
image: caddy:2-alpine
container_name: tradein-caddy
restart: unless-stopped
ports:
- "127.0.0.1:8080:80" # главный entry point — http://localhost:8080
volumes:
- ./deploy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./frontend/public:/srv/preview:ro # для /preview/tradein.html mockup
- caddy-data:/data
- caddy-config:/config
depends_on:
- frontend
- backend
volumes:
postgres-data:
caddy-data:
caddy-config: