updates :)
This commit is contained in:
+74
-29
@@ -2,7 +2,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import secrets
|
||||
import uuid
|
||||
from typing import Annotated, Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
@@ -10,7 +9,6 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from pydantic import BaseModel
|
||||
|
||||
from admin.models import (
|
||||
activate_flow_config,
|
||||
delete_flow_config,
|
||||
get_flow_config,
|
||||
get_stats,
|
||||
@@ -56,7 +54,6 @@ async def stats(_: Auth) -> dict[str, object]:
|
||||
|
||||
class FlowConfigIn(BaseModel):
|
||||
name: str
|
||||
is_active: bool = False
|
||||
search_texts: list[str] = []
|
||||
search_box_selector: str = ""
|
||||
search_box_selector_type: str = "css"
|
||||
@@ -83,7 +80,6 @@ class FlowConfigIn(BaseModel):
|
||||
def _to_db_dict(body: FlowConfigIn) -> dict[str, Any]:
|
||||
return {
|
||||
"name": body.name,
|
||||
"is_active": int(body.is_active),
|
||||
"search_texts_json": body.search_texts,
|
||||
"search_box_selector": body.search_box_selector,
|
||||
"search_box_selector_type": body.search_box_selector_type,
|
||||
@@ -138,15 +134,6 @@ async def update_config(cfg_id: int, body: FlowConfigIn, _: Auth) -> dict[str, o
|
||||
return row or {} # type: ignore[return-value]
|
||||
|
||||
|
||||
@router.post("/api/flow-configs/{cfg_id}/activate", status_code=200)
|
||||
async def activate_config(cfg_id: int, _: Auth) -> dict[str, object]:
|
||||
existing = await get_flow_config(cfg_id)
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Not found")
|
||||
await activate_flow_config(cfg_id)
|
||||
return {"activated": cfg_id}
|
||||
|
||||
|
||||
@router.delete("/api/flow-configs/{cfg_id}", status_code=204)
|
||||
async def delete_config(cfg_id: int, _: Auth) -> None:
|
||||
existing = await get_flow_config(cfg_id)
|
||||
@@ -159,32 +146,42 @@ async def delete_config(cfg_id: int, _: Auth) -> None:
|
||||
# Batch runs (managed via Huey task queue)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class BatchRunIn(BaseModel):
|
||||
class BatchTaskIn(BaseModel):
|
||||
config_id: int
|
||||
workers: int = 3
|
||||
total_runs: int = 10
|
||||
stagger_ms: int = 500
|
||||
headless: bool = True
|
||||
|
||||
|
||||
@router.post("/api/run-batch", status_code=202)
|
||||
async def start_batch(body: BatchRunIn, _: Auth) -> dict[str, object]:
|
||||
from crawler.tasks import batch_status as _status, run_batch_task, set_current
|
||||
@router.post("/api/batch-queue", status_code=202)
|
||||
async def add_to_batch_queue(body: BatchTaskIn, _: Auth) -> dict[str, object]:
|
||||
from crawler.tasks import enqueue
|
||||
|
||||
current = _status()
|
||||
if current.get("running"):
|
||||
raise HTTPException(status_code=409, detail="A batch is already running")
|
||||
cfg = await get_flow_config(body.config_id)
|
||||
if not cfg:
|
||||
raise HTTPException(status_code=404, detail=f"Flow config {body.config_id} not found")
|
||||
|
||||
from admin.models import get_active_flow_config
|
||||
active_cfg = await get_active_flow_config()
|
||||
if not active_cfg:
|
||||
raise HTTPException(status_code=422, detail="No active flow config — activate one first")
|
||||
result = enqueue(cfg, body.workers, body.total_runs, body.stagger_ms, body.headless)
|
||||
log.info("Task enqueued — config=%d runs=%d workers=%d status=%s",
|
||||
body.config_id, body.total_runs, body.workers, result["status"])
|
||||
return result
|
||||
|
||||
batch_id = uuid.uuid4().hex
|
||||
result = run_batch_task(batch_id, active_cfg, body.workers, body.total_runs, body.headless, body.stagger_ms)
|
||||
set_current(batch_id, result)
|
||||
|
||||
log.info("Batch %s enqueued — %d runs, %d workers", batch_id[:8], body.total_runs, body.workers)
|
||||
return {"batch_id": batch_id, "status": "queued"}
|
||||
@router.delete("/api/batch-queue/{queue_id}", status_code=200)
|
||||
async def remove_from_batch_queue(queue_id: str, _: Auth) -> dict[str, object]:
|
||||
from crawler.tasks import dequeue
|
||||
removed = dequeue(queue_id)
|
||||
if not removed:
|
||||
raise HTTPException(status_code=404, detail="Queue item not found")
|
||||
return {"removed": queue_id}
|
||||
|
||||
|
||||
@router.post("/api/batch-queue/clear", status_code=200)
|
||||
async def clear_batch_queue(_: Auth) -> dict[str, object]:
|
||||
from crawler.tasks import clear_queue
|
||||
count = clear_queue()
|
||||
return {"cleared": count}
|
||||
|
||||
|
||||
@router.post("/api/stop-batch", status_code=200)
|
||||
@@ -200,3 +197,51 @@ async def stop_batch(_: Auth) -> dict[str, object]:
|
||||
async def batch_status(_: Auth) -> dict[str, object]:
|
||||
from crawler.tasks import batch_status as _status
|
||||
return _status() # type: ignore[return-value]
|
||||
|
||||
|
||||
@router.get("/api/batch-runs")
|
||||
async def list_batch_runs(_: Auth) -> list[dict[str, object]]:
|
||||
from db.repositories.batch_runs import batch_runs_repo
|
||||
return await batch_runs_repo.list() # type: ignore[return-value]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Log viewer
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@router.get("/api/logs")
|
||||
def get_logs(
|
||||
_: Auth,
|
||||
n: int = 200,
|
||||
level: str = "",
|
||||
q: str = "",
|
||||
) -> dict[str, object]:
|
||||
import os
|
||||
from logger import _LOG_FILE
|
||||
|
||||
path = _LOG_FILE
|
||||
if not os.path.exists(path):
|
||||
return {"lines": [], "file": path}
|
||||
|
||||
with open(path, encoding="utf-8", errors="replace") as f:
|
||||
all_lines = f.readlines()
|
||||
|
||||
all_lines.reverse() # newest first
|
||||
|
||||
level_upper = level.upper()
|
||||
q_lower = q.lower()
|
||||
|
||||
results: list[str] = []
|
||||
for raw in all_lines:
|
||||
line = raw.rstrip()
|
||||
if not line:
|
||||
continue
|
||||
if level_upper and level_upper not in line:
|
||||
continue
|
||||
if q_lower and q_lower not in line.lower():
|
||||
continue
|
||||
results.append(line)
|
||||
if len(results) >= n:
|
||||
break
|
||||
|
||||
return {"lines": results, "file": path}
|
||||
|
||||
Reference in New Issue
Block a user