This repository has been archived on 2026-08-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Proxmox-VPS-Panel-secure/backend/app/routers/templates.py
T

113 lines
5.2 KiB
Python

import logging
import re
from pathlib import PurePosixPath
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from .. import models, schemas
from ..database import get_db
from ..deps import get_current_user, require_admin
from ..proxmox_templates import list_templates as discover_templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/templates", tags=["templates"])
def _safe_name(value: str, suffix: str = "") -> str:
"""Преобразует имя Proxmox в допустимое имя шаблона панели."""
name = re.sub(r"[^a-zA-Z0-9-]+", "-", value.lower()).strip("-") or "template"
return f"{name}-{suffix}"[:64].strip("-")
@router.get("", response_model=list[schemas.TemplateOut])
def list_templates(db: Session = Depends(get_db), _: models.User = Depends(get_current_user)):
"""Возвращает активные шаблоны панели."""
return list(db.scalars(select(models.Template).where(models.Template.is_active.is_(True)).order_by(models.Template.created_at.desc())))
@router.get("/from-proxmox")
def list_proxmox_templates(_: models.User = Depends(require_admin)):
"""Возвращает найденные на Proxmox VM- и CT-шаблоны."""
try:
return discover_templates()
except Exception:
logger.exception("Не удалось получить список шаблонов Proxmox")
raise HTTPException(status_code=502, detail="Proxmox временно недоступен")
@router.post("/sync", response_model=list[schemas.TemplateOut])
def sync_proxmox_templates(db: Session = Depends(get_db), _: models.User = Depends(require_admin)):
"""Импортирует найденные Proxmox-шаблоны в каталог панели.
Повторный запуск обновляет существующие записи по source_vmid/source_template
и не создаёт дубликаты.
"""
try:
discovered = discover_templates()
except Exception:
logger.exception("Не удалось синхронизировать шаблоны Proxmox")
raise HTTPException(status_code=502, detail="Proxmox временно недоступен")
imported: list[models.Template] = []
for item in discovered["vm_templates"]:
template = db.scalar(select(models.Template).where(models.Template.source_vmid == item["vmid"], models.Template.guest_type == models.GuestType.vm))
if template is None:
template = models.Template(name=_safe_name(item["name"], str(item["vmid"])), guest_type=models.GuestType.vm, source_vmid=item["vmid"], description=f"Импортировано из Proxmox: {item['name']}")
db.add(template)
template.cores = item["cores"]
template.memory_mb = item["memory_mb"]
template.disk_gb = template.disk_gb or 10
template.is_active = True
imported.append(template)
for item in discovered["lxc_templates"]:
source = item["volid"]
template = db.scalar(select(models.Template).where(models.Template.source_template == source))
if template is None:
base_name = PurePosixPath(item["name"]).name
base_name = re.sub(r"\.tar\.(zst|gz|xz)$", "", base_name)
template = models.Template(name=_safe_name(base_name), guest_type=models.GuestType.lxc, source_template=source, description=f"Импортировано из Proxmox: {item['name']}")
db.add(template)
template.cores = template.cores or 1
template.memory_mb = template.memory_mb or 1024
template.disk_gb = template.disk_gb or 10
template.is_active = True
imported.append(template)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail="Имена импортируемых шаблонов пересекаются с каталогом панели")
for template in imported:
db.refresh(template)
return imported
@router.post("", response_model=schemas.TemplateOut, status_code=status.HTTP_201_CREATED)
def create_template(payload: schemas.TemplateCreate, db: Session = Depends(get_db), _: models.User = Depends(require_admin)):
"""Создаёт шаблон вручную."""
template = models.Template(**payload.model_dump())
db.add(template)
try:
db.commit()
except IntegrityError:
db.rollback()
raise HTTPException(status_code=409, detail="Шаблон с таким именем уже существует")
db.refresh(template)
return template
@router.delete("/{template_id}")
def deactivate_template(template_id: int, db: Session = Depends(get_db), _: models.User = Depends(require_admin)):
"""Деактивирует шаблон без удаления связанных инстансов."""
template = db.get(models.Template, template_id)
if template is None:
raise HTTPException(status_code=404, detail="Шаблон не найден")
template.is_active = False
db.commit()
return {"ok": True}