Распаковал архив Proxmox-VPS-Panel.rar и добавил содержимое в репозиторий

This commit is contained in:
root
2026-07-24 15:10:47 +00:00
parent 0372e2c5f1
commit ac74a700ce
37 changed files with 2353 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
import React, { useEffect, useState, useCallback } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { api } from "../api.js";
import ConsoleViewer from "../components/ConsoleViewer.jsx";
export default function InstanceDetail() {
const { id } = useParams();
const navigate = useNavigate();
const [instance, setInstance] = useState(null);
const [showConsole, setShowConsole] = useState(false);
const [error, setError] = useState("");
const [busy, setBusy] = useState(false);
const refresh = useCallback(async () => {
const data = await api.getInstance(id);
setInstance(data);
}, [id]);
useEffect(() => {
refresh();
const interval = setInterval(refresh, 5000);
return () => clearInterval(interval);
}, [refresh]);
async function doAction(action) {
setBusy(true);
setError("");
try {
await api.instanceAction(id, action);
await refresh();
} catch (err) {
setError(err.message);
} finally {
setBusy(false);
}
}
async function doDelete() {
if (!confirm("Удалить этот VPS безвозвратно?")) return;
setBusy(true);
try {
await api.deleteInstance(id);
navigate("/");
} catch (err) {
setError(err.message);
setBusy(false);
}
}
if (!instance) return <div className="container">Загрузка</div>;
return (
<div className="container">
<h1 className="page-title">{instance.name}</h1>
<p className="page-sub">
vmid {instance.vmid} · node {instance.node} · {instance.guest_type === "vm" ? "VM (QEMU)" : "LXC"}
</p>
{error && <div className="error-box">{error}</div>}
<div className="card" style={{ marginBottom: 24 }}>
<div className="status-row" style={{ marginBottom: 16 }}>
<span className={`dot ${instance.status}`} /> {instance.status}
</div>
<div className="instance-actions">
<button className="btn" disabled={busy || instance.status !== "stopped"} onClick={() => doAction("start")}>
Старт
</button>
<button className="btn" disabled={busy || instance.status !== "running"} onClick={() => doAction("reboot")}>
Перезагрузить
</button>
<button className="btn" disabled={busy || instance.status !== "running"} onClick={() => doAction("shutdown")}>
Выключить
</button>
<button className="btn" disabled={busy || instance.status !== "running"} onClick={() => doAction("stop")}>
Стоп (force)
</button>
<button className="btn" onClick={() => setShowConsole((v) => !v)}>
{showConsole ? "Скрыть консоль" : "Открыть консоль"}
</button>
<button className="btn btn-danger" disabled={busy} onClick={doDelete}>
Удалить
</button>
</div>
</div>
{showConsole && (
<div className="card">
<ConsoleViewer instanceId={instance.id} />
</div>
)}
</div>
);
}