Add start/restart/stop buttons to Proxmox-linked servers' detail page
The Proxmox start/stop/restart routes already existed (used by the
Integrations page's guest table) but weren't reachable from a server's
own detail page, even when that server was linked to a Proxmox guest.
Reuses the existing POST /api/integrations/:id/proxmox/nodes/:node/
:type/:vmid/{start,stop,restart} routes and the operator+ role gate
already enforced there — no server-side changes needed. Buttons only
render for hardware.source === "proxmox", mirroring the Integrations
page's running/stopped button-set logic, with the same confirm-before-
stop prompt for the non-reversible action.
Verified end-to-end against the real dev server with a mock Proxmox
HTTPS server: linked a server to a mock guest, confirmed the detail
endpoint's live status, then triggered restart/stop/start and
confirmed the mock actually received each action and every call was
audit-logged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1a054d6c2f
commit
de9b6a2d6a
2 files changed
+60
-2
No files matched your search
@@ -79,6 +79,9 @@ export default function ServerDetail({ user }: { user: CurrentUser }) {
|
||||
const [linking, setLinking] = useState(false);
|
||||
const [linkError, setLinkError] = useState<string | null>(null);
|
||||
|
||||
const [actingOnGuest, setActingOnGuest] = useState(false);
|
||||
const [guestActionError, setGuestActionError] = useState<string | null>(null);
|
||||
|
||||
const loadDetail = useCallback(() => {
|
||||
setRefreshing(true);
|
||||
return api.servers
|
||||
@@ -227,6 +230,26 @@ export default function ServerDetail({ user }: { user: CurrentUser }) {
|
||||
|
||||
const { server, hardware, ipAddresses, dnsMatches } = detail;
|
||||
|
||||
async function guestAction(action: "start" | "stop" | "restart") {
|
||||
if (!server.proxmoxIntegrationId || !server.proxmoxNode || !server.proxmoxGuestType || server.proxmoxVmid === null) return;
|
||||
if (action === "stop" && !confirm(`Stop ${server.name}? This is an immediate power-off, not a graceful shutdown.`)) return;
|
||||
setActingOnGuest(true);
|
||||
setGuestActionError(null);
|
||||
try {
|
||||
await api.integrations.proxmox[action](
|
||||
server.proxmoxIntegrationId,
|
||||
server.proxmoxNode,
|
||||
server.proxmoxGuestType,
|
||||
server.proxmoxVmid,
|
||||
);
|
||||
await loadDetail();
|
||||
} catch (err) {
|
||||
setGuestActionError(err instanceof Error ? err.message : String(err));
|
||||
} finally {
|
||||
setActingOnGuest(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-3">
|
||||
@@ -330,7 +353,41 @@ export default function ServerDetail({ user }: { user: CurrentUser }) {
|
||||
{hardware.error && <div className="alert alert-danger">{hardware.error}</div>}
|
||||
{hardware.source === "proxmox" && !hardware.error && (
|
||||
<>
|
||||
<div className="text-secondary small mb-3">Synced live from Proxmox just now.</div>
|
||||
<div className="d-flex align-items-center justify-content-between mb-3">
|
||||
<div className="text-secondary small">
|
||||
Synced live from Proxmox just now.
|
||||
{hardware.status && (
|
||||
<span
|
||||
className={`badge ms-2 ${hardware.status === "running" ? "bg-green-lt text-green" : "bg-secondary-lt text-secondary"}`}
|
||||
>
|
||||
{hardware.status}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{canEditTasks && (
|
||||
<div className="btn-list flex-nowrap">
|
||||
{hardware.status === "running" ? (
|
||||
<>
|
||||
<button className="btn btn-sm" onClick={() => guestAction("restart")} disabled={actingOnGuest}>
|
||||
Restart
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-danger"
|
||||
onClick={() => guestAction("stop")}
|
||||
disabled={actingOnGuest}
|
||||
>
|
||||
Stop
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button className="btn btn-sm btn-primary" onClick={() => guestAction("start")} disabled={actingOnGuest}>
|
||||
Start
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{guestActionError && <div className="alert alert-danger">{guestActionError}</div>}
|
||||
<div className="row g-3">
|
||||
<div className="col-sm-6">
|
||||
<div className="text-secondary small">CPU</div>
|
||||
|
||||
Reference in new issue
Block a user