gendesign/tradein-mvp/backend/data/sql/201_purge_dead_mobileproxy_proxies.sql
lekss361 16237573ed
All checks were successful
Deploy Trade-In / changes (push) Successful in 18s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m28s
Deploy Trade-In / test (push) Successful in 2m53s
Deploy Trade-In / build-backend (push) Successful in 28s
Deploy Trade-In / deploy (push) Successful in 1m21s
chore(tradein/proxy): выпилить мёртвые узлы mobileproxy и их ключи из пула (#2613) (#2614)
DELETE 4 мёртвых mobileproxy-узлов (id 2/3/4/5) из scrape_proxies. Подписка закрыта, узлы мертвы с 4-9 июля; у трёх в rotate_url лежал чужой API-ключ открытым текстом (источник блокера PR #2611). scrape_proxy_rotations пуста, единственный FK не мешает. Условие по домену (url LIKE mobileproxy.space), не по id.
2026-08-01 20:05:06 +00:00

83 lines
6.5 KiB
PL/PgSQL
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.

-- 201_purge_dead_mobileproxy_proxies.sql
-- Issue #2613 — выпилить мёртвые узлы mobileproxy из пула scrape_proxies
-- вместе с чужим API-ключом, который лежал у них в rotate_url.
--
-- WHY:
-- Владелец подтвердил: подписка mobileproxy закрыта, продлевать не будут.
-- Прямая проба каждого узла из контейнера tradein-scraper (2026-08-01)
-- подтверждает смерть: id 2 — connection refused, id 3/4/5 — 407 Proxy
-- Authentication Required. Последняя успешная проверка (last_check_at) у
-- всех четырёх — 4-9 июля, все четыре enabled=false, consecutive_fails=5.
--
-- Две причины удалить, вторая важнее:
-- 1. Мёртвые узлы засоряют пул и его health-метрики.
-- 2. rotate_url у трёх из четырёх строк (id 3, 4, 5) хранит открытым
-- текстом чужой ключ провайдера в query-параметре ссылки ротации
-- (https://changeip.mobileproxy.space/?proxy_key=...). Именно из-за
-- неоднородности этой колонки (вперемешку с ASocks-строками, где
-- rotate_url — наш собственный API-эндпоинт БЕЗ секрета в URL,
-- авторизация Bearer-заголовком) глубокое ревью PR #2611 нашло
-- блокер: вызов ротации для такой строки отправил бы НАШ токен
-- ASocks на changeip.mobileproxy.space. Пин хоста в #2611 уже
-- закрывает саму уязвимость, но чужой секрет в базе держать незачем.
--
-- ПОЧЕМУ DELETE, А НЕ UPDATE (очистка полей + enabled=false):
-- Единственный FK, ссылающийся на scrape_proxies — scrape_proxy_rotations
-- .proxy_id (заведён 198_scrape_proxy_rotations.sql), delete_rule NO ACTION.
-- На момент миграции (замер ниже) в scrape_proxy_rotations нет НИ ОДНОЙ
-- строки вообще — таблица введена в этом же цикле работ (#2600 п.5) и
-- ручная ротация ни разу не запускалась. DELETE четырёх строк scrape_proxies
-- ничего не упирает. Если бы к строкам 2-5 успела прилипнуть история ротаций
-- к моменту применения — DELETE упадёт по FK-violation ВНУТРИ этой же
-- транзакции (BEGIN/COMMIT ниже), миграция целиком откатится, deploy
-- завершится ошибкой (auto-apply strict, exit 1) без частичного эффекта и
-- без порчи данных; отдельного ON DELETE-обработчика не требуется — узлы
-- disabled=false уже сейчас, acquire() их не выдаёт (idx_scrape_proxies_pick
-- фильтрует по enabled), новых ротаций на них взяться неоткуда до deploy.
-- Строки — исторический мусор без ссылок, полное удаление честнее частичной
-- очистки (не оставляет призрачную запись мёртвого узла в пуле) и убирает
-- секрет из базы целиком, а не только из одной колонки.
--
-- Matching (по домену url, НЕ по id):
-- id в scrape_proxies разъезжается между средами (bulk-загрузка независима
-- per-среда, тот же класс проблемы решён в 199 через host:port-matching).
-- Условие — WHERE url LIKE '%mobileproxy.space%' — ловит все четыре узла
-- независимо от порта/поддомена (ha./gi./auv./aup.mobileproxy.space) и не
-- заденет ASocks-строки (212.8.249.134 / 190.2.145.131 / 175.110.115.153 /
-- 109.236.82.42 — IP-адреса, без mobileproxy.space в url вовсе).
--
-- ЗАМЕРЫ (SELECT, read-only, прод, перед миграцией, 2026-08-01):
-- Строк под условие (url LIKE '%mobileproxy.space%'): 4 (id 2, 3, 4, 5)
-- Остаток пула после удаления (url NOT LIKE '%mobileproxy.space%'):
-- 4 (id 1, 9, 10, 11) — все ASocks
-- Строк в scrape_proxy_rotations на id 2/3/4/5: 0
-- Строк в scrape_proxy_rotations всего (таблица пуста): 0
-- Секрет-паттерн (token|bearer|secret|key=|password, regex
-- case-insensitive) в rotate_url ОСТАЮЩИХСЯ 4 строк: 0 совпадений
-- (rotate_url остающихся — https://api.asocks.com/unlimited-proxy/
-- <portId>/refresh-ip, без query-параметров вообще, авторизация Bearer
-- заголовком вне URL, см. 199_scrape_proxies_asocks_rotate_url.sql)
-- FK, ссылающиеся на scrape_proxies: ровно один —
-- scrape_proxy_rotations.proxy_id -> scrape_proxies.id, delete_rule NO ACTION.
--
-- Idempotency:
-- Обычный DELETE ... WHERE — повторный прогон находит 0 строк (уже
-- удалены), no-op. Весь файл в BEGIN/COMMIT.
--
-- ГРАНИЦЫ: НЕ трогает ASocks-строки (id 1, 9, 10, 11) и их rotate_url. НЕ
-- трогает переменные окружения (*_PROXY_URL, BROWSER_PROXY_*,
-- *_PROXY_ROTATE_URL) — их снятие отдельная задача и НЕ раньше неё, иначе
-- при пустом прокси curl_proxy_url отдаёт None = скрапер идёт напрямую с IP
-- сервера. НЕ трогает app/services/proxy_pool.py, proxy_rotation.py,
-- скраперы. Никакого DDL.
--
-- Dependencies:
-- 157_scrape_proxies.sql (scrape_proxies.url/rotate_url/enabled).
-- 198_scrape_proxy_rotations.sql (FK proxy_id -> scrape_proxies.id, NO ACTION).
BEGIN;
DELETE FROM scrape_proxies
WHERE url LIKE '%mobileproxy.space%';
COMMIT;