Commit graph

920 commits

Author SHA1 Message Date
TradeIn Deploy
78df203919 fix(tradein): cron-scrape подхватывает .env.runtime (#390)
cron запускается с пустым окружением — ADMIN_TOKEN отсутствует, и все
вызовы admin/scrape и geocode-missing получают 401 (#2 admin-auth).
Парсинг новых объявлений и сделок не работал с ~21.05.

cron-scrape.sh теперь сам читает tradein-mvp/.env.runtime
(resolve по BASH_SOURCE → ../.env.runtime, source с set -a).

Closes #390
2026-05-22 10:23:03 +05:00
TradeIn Deploy
923513f906 fix(tradein): estimate 500 — AmbiguousParameter на NULL-параметрах
Второй баг из #385 (#388 закрыл только алиас distance_m). В
_fetch_analogs ORDER BY использует :target_year и :target_house_type.
Когда год постройки / тип дома не заданы, они приходят NULL — psycopg
шлёт их без типа, PostgreSQL не может определить тип параметра →
psycopg.errors.AmbiguousParameter ($8) → 500 на каждый estimate.

Фикс: явный CAST(:target_year AS integer) и CAST(:target_house_type
AS text). Проверено на прод-БД через psycopg3: без CAST падает с
AmbiguousParameter, с CAST — успешно (7 строк).
2026-05-22 10:03:17 +05:00
TradeIn Deploy
dbe16c035e fix(tradein): estimate 500 — алиас distance_m в ORDER BY-выражении
Регрессия #385 (#6 house-match): ORDER BY использует SELECT-алиас
distance_m внутри арифметического выражения. PostgreSQL подставляет
алиасы в ORDER BY только голым термом — внутри выражения имя ищется
как колонка listings.distance_m, которой нет → "column does not
exist" → 500 на каждый геокодящийся адрес. Инлайнили ST_Distance()
вместо алиаса.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:39:36 +05:00
TradeIn Deploy
63c96628e3 feat(tradein): прогноз срока продажи + rate-limit (#9,#16) 2026-05-21 20:12:49 +03:00
TradeIn Deploy
4ea35d5740 feat(tradein): geocode-missing для deals (#4 завершение)
geocode-missing получил target=listings|deals. deals.geocode_tried_at
добавлен в миграцию 005. cron геокодит и сделки Росреестра по улицам.
2026-05-21 20:04:19 +03:00
TradeIn Deploy
a7d010173f feat(tradein): house-match аналогов + поправка на ремонт (#6,#7)
#6: аналоги сортируются по relevance — расстояние + близость года
постройки + совпадение типа дома (соразмерные квартиры из встречи Птицы).
#7: медиана корректируется коэффициентом по состоянию ремонта
(требует ремонта 0.92 ... евроремонт 1.08).
2026-05-21 20:00:47 +03:00
TradeIn Deploy
6057063aec feat(tradein): импорт реальных сделок Росреестра (#4)
deploy/import-rosreestr.sh — ETL ЕКБ-квартир из gendesign rosreestr_deals
(6.8М строк) в tradein deals. rooms выводится из площади. Дедуп по
md5(rosreestr.id). 54k реальных сделок вместо 60 синтетических.
2026-05-21 19:57:20 +03:00
TradeIn Deploy
7adb7cff66 feat(tradein): auth на admin-эндпоинтах (#2)
/api/v1/admin/scrape и geocode-missing закрыты X-Admin-Token.
settings.admin_token (env ADMIN_TOKEN). Пусто = открыто (dev),
задано = требуем заголовок. cron шлёт токен из .env.runtime.
2026-05-21 19:47:01 +03:00
TradeIn Deploy
609744df91 fix(tradein): чанковый геокодинг — cron больше не таймаутится (#1)
geocode_tried_at: после каждой попытки NOW(), повтор только через 7 дней.
Эндпоинт чанкуется (limit=100), отдаёт remaining. cron вызывает в цикле.
2026-05-21 19:43:12 +03:00
TradeIn Deploy
7cc8d8370d feat(tradein): пагинация Циан/N1 3→8 страниц — плотность данных
После перехода на реальные координаты стало видно что данных тонко.
8 страниц × 4 комнаты ≈ 900+ лотов на источник вместо ~300.
2026-05-21 14:34:29 +03:00
TradeIn Deploy
fb49c56b81 fix(tradein): убрать TRUNCATE из seed — деплой больше не wipe-ит данные
КРИТИЧНЫЙ БАГ: 003_seed_listings.sql содержал TRUNCATE listings,deals,
а deploy-tradein.yml гоняет весь data/sql/*.sql на каждом деплое →
каждый деплой стирал реальные данные парсеров и подсовывал 120 синтетики.

Удалён 003_seed_listings.sql. Вместо него 003_seed_deals.sql —
идемпотентный (INSERT ... WHERE NOT EXISTS), сидит ТОЛЬКО сделки и
ТОЛЬКО когда таблица deals пуста. listings больше не трогается seed-ом.
2026-05-21 13:19:24 +03:00
TradeIn Deploy
d2a740779d fix(tradein): реальные координаты Cian/N1 через геокодинг (был баг 0 аналогов)
Cian/N1 ставили anchor+jitter координаты — фейковые, вокруг центрального
якоря. Для адресов вдали от центра ST_DWithin не находил аналоги → 0.

Теперь Cian/N1 оставляют lat/lon=None, geocode-missing проставляет реальные
координаты по адресу (geom пересчитывается триггером). cron вызывает
geocode-missing после скрейпа. Адрес чистится от суффикса · р-н.
2026-05-21 13:14:33 +03:00
TradeIn Deploy
f7b820984b feat(tradein): радиус 1000м + пагинация Циан/N1
DEFAULT_RADIUS_M 800→1000 (по встрече Птицы: локация до 800-1000м).
Cian/N1 multi-room теперь обходят 3 страницы выдачи → ~2-3x больше лотов.
2026-05-21 11:59:16 +03:00
TradeIn Deploy
42a0c31f27 feat(tradein): N1.ru scraper + cron для 4 источников
Добавлен 4-й источник N1.ru (ekaterinburg.n1.ru) — региональный портал,
хорошо представлен на Урале. DOM-парсинг карточек data-test=offers-list-item
через curl_cffi. Multi-room сегментация 1/2/3/4к.

cron-scrape.sh переписан под прод: запрос к backend через docker exec
(без публичного admin/scrape), все 4 источника (avito/cian/yandex/n1).
2026-05-21 11:44:32 +03:00
TradeIn Deploy
ee43248b11 fix(tradein): HOSTNAME=0.0.0.0 + NEXT_PUBLIC_API_BASE_URL build-arg
502 fix: Next.js standalone server.js биндился на HOSTNAME=<container-id>
вместо 0.0.0.0 → Caddy не мог достучаться до tradein-frontend:3000.
Теперь HOSTNAME=0.0.0.0 и в Dockerfile (runner ENV), и в compose.

API routing fix: фронт собирался без NEXT_PUBLIC_API_BASE_URL, fetch шёл
на same-origin /api/* (главный gendesign-backend) вместо /trade-in/api/*.
Добавлен build-arg в Dockerfile + workflow.
2026-05-21 10:21:32 +03:00
lekss361
aeb895a9dc fix(tradein): PDF-ссылка через API_BASE_URL (basePath)
href Скачать PDF был /api/v1/... без префикса /trade-in → попадал в
main gendesign backend (404). Та же причина что #373 для apiFetch.
2026-05-21 10:15:44 +03:00
lekss361
a17a73af71 fix(tradein): префиксить API_BASE_URL через basePath
Без префикса /trade-in клиентский fetch шёл на gendsgn.ru/api/v1/...
который Caddy маршрутизировал в main gendesign backend (не tradein).
Теперь NEXT_PUBLIC_API_BASE_URL fallback на NEXT_PUBLIC_BASE_PATH —
build с BASE_PATH=/trade-in корректно префиксит запросы.
2026-05-21 01:13:46 +03:00
lekss361
5082f50d53 fix(tradein): убрать basePath redirect-loop
Под basePath=/trade-in next.config redirects() + app/page.tsx с
redirect(/trade-in) делали /trade-in/ → /trade-in/trade-in (двойной
префикс basePath) → пустая страница / 404.

- Перенёс контент app/trade-in/page.tsx → app/page.tsx (теперь корень
  Next = /trade-in/ snaружи)
- Удалил app/trade-in/ как избыточный
- Удалил redirects() блок из next.config.ts
- router.replace("/?id=...") вместо "/trade-in?id=..." — basePath
  префиксит автоматически
2026-05-21 01:05:49 +03:00
lekss361
9947cd5537 fix(tradein): commit empty frontend/public via .gitkeep
Dockerfile делает COPY --from=builder /app/public ./public — без папки билд падает 'public: not found' (gitignore прячет tradein.html, других файлов нет → git не сохранил пустую папку).
2026-05-21 00:59:13 +03:00
lekss361
02267d0306 feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in)
- FastAPI backend: PostGIS estimator + 3 scrapers (Avito/Cian/Yandex)
- Next.js 15 frontend: tradein.html mockup design, basePath=/trade-in
- WeasyPrint PDF (Брусника-style 4-page report)
- Address autocomplete с typo-tolerance + 6 EKB presets
- Изолированный docker stack gendesign-tradein (отдельная postgres БД)
- Caddy inline routes: gendsgn.ru/trade-in/* и /trade-in/api/v1/*
- Forgejo Actions: .forgejo/workflows/deploy-tradein.yml (shell-based GHCR login)
- Триггер только по paths: tradein-mvp/** (не пересекается с deploy.yml)
- Образы: ghcr.io/lekss361/gendesign-tradein-{backend,frontend}:latest

Первый запуск на сервере (вручную, один раз):
  - создать /opt/gendesign/tradein-mvp/.env.runtime (postgres pwd, contact email)
  - docker network create gendesign_shared (если нет)
  - docker compose -p gendesign-tradein up -d
  - docker compose -p gendesign exec caddy caddy reload
2026-05-21 00:25:39 +03:00