import html import json from urllib.parse import quote from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import HTMLResponse, RedirectResponse from app.auth import require_user from app.db.audit import log_audit_event from app.db.jobs import cancel_job, get_job, get_job_logs, get_jobs, retry_failed_job from app.routes.deployments import calculate_duration, render_status_pill from app.templates.layout import page router = APIRouter() LIVE_STATUSES = {"queued", "running", "cancelled_requested"} JOB_STATUSES = {"queued", "running", "cancelled_requested", "cancelled", "success", "failed"} def render_job_status(status: str | None) -> str: status_value = status or "" normalized = status_value.lower() if normalized not in JOB_STATUSES: return render_status_pill(status) if normalized == "success": class_name = "pill pill-success" elif normalized in {"queued", "running", "cancelled_requested"}: class_name = "pill pill-warning" elif normalized in {"failed", "cancelled"}: class_name = "pill pill-danger" else: class_name = "pill pill-muted" return f'{html.escape(status_value)}' 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("/jobs", response_class=HTMLResponse) def jobs_page(request: Request, user=Depends(require_user)): jobs = get_jobs() refresh = "" if any( (job.get("status") or "").lower() in LIVE_STATUSES for job in jobs ) else "" rows = "" for job in jobs: job_id = html.escape(str(job.get("id", ""))) status = render_job_status(job.get("status")) source = html.escape(job.get("source", "") or "") target_type = html.escape(job.get("target_type", "") or "") target_id = html.escape(job.get("target_id", "") or "") job_type = html.escape(job.get("type", "") or "") created_by = html.escape(job.get("created_by_display_name") or job.get("created_by_username") or "") created_at = html.escape(job.get("created_at", "") or "") started_at = html.escape(job.get("started_at", "") or "") finished_at = html.escape(job.get("finished_at", "") or "") row_class = ' class="running-row"' if (job.get("status") or "").lower() == "running" else "" rows += f"""
Fronta portálových a webhook úloh připravená pro centrální worker.
| ID | Status | Source | Target | Created by | Created at | Started at | Finished at | Akce |
|---|
{message}
Zatím nejsou uložené žádné logy.
' return page( title, f""" {refresh}| Typ | {html.escape(job.get("type", "") or "")} |
|---|---|
| Status | {status} |
| Source | {html.escape(job.get("source", "") or "")} |
| Target | {html.escape(target_type)}: {target_id_html} |
| Created by | {html.escape(job.get("created_by_display_name") or job.get("created_by_username") or "")} |
| Created at | {html.escape(job.get("created_at", "") or "")} |
| Started at | {html.escape(job.get("started_at", "") or "")} |
| Finished at | {html.escape(job.get("finished_at", "") or "")} |
| Duration | {duration} |
| Worker | {html.escape(job.get("worker_id", "") or "")} |
{payload}
{result}
{error_text}