diff --git a/app/rest.py b/app/rest.py index 94ee843..c749a22 100644 --- a/app/rest.py +++ b/app/rest.py @@ -29,6 +29,7 @@ from app.ingest.base import load_compiled_patterns from app.ingest.tautulli import parse_webhook as _parse_tautulli from app.services.blocklist import ( BlocklistCandidate, + get_candidate, list_candidates, load_telemetry_rules, mark_pushed, @@ -591,7 +592,7 @@ def scan_blocklist(background_tasks: BackgroundTasks) -> dict: try: device_map = json.loads(raw_devices) except (ValueError, TypeError): - pass + raise HTTPException(status_code=400, detail="device_names is not valid JSON — update it in Settings") telemetry_path = PATTERN_DIR / "telemetry.yaml" telemetry_rules = load_telemetry_rules(telemetry_path) if telemetry_path.exists() else [] background_tasks.add_task(run_scan, DB_PATH, source_ids, device_map, telemetry_rules) @@ -611,9 +612,9 @@ def update_blocklist_status(candidate_id: str, body: BlocklistStatusBody) -> dic @router.post("/api/blocklist/push/{candidate_id}") def push_to_pihole(candidate_id: str) -> dict: - candidates = list_candidates(DB_PATH) - candidate = next((c for c in candidates if c.id == candidate_id), None) - if candidate is None: + try: + candidate = get_candidate(DB_PATH, candidate_id) + except KeyError: raise HTTPException(status_code=404, detail="Candidate not found") if candidate.status != "approved": raise HTTPException( @@ -628,9 +629,9 @@ def push_to_pihole(candidate_id: str) -> dict: @router.delete("/api/blocklist/push/{candidate_id}") def unblock_from_pihole(candidate_id: str) -> dict: - candidates = list_candidates(DB_PATH) - candidate = next((c for c in candidates if c.id == candidate_id), None) - if candidate is None: + try: + candidate = get_candidate(DB_PATH, candidate_id) + except KeyError: raise HTTPException(status_code=404, detail="Candidate not found") if candidate.status != "pushed": raise HTTPException( diff --git a/app/services/blocklist.py b/app/services/blocklist.py index 513a778..5801537 100644 --- a/app/services/blocklist.py +++ b/app/services/blocklist.py @@ -253,6 +253,15 @@ def _get_candidate(conn: sqlite3.Connection, candidate_id: str) -> BlocklistCand return _row_to_candidate(row) +def get_candidate(db_path: Path, candidate_id: str) -> BlocklistCandidate: + """Fetch a single candidate by ID. Raises KeyError if not found.""" + conn = sqlite3.connect(str(db_path)) + try: + return _get_candidate(conn, candidate_id) + finally: + conn.close() + + def update_candidate_status(db_path: Path, candidate_id: str, new_status: str) -> BlocklistCandidate: if new_status not in _VALID_STATUSES: raise ValueError(f"Invalid status {new_status!r}. Must be one of {_VALID_STATUSES}")