Commit graph

490 commits

Author SHA1 Message Date
TradeIn Deploy
34bef40ec5 feat(tradein): бэкап tradein-postgres по cron (#397)
deploy/backup-tradein-db.sh — pg_dump через docker exec → gzip,
retention последних 7 копий. Дамп с --clean --if-exists --no-owner
(самодостаточен для restore). До этого бэкапа БД не было.

Активация: добавить в crontab прод-хоста (пример — в шапке скрипта).

Closes #397
2026-05-22 11:22:30 +05:00
5e907aa7af Merge pull request 'feat(tradein): мониторинг ошибок через GlitchTip (#396)' (#410) from feat/tradein-glitchtip into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 23s
Deploy Trade-In / deploy (push) Successful in 21s
Deploy Trade-In / build-backend (push) Successful in 58s
2026-05-22 06:21:24 +00:00
TradeIn Deploy
59ad41c183 feat(tradein): мониторинг ошибок через GlitchTip (#396)
Подключаем tradein-backend к GlitchTip (Sentry-совместимый, уже на
сервере). Необработанные исключения FastAPI улетают в дашборд —
баг estimate-500 (#388/#389) был бы виден сразу с трейсбеком.

- sentry-sdk в зависимостях.
- main.py — sentry_sdk.init(), env-gated: пусто GLITCHTIP_DSN → off.
- config.glitchtip_dsn + GLITCHTIP_DSN в docker-compose.prod.yml.
- traces_sample_rate=0 (только ошибки), send_default_pii=False.

Активация: создать проект в GlitchTip → DSN в .env.runtime.
2026-05-22 11:18:26 +05:00
4c51406fda Merge pull request 'feat(tradein): CRM-поля в форме оценки (#395)' (#409) from feat/tradein-crm-fields into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-backend (push) Successful in 43s
Deploy Trade-In / build-frontend (push) Successful in 1m24s
Deploy Trade-In / deploy (push) Successful in 18s
2026-05-22 06:15:32 +00:00
TradeIn Deploy
48ccbdafc8 feat(tradein): CRM-поля в форме оценки (#395)
Доп. поля для трейд-ин менеджера: тип собственности, ипотека/
обременение, имя и телефон клиента. На расчёт оценки не влияют —
сохраняются в trade_in_estimates вместе с записью.

- data/sql/008_crm_fields.sql — 4 колонки в trade_in_estimates.
- TradeInEstimateInput + estimator INSERT — приём и сохранение.
- EstimateForm — секция «CRM — для менеджера» (4 поля).

Closes #395
2026-05-22 11:15:15 +05:00
88d2840edf Merge pull request 'feat(tradein): фронтенд загрузки фото квартиры (#394)' (#408) from feat/tradein-photo-upload-ui into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 1m24s
Deploy Trade-In / deploy (push) Successful in 17s
2026-05-22 06:11:05 +00:00
TradeIn Deploy
0a1df4d13a feat(tradein): фронтенд загрузки фото квартиры (#394)
Компонент PhotoUpload на result-view: выбор image/* (multiple),
загрузка в POST /estimate/{id}/photos, превью загруженных фото.
Multipart напрямую через fetch — apiFetch форсит JSON content-type.

Closes #394
2026-05-22 11:10:50 +05:00
a904037f95 Merge pull request 'feat(tradein): backend загрузки фото квартиры (#394)' (#407) from feat/tradein-photo-upload into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 1m10s
Deploy Trade-In / deploy (push) Successful in 20s
2026-05-22 06:06:10 +00:00
TradeIn Deploy
5413cce953 feat(tradein): backend загрузки фото квартиры (#394)
Фото квартиры прикрепляются к оценке, хранятся в Postgres (bytea) —
без отдельного файлового тома, уезжают вместе с бэкапом БД.

- data/sql/007_estimate_photos.sql — таблица estimate_photos
  (FK на trade_in_estimates, ON DELETE CASCADE).
- POST /estimate/{id}/photos — multipart-загрузка (image/*, ≤10 МБ,
  ≤12 фото на оценку).
- GET  /estimate/{id}/photos — список метаданных.
- GET  /estimate/{id}/photos/{photo_id} — отдать содержимое.
- python-multipart добавлен в зависимости (FastAPI UploadFile).

Фронтенд (file-drop в форме) — отдельным PR.
2026-05-22 11:05:35 +05:00
c86f2e5a72 Merge pull request 'feat(tradein): кадастр зданий как источник house_metadata (#393)' (#406) from feat/tradein-cadastre-enrichment into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 23s
Deploy Trade-In / build-backend (push) Successful in 40s
Deploy Trade-In / deploy (push) Successful in 15s
2026-05-22 05:55:05 +00:00
TradeIn Deploy
9cc8ab96f7 feat(tradein): кадастр зданий как источник house_metadata (#393)
В gendesign-БД есть cad_buildings — ~36к зданий ЕКБ с годом постройки
и этажностью (Росреестр). Подключаем как ПРИОРИТЕТНЫЙ источник
обогащения house_metadata, OSM Overpass остаётся fallback-ом.

- data/sql/006_cad_buildings.sql — таблица cad_buildings в tradein-БД.
- deploy/import-cadastre.sh — ETL: ЕКБ-срез cad_buildings из
  gendesign-postgres → tradein (по образцу import-rosreestr.sh).
- house_metadata._cad_buildings_get — lookup по гео-близости (60 м),
  проверяется ПЕРВЫМ в get_house_metadata, до OSM.

cad_buildings — кадастр ЗДАНИЙ, не квартир: поле ручного ввода кадастра
(исходная идея #393) не делаем — гео-обогащение полезнее и работает
автоматически для каждой оценки.

Closes #393
2026-05-22 10:54:42 +05:00
64c6759002 Merge pull request 'feat(tradein): обогащение house_metadata через OSM Overpass (#392)' (#405) from feat/tradein-house-metadata-osm into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 45s
Deploy Trade-In / deploy (push) Successful in 15s
2026-05-22 05:40:13 +00:00
TradeIn Deploy
bf2886509d feat(tradein): обогащение house_metadata через OSM Overpass (#392)
Когда пользователь не указал год постройки / тип дома, estimator
подтягивает их из OpenStreetMap (Overpass API) — building:levels,
start_date, building:material. Улучшает house-match аналогов (#6).

- app/services/house_metadata.py — Overpass-провайдер + кэш в таблице
  house_metadata (TTL 30 дней, lookup по близости координат, 40 м).
- estimator.estimate_quality — обогащает target_year / target_house_type
  перед _fetch_analogs. Best-effort: ошибка OSM → оценка без обогащения.

OSM по ЕКБ: building:levels населён хорошо, год постройки — частично.

Closes #392
2026-05-22 10:39:56 +05:00
4dc173c15b Merge pull request 'chore(security): убрать sshkey.txt из репозитория (#391)' (#404) from chore/untrack-sshkey-secrets into main 2026-05-22 05:29:10 +00:00
TradeIn Deploy
6f372402be chore(security): убрать sshkey.txt из репозитория (#391)
sshkey.txt содержит открытым текстом GHCR-PAT, пароли БД, ADMIN_TOKEN,
пароль Obsidian. git rm --cached снимает файл с отслеживания (локальная
копия остаётся) + добавлен в .gitignore.

Частично закрывает #391. Остаётся (требует ручных действий):
- ротация всех засвеченных секретов;
- чистка истории git (filter-repo) — секреты остаются в старых коммитах.
2026-05-22 10:28:23 +05:00
3b2850b91b Merge pull request 'fix(tradein): cron-scrape подхватывает .env.runtime (#390)' (#403) from fix/tradein-cron-scrape-env into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-backend (push) Successful in 23s
Deploy Trade-In / build-frontend (push) Successful in 24s
Deploy Trade-In / deploy (push) Successful in 16s
2026-05-22 05:23:33 +00:00
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
e170dddd24 Merge pull request 'fix(tradein): estimate 500 — AmbiguousParameter на NULL-параметрах' (#389) from fix/tradein-estimate-ambiguous-param into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 40s
Deploy Trade-In / deploy (push) Successful in 16s
2026-05-22 05:03:53 +00: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
137c8bbb08 Merge pull request 'fix(tradein): estimate 500 — алиас distance_m в ORDER BY' (#388) from fix/tradein-estimate-orderby-alias into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 39s
Deploy Trade-In / deploy (push) Successful in 15s
2026-05-21 17:40:24 +00: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
cae12fe6c3 Merge pull request 'feat(tradein): прогноз срока продажи + rate-limit (#9,#16)' (#387) from feat/tradein-dom-ratelimit into main
All checks were successful
Deploy Trade-In / deploy (push) Successful in 15s
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 44s
2026-05-21 17:13:08 +00:00
TradeIn Deploy
63c96628e3 feat(tradein): прогноз срока продажи + rate-limit (#9,#16) 2026-05-21 20:12:49 +03:00
c3a9582957 Merge pull request 'feat(tradein): geocode-missing для deals (#4)' (#386) from feat/tradein-geocode-deals into main
All checks were successful
Deploy Trade-In / deploy (push) Successful in 16s
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Successful in 25s
Deploy Trade-In / build-backend (push) Successful in 45s
2026-05-21 17:04:35 +00: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
9ac4f35abc Merge pull request 'feat(tradein): house-match аналогов + поправка на ремонт (#6,#7)' (#385) from feat/tradein-house-match-repair into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 48s
Deploy Trade-In / deploy (push) Successful in 20s
2026-05-21 17:01:03 +00: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
8383ec0ed2 Merge pull request 'feat(tradein): импорт реальных сделок Росреестра (#4)' (#384) from feat/tradein-rosreestr-import into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-backend (push) Successful in 23s
Deploy Trade-In / build-frontend (push) Successful in 23s
Deploy Trade-In / deploy (push) Successful in 15s
2026-05-21 16:57:36 +00: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
6270bb91f1 Merge pull request 'feat(tradein): auth на admin-эндпоинтах (#2)' (#383) from feat/tradein-admin-auth into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 23s
Deploy Trade-In / build-backend (push) Successful in 38s
Deploy Trade-In / deploy (push) Successful in 15s
2026-05-21 16:47:18 +00: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
0b9bf55ba0 Merge pull request 'fix(tradein): чанковый геокодинг — cron не таймаутится (#1)' (#382) from fix/tradein-geocode-chunked into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 26s
Deploy Trade-In / build-backend (push) Successful in 44s
Deploy Trade-In / deploy (push) Successful in 16s
2026-05-21 16:43:46 +00: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
8c7a628d17 Merge pull request 'feat(tradein): пагинация Циан/N1 3→8 страниц' (#381) from feat/tradein-deep-pagination into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 43s
Deploy Trade-In / deploy (push) Successful in 16s
2026-05-21 11:34:45 +00:00
TradeIn Deploy
7cc8d8370d feat(tradein): пагинация Циан/N1 3→8 страниц — плотность данных
После перехода на реальные координаты стало видно что данных тонко.
8 страниц × 4 комнаты ≈ 900+ лотов на источник вместо ~300.
2026-05-21 14:34:29 +03:00
dbe0bbebff Merge pull request 'fix(tradein): убрать TRUNCATE из seed — деплой больше не wipe-ит данные' (#380) from fix/tradein-seed-truncate into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 26s
Deploy Trade-In / deploy (push) Successful in 15s
2026-05-21 10:19:40 +00: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
1d4b720991 Merge pull request 'fix(tradein): реальные координаты Cian/N1 через геокодинг' (#379) from fix/tradein-real-coords into main
All checks were successful
Deploy Trade-In / build-backend (push) Successful in 45s
Deploy Trade-In / deploy (push) Successful in 16s
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 28s
2026-05-21 10:14:48 +00: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
362b1c1e15 Merge pull request 'feat(tradein): радиус 1000м + пагинация Циан/N1' (#378) from feat/tradein-radius-pagination into main
All checks were successful
Deploy Trade-In / build-backend (push) Successful in 10m59s
Deploy Trade-In / deploy (push) Successful in 15s
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
2026-05-21 08:59:37 +00: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
2ecc26f63b Merge pull request 'feat(tradein): N1.ru scraper + cron для 4 источников' (#377) from feat/tradein-n1-scraper into main
All checks were successful
Deploy Trade-In / build-backend (push) Successful in 43s
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 26s
Deploy Trade-In / deploy (push) Successful in 16s
2026-05-21 08:44:46 +00: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
db36b6db91 Merge pull request 'fix(tradein): HOSTNAME=0.0.0.0 + NEXT_PUBLIC_API_BASE_URL build-arg' (#376) from fix/tradein-hostname-502 into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 1m27s
Deploy Trade-In / deploy (push) Successful in 17s
Deploy Trade-In / build-backend (push) Successful in 24s
2026-05-21 07:37:16 +00: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
0a649850a0 Merge pull request 'fix(tradein): PDF-ссылка через API_BASE_URL (basePath)' (#375) from fix/tradein-pdf-link-basepath into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Successful in 1m27s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / deploy (push) Successful in 15s
2026-05-21 07:16:11 +00: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
259f5ece4d Merge pull request 'fix(tradein): Caddy strip только /trade-in для API' (#374) from fix/tradein-caddy-api-strip into main
All checks were successful
Deploy / build-worker (push) Successful in 25s
Deploy / deploy (push) Successful in 48s
Deploy / changes (push) Successful in 4s
Deploy / build-backend (push) Successful in 27s
Deploy / build-frontend (push) Successful in 27s
2026-05-20 22:17:54 +00:00
lekss361
2ce29973f9 fix(tradein): Caddy strip только /trade-in для API (не /trade-in/api)
handle_path /trade-in/api/* стрипал /trade-in/api целиком — FastAPI
получал /v1/trade-in/estimate и возвращал 404. FastAPI router на
/api/v1/trade-in/*, нужен strip только префикса basePath /trade-in.
2026-05-21 01:17:24 +03:00
ba2afedf58 Merge pull request 'fix(tradein): префиксить API_BASE_URL через basePath' (#373) from fix/tradein-api-basepath into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 1m27s
Deploy Trade-In / deploy (push) Successful in 16s
2026-05-20 22:14:10 +00:00