Initial commit: Debian Manager — управление Debian-серверами
- SSH/SFTP управление без агентов - Пакеты (apt), Docker, файлы, systemd, ufw, cron - Плейбуки, групповые операции, планировщик - Бэкапы, отчёты, Portainer, Proxmox VE, Gitea Deploy - Терминал, Telegram-уведомления, тёмная/светлая тема
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Очередь длинных задач с журналом."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime, timezone
|
||||
from typing import Awaitable, Callable, Coroutine, List, Optional
|
||||
|
||||
from app.db.database import get_session
|
||||
from app.db.models import TaskRecord
|
||||
|
||||
LogCallback = Callable[[str], None]
|
||||
|
||||
|
||||
class TaskManager:
|
||||
def __init__(self):
|
||||
self._running: dict[int, asyncio.Task] = {}
|
||||
|
||||
def start(self, kind: str, title: str, server_id: Optional[int],
|
||||
coro_factory: Callable[[LogCallback], Coroutine],
|
||||
on_log: Optional[LogCallback] = None,
|
||||
on_done: Optional[Callable[[TaskRecord], None]] = None) -> int:
|
||||
"""Запустить задачу. coro_factory(log) — корутина, пишущая строки в log."""
|
||||
with get_session() as s:
|
||||
rec = TaskRecord(kind=kind, title=title, server_id=server_id, status="running")
|
||||
s.add(rec)
|
||||
s.commit()
|
||||
s.refresh(rec)
|
||||
task_id = rec.id
|
||||
|
||||
def log(line: str) -> None:
|
||||
with get_session() as s:
|
||||
r = s.get(TaskRecord, task_id)
|
||||
if r:
|
||||
r.log += line + "\n"
|
||||
s.add(r)
|
||||
s.commit()
|
||||
if on_log:
|
||||
on_log(line)
|
||||
|
||||
async def runner():
|
||||
status = "success"
|
||||
try:
|
||||
await coro_factory(log)
|
||||
except asyncio.CancelledError:
|
||||
status = "cancelled"
|
||||
log("[отменено пользователем]")
|
||||
except Exception as e:
|
||||
status = "error"
|
||||
log(f"[ОШИБКА] {e}")
|
||||
with get_session() as s:
|
||||
r = s.get(TaskRecord, task_id)
|
||||
if r:
|
||||
r.status = status
|
||||
r.finished_at = datetime.now(timezone.utc)
|
||||
s.add(r)
|
||||
s.commit()
|
||||
s.refresh(r)
|
||||
if on_done:
|
||||
on_done(r)
|
||||
|
||||
self._running[task_id] = asyncio.ensure_future(runner())
|
||||
return task_id
|
||||
|
||||
def cancel(self, task_id: int) -> None:
|
||||
t = self._running.get(task_id)
|
||||
if t and not t.done():
|
||||
t.cancel()
|
||||
|
||||
def get(self, task_id: int) -> Optional[TaskRecord]:
|
||||
with get_session() as s:
|
||||
return s.get(TaskRecord, task_id)
|
||||
|
||||
def list_recent(self, limit: int = 50) -> List[TaskRecord]:
|
||||
from sqlmodel import select
|
||||
with get_session() as s:
|
||||
return list(s.exec(
|
||||
select(TaskRecord).order_by(TaskRecord.id.desc()).limit(limit)
|
||||
).all())
|
||||
|
||||
|
||||
tasks = TaskManager()
|
||||
Reference in New Issue
Block a user