feat: add start/stop/restart buttons for VMs and LXCs

- New POST /api/power route for Proxmox power operations
- powerControl() method in proxmox.js library
- Start/Stop/Restart action buttons in VMList and LXCList tables
- Status feedback banner in Dashboard on power action

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-07 08:07:38 +01:00
parent 47f10cc2c2
commit 51753269ca
5 changed files with 127 additions and 5 deletions

View File

@@ -4,7 +4,10 @@ import StatusBadge from './StatusBadge';
* @param {{ lxc: Array<{ vmid: number; name: string; type: string; status: string; cpu: number; memory: { total: number; used: number }; disk: { total: number; used: number }; node: string }> }} props
* @returns {import('react').ReactNode}
*/
export default function LXCList({ lxc }) {
/**
* @param {{ lxc: Array<{ vmid: number; name: string; type: string; status: string; cpu: number; memory: { total: number; used: number }; disk: { total: number; used: number }; node: string }>, onPowerAction: (node: string, vmid: number, type: string, action: string) => void }} props
*/
export default function LXCList({ lxc, onPowerAction }) {
if (!lxc?.length) {
return <p className="text-text-secondary text-sm">No LXC containers found.</p>;
}
@@ -38,6 +41,20 @@ export default function LXCList({ lxc }) {
<td className="px-4 py-2 text-text-secondary">{(item.cpu * 100).toFixed(1)}%</td>
<td className="px-4 py-2 text-text-secondary">{formatBytes(item.memory?.used || 0)} / {formatBytes(item.memory?.total || 0)}</td>
<td className="px-4 py-2"><StatusBadge status={item.status} /></td>
<td className="px-4 py-2">
<button
onClick={() => onPowerAction(item.node, item.vmid, 'lxc', 'restart')}
className="mr-2 rounded-md border border-border-card bg-bg-card px-2 py-1 text-xs text-text-primary hover:border-accent hover:text-accent"
>
Restart
</button>
<button
onClick={() => onPowerAction(item.node, item.vmid, 'lxc', 'stop')}
className="rounded-md border border-status-red/30 bg-status-red/10 px-2 py-1 text-xs text-status-red hover:bg-status-red/20"
>
Stop
</button>
</td>
</tr>
))}
</tbody>
@@ -58,6 +75,7 @@ export default function LXCList({ lxc }) {
<th className="px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wider text-text-secondary">Node</th>
<th className="px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wider text-text-secondary">Disk</th>
<th className="px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wider text-text-secondary">Status</th>
<th className="px-4 py-2.5 text-left text-xs font-medium uppercase tracking-wider text-text-secondary">Actions</th>
</tr>
</thead>
<tbody>
@@ -68,6 +86,14 @@ export default function LXCList({ lxc }) {
<td className="px-4 py-2 text-text-secondary">{item.node}</td>
<td className="px-4 py-2 text-text-secondary">{formatBytes(item.disk?.used || 0)}</td>
<td className="px-4 py-2"><StatusBadge status={item.status} /></td>
<td className="px-4 py-2">
<button
onClick={() => onPowerAction(item.node, item.vmid, 'lxc', 'start')}
className="rounded-md border border-status-green/30 bg-status-green/10 px-2 py-1 text-xs text-status-green hover:bg-status-green/20"
>
Start
</button>
</td>
</tr>
))}
</tbody>