-- Централизованная таблица настроек для всех Celery-job'ов. -- -- Контекст: -- До этой миграции конфиг был разбросан по трём местам: -- • scrape_kn — env-переменные SCRAPE_KN_CRON / SCRAPE_KN_DEFAULT_REGIONS -- • nspd_geo — per-row rate_ms в nspd_geo_jobs (нет global default) -- • objective_etl — hardcode в settings + env OBJECTIVE_ANTON_SQLITE_PATH -- • objective_sync — уже в objective_sync_config (оставляем как deprecated fallback) -- -- Цель: единая таблица job_settings — один UI /admin/jobs/settings для всех job'ов. -- -- Зависимости: -- Нет FK на другие таблицы. objective_sync_config НЕ дропается — она помечается -- DEPRECATED и остаётся как fallback для уже задеплоенного backend-кода. -- -- Порядок применения: -- Применяется один раз на чистой или работающей БД. -- Идемпотентно (CREATE IF NOT EXISTS + INSERT ON CONFLICT DO NOTHING). -- Backend-код должен быть задеплоен ПОСЛЕ этой миграции. -- -- Проверка после применения: -- SELECT * FROM job_settings; -- должна вернуть 4 строки -- SELECT * FROM objective_sync_config; -- должна по-прежнему работать BEGIN; -- ── Создание таблицы ───────────────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS job_settings ( job_type TEXT PRIMARY KEY, -- 'scrape_kn' | 'nspd_geo' | 'objective_etl' | 'objective_sync' enabled BOOLEAN NOT NULL DEFAULT true, queue_name TEXT NOT NULL DEFAULT 'celery', -- celery (default) | scrape_kn | geo cron_schedule TEXT, -- Стандартный crontab: 'minute hour dom month dow'. NULL = только ручной запуск. rate_ms INTEGER, -- Пауза между API-запросами (мс). NULL/0 = не throttled. max_retries SMALLINT NOT NULL DEFAULT 2, max_concurrency SMALLINT NOT NULL DEFAULT 1, -- Макс. параллельных task'ов этого типа. extra_config JSONB NOT NULL DEFAULT '{}'::jsonb, -- Job-specific: {"regions":[66], "groups_csv":"...", "sqlite_path":"..."}. updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_by TEXT, description TEXT -- Человеко-читаемое описание для UI. ); COMMENT ON TABLE job_settings IS 'Centralized settings for all Celery jobs. See /admin/jobs/settings UI. ' 'Replaces scattered env vars and per-table configs.'; COMMENT ON COLUMN job_settings.queue_name IS 'Celery queue routing. Allows concurrency between job types within one worker container.'; COMMENT ON COLUMN job_settings.cron_schedule IS 'Standard crontab string (5-field: minute hour dom month dow). ' 'NULL = manual trigger only. Beat re-reads on restart.'; COMMENT ON COLUMN job_settings.rate_ms IS 'Delay between API calls in milliseconds (used by NSPD geo). NULL/0 for non-throttled jobs.'; COMMENT ON COLUMN job_settings.extra_config IS 'Job-specific JSON config: e.g. {"regions":[66,77], "groups_csv":"Свердловская область,...", ' '"sqlite_path":"/data/anton-sqlite/analysis.db"}.'; -- ── Seed данные ─────────────────────────────────────────────────────────────── -- ON CONFLICT DO NOTHING — безопасен при повторном запуске. -- Defaults взяты из: -- scrape_kn → backend/app/core/config.py (scrape_kn_cron / scrape_kn_default_regions) -- nspd_geo → nspd_geo_jobs.rate_ms most common value (600) на проде -- objective_etl → backend/app/core/config.py (objective_anton_sqlite_path) -- objective_sync → objective_sync_config.cron_schedule (prod value '0 6 * * tue') INSERT INTO job_settings (job_type, enabled, queue_name, cron_schedule, rate_ms, max_retries, max_concurrency, extra_config, description) VALUES ( 'scrape_kn', true, 'scrape_kn', '15 4 * * mon', NULL, 2, 1, '{"default_regions": [66]}'::jsonb, 'Скрейпинг КН (ЦИАН): загрузка объявлений по регионам. ' 'Расписание и регионы ранее задавались через env SCRAPE_KN_CRON / SCRAPE_KN_DEFAULT_REGIONS.' ), ( 'nspd_geo', true, 'geo', NULL, 600, 2, 1, '{}'::jsonb, 'Геокодирование объектов через НСПД/rosreestr2coord. ' 'Запускается вручную по job-конфигурациям nspd_geo_jobs. rate_ms — глобальный дефолт.' ), ( 'objective_etl', true, 'celery', NULL, NULL, 2, 1, '{"sqlite_path": "/data/anton-sqlite/analysis.db"}'::jsonb, 'ETL: SQLite Антоновского → PG (objective_* таблицы). ' 'Запускается вручную. sqlite_path ранее задавался через env OBJECTIVE_ANTON_SQLITE_PATH.' ), ( 'objective_sync', true, 'celery', '0 6 * * tue', NULL, 2, 1, '{ "groups_csv": "Свердловская область,Челябинск,Тюмень,Пермь", "use_ddu": true, "use_dkp": true, "period_months_back": 1, "inter_group_delay_s": 30, "rate_ms": 3000 }'::jsonb, 'Синхронизация DOM.РФ Objective (отчёты). ' 'Детальный конфиг дублирован в objective_sync_config (deprecated legacy fallback).' ) ON CONFLICT (job_type) DO NOTHING; -- ── Пометить objective_sync_config как deprecated ───────────────────────────── -- Таблица НЕ дропается: backend продолжает читать её как fallback до рефакторинга. COMMENT ON TABLE objective_sync_config IS 'DEPRECATED: use job_settings (job_type=''objective_sync'') for new config reads. ' 'Kept for backward compat until backend refactor. Do NOT drop until refactor is deployed.'; COMMIT;