diff --git a/frontend/src/pages/AdminTemplates.jsx b/frontend/src/pages/AdminTemplates.jsx index 6da676d..6d58c73 100644 --- a/frontend/src/pages/AdminTemplates.jsx +++ b/frontend/src/pages/AdminTemplates.jsx @@ -1,7 +1,16 @@ import React, { useEffect, useState } from "react"; import { api } from "../api.js"; -const empty = { name: "", description: "", guest_type: "vm", source_vmid: "", source_template: "", cores: 1, memory_mb: 1024, disk_gb: 10 }; +const empty = { + name: "", + description: "", + guest_type: "vm", + source_vmid: "", + source_template: "", + cores: 1, + memory_mb: 1024, + disk_gb: 10, +}; export default function AdminTemplates() { const [templates, setTemplates] = useState([]); @@ -9,66 +18,196 @@ export default function AdminTemplates() { const [form, setForm] = useState(empty); const [error, setError] = useState(""); const [busy, setBusy] = useState(false); + const [importing, setImporting] = useState(null); - async function refresh() { setTemplates(await api.listTemplates()); } - async function fetchProxmox() { setBusy(true); try { setProxmoxTemplates(await api.fetchProxmoxTemplates()); } catch (err) { setError(err.message); } finally { setBusy(false); } } - async function importVm(vm) { await api.createTemplate({ name: vm.name || "VM-"+vm.vmid, guest_type: "vm", source_vmid: vm.vmid, cores: vm.cores, memory_mb: vm.memory_mb, disk_gb: vm.disk_gb || 10 }); await refresh(); } - async function importLxc(lxc) { await api.createTemplate({ name: lxc.name, guest_type: "lxc", source_template: lxc.volid, cores: 1, memory_mb: 1024, disk_gb: 8 }); await refresh(); } - async function deleteTemplate(id) { if (!confirm("Удалить шаблон?")) return; await api.deleteTemplate(id); await refresh(); } + async function refresh() { + setTemplates(await api.listTemplates()); + } - useEffect(() => { refresh(); }, []); - function set(f, v) { setForm(fr => ({ ...fr, [f]: v })); } + async function fetchProxmox() { + setError(""); + setBusy(true); + try { + setProxmoxTemplates(await api.fetchProxmoxTemplates()); + } catch (err) { + setError(err.message); + } finally { + setBusy(false); + } + } - async function onSubmit(e) { e.preventDefault(); setError(""); setBusy(true); try { await api.createTemplate({ ...form, cores: Number(form.cores), memory_mb: Number(form.memory_mb), disk_gb: Number(form.disk_gb), source_vmid: form.source_vmid ? Number(form.source_vmid) : null, source_template: form.source_template || null }); setForm(empty); await refresh(); } catch (err) { setError(err.message); } finally { setBusy(false); } } + async function importVm(vm) { + setError(""); + setImporting(`vm-${vm.vmid}`); + try { + await api.createTemplate({ + name: vm.name || `VM-${vm.vmid}`, + guest_type: "vm", + source_vmid: Number(vm.vmid), + source_template: null, + cores: vm.cores || 1, + memory_mb: vm.memory_mb || 1024, + disk_gb: vm.disk_gb || 10, + }); + await refresh(); + } catch (err) { + setError(err.message); + } finally { + setImporting(null); + } + } + + async function importLxc(lxc) { + setError(""); + const key = lxc.vmid ? `lxc-vmid-${lxc.vmid}` : `lxc-${lxc.volid}`; + setImporting(key); + try { + await api.createTemplate({ + name: lxc.name || (lxc.vmid ? `LXC-${lxc.vmid}` : "LXC"), + guest_type: "lxc", + source_vmid: lxc.vmid ? Number(lxc.vmid) : null, + source_template: lxc.vmid ? null : lxc.volid, + cores: lxc.cores || 1, + memory_mb: lxc.memory_mb || 1024, + disk_gb: lxc.disk_gb || 8, + }); + await refresh(); + } catch (err) { + setError(err.message); + } finally { + setImporting(null); + } + } + + async function deleteTemplate(id) { + if (!confirm("Удалить шаблон?")) return; + try { + await api.deleteTemplate(id); + await refresh(); + } catch (err) { + setError(err.message); + } + } + + useEffect(() => { + refresh().catch((err) => setError(err.message)); + }, []); + + function set(field, value) { + setForm((current) => ({ ...current, [field]: value })); + } + + async function onSubmit(event) { + event.preventDefault(); + setError(""); + setBusy(true); + try { + await api.createTemplate({ + ...form, + cores: Number(form.cores), + memory_mb: Number(form.memory_mb), + disk_gb: Number(form.disk_gb), + source_vmid: form.source_vmid ? Number(form.source_vmid) : null, + source_template: form.source_template || null, + }); + setForm(empty); + await refresh(); + } catch (err) { + setError(err.message); + } finally { + setBusy(false); + } + } + + const vmTemplates = proxmoxTemplates?.vm_templates || []; + const lxcTemplates = proxmoxTemplates?.lxc_templates || []; return (
| VMID | Имя | vCPU | RAM | |
|---|---|---|---|---|
| {vm.vmid} | {vm.name} | {vm.cores} | {vm.memory_mb} МБ | -
| Имя | Хранилище | Размер | |
|---|---|---|---|
| {lxc.name} | {lxc.storage} | {lxc.size_mb} МБ | -
Шаблоны не найдены.
} -| VMID | Имя | vCPU | RAM | |
|---|---|---|---|---|
| {vm.vmid} | {vm.name} | {vm.cores} | {vm.memory_mb} МБ | ++ |
VM-шаблоны не найдены.
} +| VMID | Имя | Источник | vCPU | RAM | |
|---|---|---|---|---|---|
| {lxc.vmid || "—"} | +{lxc.name} | +{source} | +{lxc.cores || 1} | +{lxc.memory_mb || `${lxc.size_mb || 0} МБ`} | ++ |
LXC-шаблоны не найдены.
} +