Распаковал архив Proxmox-VPS-Panel.rar и добавил содержимое в репозиторий
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user