Add HTML5 operations monitor
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-16 21:53:56 +03:00
parent c3193b8211
commit 84303f208b
3 changed files with 122 additions and 0 deletions
@@ -70,6 +70,54 @@ def render_html5_project_rows(projects: Iterable[object]) -> str:
return project_rows
def render_html5_operations(jobs: Iterable[object]) -> str:
job_list = list(jobs)
return _page(
"SFERA HTML5 operations",
f"""
<main class="shell" data-html5-page="operations">
<section class="hero">
<div>
<p class="eyebrow">SFERA HTML5</p>
<h1>Операции сервера</h1>
<p class="lead">Очередь фоновых задач отрисовывается API-сервером и обновляется htmx polling без React runtime.</p>
</div>
<div class="hero-metrics">
<strong>{len(job_list)}</strong>
<span>jobs</span>
</div>
</section>
<section class="band">
<div class="section-title">
<h2>Очередь</h2>
<a class="button" href="/html5">Проекты</a>
</div>
<div class="table-wrap">
<table data-html5-operations>
<thead>
<tr><th>Job</th><th>Проект</th><th>Статус</th><th>Stage</th><th>Сообщение</th></tr>
</thead>
<tbody
data-html5-operations-body
hx-get="/html5/operations/jobs"
hx-trigger="every 3s"
hx-swap="innerHTML"
>{render_html5_operation_rows(job_list)}</tbody>
</table>
</div>
</section>
</main>
""",
)
def render_html5_operation_rows(jobs: Iterable[object]) -> str:
rows = "\n".join(_operation_row(job) for job in jobs)
if not rows:
return '<tr><td colspan="5" class="muted">Фоновые операции пока не запускались</td></tr>'
return rows
def render_html5_editor(
*,
project_id: str,
@@ -474,6 +522,29 @@ def _project_link(project: object, active_project_id: str) -> str:
return f'<a class="project-link{active}" href="/html5/projects/{quote(project_id)}/editor">{escape(name)}</a>'
def _operation_row(job: object) -> str:
job_id = str(getattr(job, "job_id", ""))
kind = str(getattr(job, "kind", ""))
status = _enum_text(getattr(job, "status", ""))
payload = getattr(job, "payload", {}) or {}
project_id = str(payload.get("project_id") or "")
stage = str(payload.get("stage") or "")
message = str(payload.get("message") or getattr(job, "error", "") or "")
project_link = (
f'<a href="/html5/projects/{quote(project_id)}/setup">{escape(project_id)}</a>'
if project_id
else '<span class="muted">-</span>'
)
return f"""
<tr data-html5-operation="{escape(job_id)}">
<td><strong>{escape(kind)}</strong><small>{escape(job_id)}</small></td>
<td>{project_link}</td>
<td>{escape(status)}</td>
<td>{escape(stage or "-")}</td>
<td>{escape(message or "-")}</td>
</tr>"""
def _topbar(project_id: str, project_nav: str) -> str:
return f"""
<header class="topbar" data-html5-topbar>