All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (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 / backend-tests (pull_request) Successful in 5m4s
Клиент пишет боту в личку → воркер зеркалит сообщение через copyMessage в топик супергруппы-форума → оператор отвечает реплаем на зеркало → бот доставляет ответ клиенту. Полный лог переписки в Postgres. Отдельный контейнер на long-polling, а не webhook в tradein-backend: не нужно пробивать дырку в auth-middleware (_PUBLIC_PATHS, #2213) и маршрут в Caddy, нулевая внешняя поверхность, падение бота не задевает API. Без aiogram — httpx уже в зависимостях, нужны только getUpdates/copyMessage. Маршрутизация ответа — по topic_message_id: message_id в Telegram уникален в пределах чата сквозь все топики, а все зеркала лежат в одном support-чате, поэтому спутать адресата нельзя. Реплай на шапку/на ответ другого оператора не резолвится (у direction='out' topic_message_id IS NULL) → тихий игнор. Безопасность (найдено ревью, воспроизведено эмпирически): - токен Telegram живёт в PATH URL, поэтому sanitize_url его не режет; утекал в GlitchTip через locals стек-фреймов (include_local_variables по умолчанию True) и через span data HttpxIntegration. Закрыто include_local_variables=False + regex-редактор в before_send (обе формы: /bot<id>:<secret> и голая <id>:<secret>), поверх существующего PII-scrub. - httpx-логгер печатает полный URL на INFO → боевой токен уходил бы в docker logs каждые 30с. Приглушён до WARNING. Надёжность: - kill-switch при пустом токене — idle-блокировка, не exit(0): при restart: unless-stopped выход с любым кодом даёт рестарт-луп. unless-stopped выбран сознательно — только он гарантирует автозапуск после ребута VPS. - stop_grace_period: 120s — дефолтные 10с убивали бы контейнер раньше, чем докрутится long-poll (30с) и отработает drain (100с). - сбой SQL теперь ловится отдельно и делает rollback перед сдвигом offset: иначе сессия в failed-transaction не давала сохранить offset, апдейт переигрывался и зеркалился в топик по кругу. 152-ФЗ: переписка — ПДн, ON DELETE CASCADE по chat_id, удаление клиента одним DELETE. Ретенция — follow-up. Бот не включается автоматически: TELEGRAM_* задаются в runtime-env на VPS, без них воркер штатно висит в idle. Порядок — в DEPLOY.md. Тесты: 51 passed (маршрутизация обоих направлений, дедуп, 403→is_blocked, throttle-окно шапки, redaction токена во всех формах event).
53 lines
3 KiB
Text
53 lines
3 KiB
Text
# Trade-In MVP — environment variables example.
|
||
# Скопируй в .env (или экспортируй в shell) для override defaults.
|
||
|
||
# === Backend ===
|
||
DATABASE_URL=postgresql+psycopg://tradein:tradein@postgres:5432/tradein
|
||
CORS_ORIGINS=["http://localhost:8080","http://localhost:3000"]
|
||
ENVIRONMENT=dev
|
||
|
||
# Yandex Geocoder API key (25k req/day free tier).
|
||
# Required for backfill scripts (scripts/backfill_house_coords.py + audit_address_mismatch.py).
|
||
# Empty = Nominatim fallback для backend геокодинга; backfill scripts требуют этот ключ
|
||
# и упадут с SystemExit без него.
|
||
YANDEX_GEOCODER_API_KEY=
|
||
|
||
# DaData /clean/address — обогащение target адреса в estimate flow (PR Q1).
|
||
# Возвращает canonical-форму, kadastr_num, ФИАС, координаты, ближайшее метро.
|
||
# Demo tier: 100 req/день — хватит для тестов и low-traffic prod.
|
||
# Если хотя бы один не задан → service возвращает None gracefully, estimator
|
||
# продолжает работу без enrichment.
|
||
# Получение ключей: https://dadata.ru/api/clean/
|
||
DADATA_API_TOKEN=
|
||
DADATA_API_SECRET=
|
||
|
||
# === Scrapers ===
|
||
# Транспорт скраперов: "browser" (default — через камуфокс-сайдкар tradein-browser,
|
||
# обходит anti-bot/datacenter-IP) или "curl_cffi" (legacy fallback).
|
||
# SCRAPER_FETCH_MODE=browser
|
||
#
|
||
# #915 Stage 3: гонять avito IMV-backfill через /fetch-json сайдкара (in-page fetch
|
||
# на avito.ru — same-origin + прокси + warmed-cookies обходят datacenter-403, #562).
|
||
# Dormant по умолчанию; включать после прод-валидации IMV-через-браузер.
|
||
# AVITO_IMV_USE_BROWSER_FETCHER=false
|
||
|
||
# === Frontend (build-time) ===
|
||
# Backend URL для Next.js rewrites (browser обращается к /api/* — Next.js проксирует на BACKEND_URL/api/*).
|
||
# В docker compose это уже выставлено в http://backend:8000 — менять не нужно.
|
||
# BACKEND_URL=http://backend:8000
|
||
|
||
# === Postgres (если меняешь — синхронизируй с DATABASE_URL и docker-compose.yml) ===
|
||
POSTGRES_USER=tradein
|
||
POSTGRES_PASSWORD=tradein
|
||
POSTGRES_DB=tradein
|
||
|
||
# === Telegram support-bot bridge (tgbot service, docker-compose.prod.yml) ===
|
||
# Long-polling worker: пересылает support-обращения в Telegram-топик. Не FastAPI,
|
||
# отдельный процесс (app/tgbot_main.py), env читается из backend/.env.runtime на VPS.
|
||
#
|
||
# BotFather token. Пусто = бот не стартует (выключен).
|
||
TELEGRAM_BOT_TOKEN=
|
||
# ID супергруппы-форума с включёнными топиками (вида -100XXXXXXXXXX).
|
||
TELEGRAM_SUPPORT_CHAT_ID=
|
||
# ID топика (thread) внутри супергруппы, куда падают support-сообщения.
|
||
TELEGRAM_SUPPORT_TOPIC_ID=
|