feat: bulk validator tab + status/niche/type browse filters

- New app/validator.py: background HTTP checker for entire dataset
  - 50 concurrent checks, skips already-validated domains
  - Extracts prescreen_status, server, IP, load_time_ms
  - start/stop/status API at /api/validator/start|stop|status

- New dedicated "Validator 🔬" tab with stats grid, TLD filter,
  Start/Stop controls, live progress indicator

- Browse tab: "Live" column replaced with "Status" dot (color-coded
  ● from prescreen_status, falls back to is_live)
- Browse tab: new Status / Niche / Type filter dropdowns

- db.py: added ip TEXT + load_time_ms INTEGER columns + migrations;
  get_enriched() supports prescreen_status/niche/site_type filters

- main.py: /api/enriched extended with prescreen_status/niche/site_type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 08:27:24 +02:00
parent 468d76387d
commit 8f387cada2
4 changed files with 450 additions and 14 deletions

View File

@@ -22,6 +22,7 @@ from app.db import (
)
from app.enricher import start_worker, pause_worker, resume_worker, is_running, ensure_workers_alive
from app.scorer import run_scoring
from app.validator import start_validator, stop_validator, get_validator_status
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger(__name__)
@@ -158,17 +159,40 @@ async def enriched(
kit_digital: Optional[bool] = Query(None),
ai_only: bool = Query(False),
lead_quality: str = Query(None),
prescreen_status: str = Query(None),
niche: str = Query(None),
site_type: str = Query(None),
page: int = Query(1, ge=1),
limit: int = Query(100, ge=1, le=1000),
):
total, rows = await get_enriched(
min_score=min_score, cms=cms, country=country,
kit_digital=kit_digital, ai_only=ai_only, lead_quality=lead_quality,
prescreen_status=prescreen_status, niche=niche, site_type=site_type,
page=page, limit=limit,
)
return {"page": page, "limit": limit, "total": total, "results": rows}
# ── Bulk Validator endpoints ──────────────────────────────────────────────────
@app.post("/api/validator/start")
async def validator_start(tld: str = Query(None)):
start_validator(tld_filter=tld or None)
return get_validator_status()
@app.post("/api/validator/stop")
async def validator_stop():
stop_validator()
return {"status": "stopped"}
@app.get("/api/validator/status")
async def validator_status():
return get_validator_status()
# ── AI assessment endpoints ───────────────────────────────────────────────────
@app.post("/api/prescreen/batch")