Журнал: пагинация 30 записей на страницу, API offset/total_pages

- job_journal: collect_recent_journal_entries_page_sync(limit, offset)
- GET /api/v1/journal/recent: limit по умолчанию 30, offset, total, page, total_pages
- journal.html/js: навигация Первая/Назад/номера/Вперёд/Последняя, стили
- app/docs/api_routes.md: описание query и пример ответа
- Прочие изменения UI/API (аддоны, helm, job_journal в кластерах) в том же коммите
This commit is contained in:
Sergey Antropoff
2026-04-04 11:16:43 +03:00
parent 52538d9816
commit 17f6233fd7
22 changed files with 2811 additions and 4 deletions
+53
View File
@@ -386,34 +386,87 @@ class JobStore:
async def set_success(self, job_id: str, *, result: dict[str, Any] | None = None, message: str | None = None) -> None:
logs = take_logs_finalize_sync(job_id)
snapshot: dict[str, Any] | None = None
async with self._lock:
if job_id in self._jobs:
self._jobs[job_id].status = "success"
self._jobs[job_id].result = result
self._jobs[job_id].message = message
self._jobs[job_id].log_lines = logs
r = self._jobs[job_id]
snapshot = {
"job_id": r.job_id,
"kind": r.kind,
"cluster_name": r.cluster_name,
"status": r.status,
"message": r.message,
"created_at_utc": r.created_at_utc,
"log_lines": list(r.log_lines),
"result": dict(r.result) if r.result else None,
}
set_progress_sync(job_id, "Готово", 100)
await self._persist_async()
if snapshot and snapshot.get("cluster_name"):
from core.job_journal import append_job_from_record_sync
await asyncio.to_thread(append_job_from_record_sync, **snapshot)
async def set_failed(self, job_id: str, message: str) -> None:
logs = take_logs_finalize_sync(job_id)
# Чтобы в kind_k8s_jobs.json и в UI всегда был хотя бы текст ошибки (ранний сбой без stdout).
if not logs:
logs = [f"[ошибка] {message}"]
snapshot: dict[str, Any] | None = None
async with self._lock:
if job_id in self._jobs:
self._jobs[job_id].status = "failed"
self._jobs[job_id].message = message
self._jobs[job_id].log_lines = logs
r = self._jobs[job_id]
snapshot = {
"job_id": r.job_id,
"kind": r.kind,
"cluster_name": r.cluster_name,
"status": r.status,
"message": r.message,
"created_at_utc": r.created_at_utc,
"log_lines": list(r.log_lines),
"result": dict(r.result) if r.result else None,
}
logger.warning("Задание %s завершилось ошибкой: %s", job_id, message)
await self._persist_async()
if snapshot and snapshot.get("cluster_name"):
from core.job_journal import append_job_from_record_sync
await asyncio.to_thread(append_job_from_record_sync, **snapshot)
async def set_cancelled(self, job_id: str, message: str = "Операция отменена") -> None:
logs = take_logs_finalize_sync(job_id)
if not logs:
logs = [f"[отмена] {message}"]
snapshot: dict[str, Any] | None = None
async with self._lock:
if job_id in self._jobs:
self._jobs[job_id].status = "cancelled"
self._jobs[job_id].message = message
self._jobs[job_id].log_lines = logs
r = self._jobs[job_id]
snapshot = {
"job_id": r.job_id,
"kind": r.kind,
"cluster_name": r.cluster_name,
"status": r.status,
"message": r.message,
"created_at_utc": r.created_at_utc,
"log_lines": list(r.log_lines),
"result": dict(r.result) if r.result else None,
}
logger.info("Задание %s отменено: %s", job_id, message)
await self._persist_async()
if snapshot and snapshot.get("cluster_name"):
from core.job_journal import append_job_from_record_sync
await asyncio.to_thread(append_job_from_record_sync, **snapshot)
async def get(self, job_id: str) -> JobRecord | None:
async with self._lock: