diff --git a/README.md b/README.md index 3a063b9..18de1c6 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ All modules from the original plan are built: overview opens a detail page with CPU/RAM/disk status, IP addresses, and matching DNS names (looked up from the DNS module's cache) — live from Proxmox for VM/LXC-backed servers, or from the agent's own hardware - report for everything else. + report for everything else. Proxmox-linked servers also get + start/stop/restart buttons right on the detail page. - **Integrations → Tailscale** — device list with online/authorized status, and authorize/deauthorize/remove actions; a live device-count widget. - **Integrations → Gitea** — repo list with each repo's last CI run status, diff --git a/web/src/pages/ServerDetail.tsx b/web/src/pages/ServerDetail.tsx index 20ae89e..67c2cee 100644 --- a/web/src/pages/ServerDetail.tsx +++ b/web/src/pages/ServerDetail.tsx @@ -79,6 +79,9 @@ export default function ServerDetail({ user }: { user: CurrentUser }) { const [linking, setLinking] = useState(false); const [linkError, setLinkError] = useState(null); + const [actingOnGuest, setActingOnGuest] = useState(false); + const [guestActionError, setGuestActionError] = useState(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 ( <>
@@ -330,7 +353,41 @@ export default function ServerDetail({ user }: { user: CurrentUser }) { {hardware.error &&
{hardware.error}
} {hardware.source === "proxmox" && !hardware.error && ( <> -
Synced live from Proxmox just now.
+
+
+ Synced live from Proxmox just now. + {hardware.status && ( + + {hardware.status} + + )} +
+ {canEditTasks && ( +
+ {hardware.status === "running" ? ( + <> + + + + ) : ( + + )} +
+ )} +
+ {guestActionError &&
{guestActionError}
}
CPU