148 lines
4.2 KiB
Python
148 lines
4.2 KiB
Python
from datetime import date
|
|
|
|
from fastapi import APIRouter, BackgroundTasks, Depends, Request
|
|
from sqlalchemy import desc, select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.config import Settings, get_settings
|
|
from app.db import SessionLocal, get_db
|
|
from app.models import CrawlRun, Employee
|
|
from app.security import require_admin
|
|
from app.services.admin_data import employee_display_payload, list_employees_page, run_detail_payload, run_payload, stats_payload
|
|
from app.services.crawl_control import get_running_run, run_crawl_if_idle
|
|
from app.version import BACKEND_VERSION, FRONTEND_VERSION
|
|
|
|
router = APIRouter(prefix="/api")
|
|
|
|
|
|
@router.get("/health")
|
|
def health() -> dict:
|
|
return {"status": "ok", "backend_version": BACKEND_VERSION, "frontend_version": FRONTEND_VERSION}
|
|
|
|
|
|
@router.get("/employees")
|
|
def list_employees(
|
|
request: Request,
|
|
status: str | None = None,
|
|
q: str | None = None,
|
|
started_from: date | None = None,
|
|
started_to: date | None = None,
|
|
has_email: bool | None = None,
|
|
sort: str = "full_name",
|
|
direction: str = "asc",
|
|
limit: int = 50,
|
|
offset: int = 0,
|
|
db: Session = Depends(get_db),
|
|
settings: Settings = Depends(get_settings),
|
|
) -> dict:
|
|
require_admin(request, settings)
|
|
return list_employees_page(
|
|
db,
|
|
status=status,
|
|
q=q,
|
|
started_from=started_from,
|
|
started_to=started_to,
|
|
has_email=has_email,
|
|
sort=sort,
|
|
direction=direction,
|
|
limit=limit,
|
|
offset=offset,
|
|
)
|
|
|
|
|
|
@router.get("/employees/{employee_id}")
|
|
def get_employee(
|
|
employee_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
settings: Settings = Depends(get_settings),
|
|
) -> dict:
|
|
require_admin(request, settings)
|
|
employee = db.get(Employee, employee_id)
|
|
if not employee:
|
|
return {"error": "not_found"}
|
|
return _employee_detail(employee)
|
|
|
|
|
|
@router.get("/crawl-runs")
|
|
def list_crawl_runs(
|
|
request: Request,
|
|
limit: int = 20,
|
|
db: Session = Depends(get_db),
|
|
settings: Settings = Depends(get_settings),
|
|
) -> dict:
|
|
require_admin(request, settings)
|
|
runs = db.scalars(select(CrawlRun).order_by(desc(CrawlRun.started_at)).limit(limit)).all()
|
|
return {"items": [run_payload(run) for run in runs]}
|
|
|
|
|
|
@router.get("/crawl-runs/latest")
|
|
def latest_crawl_run(
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
settings: Settings = Depends(get_settings),
|
|
) -> dict:
|
|
require_admin(request, settings)
|
|
running = get_running_run(db)
|
|
latest = db.scalar(select(CrawlRun).order_by(desc(CrawlRun.started_at)).limit(1))
|
|
return {"running": run_payload(running), "latest": run_payload(latest)}
|
|
|
|
|
|
@router.get("/crawl-runs/{run_id}")
|
|
def get_crawl_run(
|
|
run_id: int,
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
settings: Settings = Depends(get_settings),
|
|
) -> dict:
|
|
require_admin(request, settings)
|
|
run = db.get(CrawlRun, run_id)
|
|
if not run:
|
|
return {"error": "not_found"}
|
|
return run_detail_payload(db, run) or {"error": "not_found"}
|
|
|
|
|
|
@router.post("/crawl-runs")
|
|
def trigger_crawl(
|
|
request: Request,
|
|
background_tasks: BackgroundTasks,
|
|
db: Session = Depends(get_db),
|
|
settings: Settings = Depends(get_settings),
|
|
) -> dict:
|
|
require_admin(request, settings)
|
|
running = get_running_run(db)
|
|
if running:
|
|
return {"status": "already_running", "run": run_payload(running)}
|
|
|
|
def _crawl() -> None:
|
|
with SessionLocal() as db:
|
|
run_crawl_if_idle(db, settings)
|
|
|
|
background_tasks.add_task(_crawl)
|
|
return {"status": "scheduled"}
|
|
|
|
|
|
@router.get("/stats")
|
|
def stats(
|
|
request: Request,
|
|
db: Session = Depends(get_db),
|
|
settings: Settings = Depends(get_settings),
|
|
) -> dict:
|
|
require_admin(request, settings)
|
|
return stats_payload(db)
|
|
|
|
|
|
def _employee_summary(employee: Employee) -> dict:
|
|
return employee_display_payload(employee)
|
|
|
|
|
|
def _employee_detail(employee: Employee) -> dict:
|
|
data = _employee_summary(employee)
|
|
data["current_data"] = employee.current_data
|
|
data["tabs"] = [{"title": tab.title, "href": tab.href, "data_index": tab.data_index} for tab in employee.tabs]
|
|
return data
|
|
|
|
|
|
def _run_summary(run: CrawlRun) -> dict:
|
|
return run_payload(run) or {}
|