diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 7c01fa3..d67222b 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,56 +1,24 @@ import React, { useEffect, useState } from "react"; import { createRoot } from "react-dom/client"; import { - clearToken, - createTemplate, - deactivateTemplate, - getMe, - getToken, - listInstances, - listTemplates, - login, - register, + clearToken, createInstance, createTemplate, deactivateTemplate, + getMe, getToken, listInstances, listTemplates, login, register, } from "./api"; import "./styles.css"; -function App() { +const emptyTemplate = { + name: "", description: "", guest_type: "vm", source_vmid: "", + source_template: "", cores: 1, memory_mb: 1024, disk_gb: 10, +}; + +function Auth({ onLogin }) { const [mode, setMode] = useState("login"); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); - const [user, setUser] = useState(null); - const [templates, setTemplates] = useState([]); - const [instances, setInstances] = useState([]); - const [templateForm, setTemplateForm] = useState({ - name: "", - description: "", - guest_type: "vm", - source_vmid: "", - source_template: "", - cores: 1, - memory_mb: 1024, - disk_gb: 10, - }); - async function loadPanel() { - const [templateData, instanceData] = await Promise.all([ - listTemplates(), - listInstances(), - ]); - setTemplates(templateData); - setInstances(instanceData); - } - - useEffect(() => { - if (!getToken()) return; - Promise.all([getMe(), loadPanel()]).then(([currentUser]) => { - setUser(currentUser); - }).catch(() => clearToken()); - }, []); - - async function handleSubmit(event) { + async function submit(event) { event.preventDefault(); - setMessage(""); try { if (mode === "register") { await register(email, password); @@ -59,136 +27,103 @@ function App() { return; } await login(email, password); - const currentUser = await getMe(); - setUser(currentUser); - await loadPanel(); - setMessage(`Вы вошли как ${currentUser.role}.`); + onLogin(await getMe()); } catch (error) { setMessage(error.message); } } - async function handleCreateTemplate(event) { + return
+

Proxmox VPS Panel

+

{mode === "login" ? "Вход" : "Регистрация"}

+
+ + + +
+ + {message &&

{message}

} +
; +} + +function App() { + const [user, setUser] = useState(null); + const [message, setMessage] = useState(""); + const [templates, setTemplates] = useState([]); + const [instances, setInstances] = useState([]); + const [templateForm, setTemplateForm] = useState(emptyTemplate); + const [instanceForm, setInstanceForm] = useState({ name: "", template_id: "", username: "paneluser", password: "" }); + + async function refresh() { + const [templateData, instanceData] = await Promise.all([listTemplates(), listInstances()]); + setTemplates(templateData); + setInstances(instanceData); + setInstanceForm((current) => ({ ...current, template_id: current.template_id || String(templateData[0]?.id || "") })); + } + + useEffect(() => { + if (!getToken()) return; + Promise.all([getMe(), refresh()]).then(([current]) => setUser(current)).catch(() => clearToken()); + }, []); + + async function submitTemplate(event) { event.preventDefault(); - setMessage(""); try { - const payload = { + await createTemplate({ ...templateForm, - cores: Number(templateForm.cores), - memory_mb: Number(templateForm.memory_mb), - disk_gb: Number(templateForm.disk_gb), - source_vmid: templateForm.guest_type === "vm" - ? Number(templateForm.source_vmid) - : null, - source_template: templateForm.guest_type === "lxc" - ? templateForm.source_template - : null, - }; - await createTemplate(payload); + source_vmid: templateForm.guest_type === "vm" ? Number(templateForm.source_vmid) : null, + source_template: templateForm.guest_type === "lxc" ? templateForm.source_template : null, + cores: Number(templateForm.cores), memory_mb: Number(templateForm.memory_mb), disk_gb: Number(templateForm.disk_gb), + }); + setTemplateForm(emptyTemplate); setMessage("Шаблон создан."); - setTemplateForm({ ...templateForm, name: "", description: "" }); - await loadPanel(); - } catch (error) { - setMessage(error.message); - } + await refresh(); + } catch (error) { setMessage(error.message); } } - async function handleDeactivate(id) { - if (!window.confirm("Деактивировать этот шаблон?")) return; + async function submitInstance(event) { + event.preventDefault(); try { - await deactivateTemplate(id); - await loadPanel(); - setMessage("Шаблон деактивирован."); - } catch (error) { - setMessage(error.message); - } + const result = await createInstance({ ...instanceForm, template_id: Number(instanceForm.template_id) }); + setMessage(`Инстанс создан: VMID ${result.vmid}. Пароль покажите пользователю и больше нигде не сохраняйте: ${result.initial_password}`); + setInstanceForm((current) => ({ ...current, name: "", password: "" })); + await refresh(); + } catch (error) { setMessage(error.message); } } - function logout() { - clearToken(); - setUser(null); - setTemplates([]); - setInstances([]); + async function deactivate(id) { + if (!window.confirm("Деактивировать шаблон?")) return; + try { await deactivateTemplate(id); await refresh(); } catch (error) { setMessage(error.message); } } - if (!user) { - return ( -
-
-

Proxmox VPS Panel

-

{mode === "login" ? "Вход" : "Регистрация"}

-
- - - -
- - {message &&

{message}

} -
-
- ); - } + if (!user) return ; - return ( -
-
-
Proxmox VPS Panel{user.email} · {user.role}
- -
+ return
+
Proxmox VPS Panel{user.email} · {user.role}
+ {message &&

{message}

} +
+

Мои инстансы

{instances.length === 0 ?

Инстансов пока нет.

:
{instances.map((item) =>
{item.name}{item.guest_type.toUpperCase()} · VMID {item.vmid ?? "не назначен"}{item.status} · {item.node}
)}
}
+

Доступные шаблоны

{templates.length === 0 ?

Шаблонов пока нет.

:
{templates.map((item) =>
{item.name}{item.guest_type.toUpperCase()} · {item.cores} CPU · {item.memory_mb} MB · {item.disk_gb} GB{user.role === "admin" && }
)}
}
+
- {message &&

{message}

} +

Создать VPS

Операция может занимать несколько минут. Пароль будет показан только в ответе создания.

+ + +
+ +
-
-
-

Мои инстансы

- {instances.length === 0 ?

Инстансов пока нет.

: ( -
{instances.map((item) => ( -
- {item.name} - {item.guest_type.toUpperCase()} · VMID {item.vmid ?? "не назначен"} - {item.status} · {item.node} -
- ))}
- )} -
- -
-

Доступные шаблоны

- {templates.length === 0 ?

Шаблонов пока нет.

: ( -
{templates.map((item) => ( -
- {item.name} - {item.guest_type.toUpperCase()} · {item.cores} CPU · {item.memory_mb} MB · {item.disk_gb} GB - {user.role === "admin" && } -
- ))}
- )} -
-
- - {user.role === "admin" &&
-

Создать шаблон

-
- - - - {templateForm.guest_type === "vm" ? ( - - ) : ( - - )} -
- - - -
- -
-
} -
- ); + {user.role === "admin" &&

Создать шаблон

+ + + + {templateForm.guest_type === "vm" ? : } +
+ +
} +
; } createRoot(document.getElementById("root")).render();