feat: v1.1.0 — backup module, IPAM node binding, HA, task queue, theme CSS

- Backup module: full CRUD for storages, backup files, scheduled jobs (APScheduler)
- Proxmox client: storage/backup/restore/HA methods
- IPAM: proxmox_node field, server+node binding for networks and allocations
- Task queue service: improvements and new task types
- Monitoring worker: extended metrics and alerts
- HA support: add/remove VMs from Proxmox HA groups
- tasks.html: new task tracking page
- proxmox_vms.html: major UI update (bulk ops, HA, snapshots)
- backups.html: full backup management UI (3 tabs)
- base.html: sidebar backup link, websocket manager
- theme.css: full CSS variables theme, 395 lines added
- migrations: migration #15 (BackupJob), #16 (proxmox_node)
- RBAC: backup resource permissions
- websocket_manager.py: new module for WS connections
This commit is contained in:
markmorado
2026-03-08 18:29:29 +01:00
parent 2c8becc6d4
commit b3db346cfb
21 changed files with 3676 additions and 93 deletions

View File

@@ -18,10 +18,22 @@ try:
from backend.app.db import SessionLocal
from backend.app.models import TaskQueue, ProxmoxServer, User
from backend.app.proxmox_client import ProxmoxClient
from backend.app.websocket_manager import broadcast_task_update
except ImportError:
from app.db import SessionLocal
from app.models import TaskQueue, ProxmoxServer, User
from app.proxmox_client import ProxmoxClient
from app.websocket_manager import broadcast_task_update
def _ws_broadcast(task: TaskQueue, event: str = "task_update") -> None:
"""Fire-and-forget WS broadcast for a TaskQueue update."""
try:
data = task.to_dict()
data["kind"] = "bulk"
broadcast_task_update(task.user_id, event, data)
except Exception as e:
logger.debug(f"[TASK QUEUE] WS broadcast skipped: {e}")
class TaskQueueService:
@@ -114,6 +126,7 @@ class TaskQueueService:
task.completed_at = utcnow()
db.commit()
_ws_broadcast(task, "task_update")
logger.info(f"[TASK QUEUE] Task #{task_id} cancelled by user {user_id}")
return True
@@ -220,6 +233,7 @@ class TaskQueueProcessor:
task.started_at = utcnow()
db.commit()
_ws_broadcast(task, "task_update")
logger.info(f"[TASK QUEUE] Starting task #{task.id}: {task.task_type}")
# Determine action from task type
@@ -276,12 +290,14 @@ class TaskQueueProcessor:
# Update progress
task.results = results
db.commit()
_ws_broadcast(task, "task_update")
# Mark as completed
task.status = 'completed'
task.completed_at = utcnow()
db.commit()
_ws_broadcast(task, "task_update")
logger.info(f"[TASK QUEUE] Completed task #{task.id}: {task.completed_items} success, {task.failed_items} failed")
except Exception as e:
@@ -291,6 +307,7 @@ class TaskQueueProcessor:
task.error_message = str(e)
task.completed_at = utcnow()
db.commit()
_ws_broadcast(task, "task_update")
except Exception:
pass
finally: