fix: устранены все найденные аудитом баги и тихие падения
- SQL injection паттерн → параметризованные запросы во всех местах - except: pass/continue → logger.warning() везде, ничего не тонет молча - WAL mode + индекс domain_dedup_key в database.py - try/finally для conn в main.py, утечка соединения устранена - backoff 30с при 403/429 от Rusprofile/ЕГРЮЛ - ликвидированные компании → egrul_status="liquidated" - max_candidates в contacts_finder считает только реальных кандидатов - DB_PATH абсолютный (Path(__file__).parent), HH_PAUSE_BETWEEN_QUERIES в config - HH_SIGNAL_QUERIES дубль убран из launcher.py → импорт из config - path traversal защита в egrul_enricher debug_dump_html Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+9
-3
@@ -4,6 +4,7 @@
|
||||
Сама занимается дедупликацией: ИНН > телефон > домен.
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -12,6 +13,8 @@ from typing import Optional
|
||||
import config
|
||||
from normalization import phone_dedup_key, normalize_domain
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
# Схема таблиц
|
||||
# ───────────────────────────────────────────────────────────────────────
|
||||
@@ -119,6 +122,7 @@ CREATE INDEX IF NOT EXISTS idx_leads_score ON leads(score DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_leads_source ON leads(source);
|
||||
CREATE INDEX IF NOT EXISTS idx_leads_outreach ON leads(outreach_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_leads_has_website ON leads(has_website);
|
||||
CREATE INDEX IF NOT EXISTS idx_leads_domain ON leads(domain_dedup_key);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sources_log (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -208,6 +212,7 @@ def get_connection(db_path: str = "leads.db") -> sqlite3.Connection:
|
||||
"""Открыть соединение с включённым row_factory (sqlite3.Row для dict-доступа)."""
|
||||
conn = sqlite3.connect(db_path)
|
||||
conn.row_factory = sqlite3.Row
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
return conn
|
||||
|
||||
|
||||
@@ -748,8 +753,9 @@ def cleanup_bad_director_names(conn: sqlite3.Connection) -> int:
|
||||
"Производство", "Услуги", "Работ",
|
||||
]
|
||||
# Совпадение если первое слово в director_name — должность
|
||||
where_parts = [f"director_name LIKE '{m}%'" for m in bad_markers]
|
||||
where_parts = ["director_name LIKE ?"] * len(bad_markers)
|
||||
where_clause = " OR ".join(where_parts)
|
||||
params = [f"{m}%" for m in bad_markers]
|
||||
sql = f"""
|
||||
UPDATE leads
|
||||
SET director_name = NULL,
|
||||
@@ -757,7 +763,7 @@ def cleanup_bad_director_names(conn: sqlite3.Connection) -> int:
|
||||
egrul_status = NULL
|
||||
WHERE {where_clause}
|
||||
"""
|
||||
cursor = conn.execute(sql)
|
||||
cursor = conn.execute(sql, params)
|
||||
conn.commit()
|
||||
return cursor.rowcount
|
||||
|
||||
@@ -878,7 +884,7 @@ def get_stats(conn: sqlite3.Connection) -> dict:
|
||||
).fetchall())
|
||||
threshold = config.HOT_LEAD_THRESHOLD
|
||||
hot = conn.execute(
|
||||
f"SELECT COUNT(*) FROM leads WHERE score >= {int(threshold)}"
|
||||
"SELECT COUNT(*) FROM leads WHERE score >= ?", (int(threshold),)
|
||||
).fetchone()[0]
|
||||
with_phone = conn.execute(
|
||||
"SELECT COUNT(*) FROM leads WHERE phone_primary IS NOT NULL"
|
||||
|
||||
Reference in New Issue
Block a user