import html import json import math from urllib.parse import quote, urlencode from fastapi import APIRouter, Depends, HTTPException, Query, Request from fastapi.responses import HTMLResponse from app.auth import require_user from app.db.audit import log_audit_event from app.db.workers import get_worker, get_workers from app.templates.layout import page router = APIRouter() DEFAULT_PAGE_SIZE = 20 def render_worker_badge(online: bool) -> str: if online: return 'online' return 'offline' def pretty_json(value: str | None) -> str: if not value: return "{}" try: return json.dumps(json.loads(value), ensure_ascii=False, indent=2, sort_keys=True) except (TypeError, ValueError): return value @router.get("/workers", response_class=HTMLResponse) def workers_page( request: Request, status: str = Query(""), q: str = Query(""), page_number: int = Query(1, alias="page", ge=1), user=Depends(require_user), ): all_workers = get_workers() selected_status = status.strip().lower() query = q.strip() online_count = sum(1 for worker in all_workers if worker.get("online")) total_count = len(all_workers) offline_count = total_count - online_count workers = all_workers if selected_status == "online": workers = [worker for worker in workers if worker.get("online")] elif selected_status == "offline": workers = [worker for worker in workers if not worker.get("online")] else: selected_status = "" if query: workers = [worker for worker in workers if query.lower() in (worker.get("id", "") or "").lower()] total_filtered = len(workers) total_pages = max(1, math.ceil(total_filtered / DEFAULT_PAGE_SIZE)) if page_number > total_pages: page_number = total_pages offset = (page_number - 1) * DEFAULT_PAGE_SIZE workers = workers[offset : offset + DEFAULT_PAGE_SIZE] log_audit_event( user, action="workers.view", target_type="workers", metadata={ "online": online_count, "offline": offline_count, "total": total_count, }, ) rows = "" for worker in workers: worker_id_raw = worker.get("id", "") or "" worker_id = html.escape(worker_id_raw) worker_url_id = quote(worker_id_raw, safe="") status = html.escape(worker.get("status", "") or "") last_seen_at = html.escape(worker.get("last_seen_at", "") or "") started_at = html.escape(worker.get("started_at", "") or "") badge = render_worker_badge(bool(worker.get("online"))) current_job_id = worker.get("current_job_id") current_job = "" if current_job_id is not None: current_job_label = html.escape(str(current_job_id)) current_job = f'#{current_job_label}' rows += f"""
Přehled worker procesů obsluhujících úlohy portálu.
| ID workeru | Status | Naposledy viděn | Aktuální úloha | Spuštěn | Akce |
|---|
{metadata}
| ID workeru | {worker_id_html} |
|---|---|
| Status | {badge} {status} |
| Naposledy viděn | {last_seen_at} |
| Aktuální úloha | {current_job} |
| Spuštěn | {started_at} |