fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/index';
|
|
|
|
|
import { Button } from '@/components/ui/index';
|
|
|
|
|
import { Input } from '@/components/ui/index';
|
|
|
|
|
import { Label } from '@/components/ui/index';
|
|
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/index';
|
|
|
|
|
import { listProxmoxNodes, listProxmoxDatastores, createProxmoxVm } from '@/lib/proxmoxClient';
|
|
|
|
|
import { toast } from 'sonner';
|
|
|
|
|
|
|
|
|
|
interface CreateVmDialogProps {
|
|
|
|
|
isOpen: boolean;
|
|
|
|
|
clusterId: string;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
onCreated: () => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const OS_TYPES = [
|
|
|
|
|
{ value: 'l26', label: 'Linux 2.6+' },
|
|
|
|
|
{ value: 'l24', label: 'Linux 2.4' },
|
|
|
|
|
{ value: 'win11', label: 'Windows 11' },
|
|
|
|
|
{ value: 'win10', label: 'Windows 10/2016/2019' },
|
|
|
|
|
{ value: 'win8', label: 'Windows 8/2012' },
|
|
|
|
|
{ value: 'win7', label: 'Windows 7/2008' },
|
|
|
|
|
{ value: 'other', label: 'Other' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export function CreateVmDialog({ isOpen, clusterId, onClose, onCreated }: CreateVmDialogProps) {
|
|
|
|
|
const [nodes, setNodes] = useState<string[]>([]);
|
|
|
|
|
const [storages, setStorages] = useState<string[]>([]);
|
|
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
|
|
|
|
|
|
const [nodeId, setNodeId] = useState('');
|
|
|
|
|
const [vmid, setVmid] = useState(100);
|
|
|
|
|
const [name, setName] = useState('');
|
|
|
|
|
const [memory, setMemory] = useState(2048);
|
|
|
|
|
const [cores, setCores] = useState(2);
|
|
|
|
|
const [sockets, setSockets] = useState(1);
|
|
|
|
|
const [osType, setOsType] = useState('l26');
|
|
|
|
|
const [storage, setStorage] = useState('');
|
|
|
|
|
const [diskSize, setDiskSize] = useState(20);
|
|
|
|
|
const [netBridge, setNetBridge] = useState('vmbr0');
|
|
|
|
|
const [iso, setIso] = useState('');
|
2026-06-21 23:16:13 +00:00
|
|
|
const [isoError, setIsoError] = useState('');
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!isOpen || !clusterId) return;
|
|
|
|
|
|
|
|
|
|
listProxmoxNodes(clusterId)
|
|
|
|
|
.then((data) => {
|
|
|
|
|
const nodeNames = (data as Array<{ node?: string; status?: string }>)
|
|
|
|
|
.filter((n) => n.status === 'online' || n.node)
|
|
|
|
|
.map((n) => n.node ?? '')
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
setNodes(nodeNames);
|
|
|
|
|
setNodeId(nodeNames[0] ?? '');
|
|
|
|
|
})
|
|
|
|
|
.catch(() => toast.error('Failed to load cluster nodes'));
|
|
|
|
|
|
|
|
|
|
listProxmoxDatastores(clusterId)
|
|
|
|
|
.then((data) => {
|
|
|
|
|
const storageIds = (data as Array<{ storage?: string }>)
|
|
|
|
|
.map((s) => s.storage ?? '')
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
setStorages(storageIds);
|
|
|
|
|
setStorage(storageIds[0] ?? 'local-lvm');
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {
|
|
|
|
|
setStorages(['local-lvm', 'local']);
|
|
|
|
|
setStorage('local-lvm');
|
|
|
|
|
});
|
|
|
|
|
}, [isOpen, clusterId]);
|
|
|
|
|
|
2026-06-21 23:16:13 +00:00
|
|
|
const ISO_RE = /^[a-zA-Z0-9_-]+:iso\/.+\.iso$/;
|
|
|
|
|
|
|
|
|
|
const validateIso = (value: string): string => {
|
|
|
|
|
if (!value) return '';
|
|
|
|
|
return ISO_RE.test(value) ? '' : "Must be in the format 'storage:iso/filename.iso'";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleIsoChange = (value: string) => {
|
|
|
|
|
setIso(value);
|
|
|
|
|
setIsoError(validateIso(value));
|
|
|
|
|
};
|
|
|
|
|
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
const handleSubmit = async () => {
|
|
|
|
|
if (!nodeId) { toast.error('Please select a target node'); return; }
|
|
|
|
|
if (!name.trim()) { toast.error('VM name is required'); return; }
|
|
|
|
|
if (vmid < 100 || vmid > 999999999) { toast.error('VMID must be between 100 and 999999999'); return; }
|
2026-06-21 23:16:13 +00:00
|
|
|
if (isoError) { toast.error(isoError); return; }
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
|
|
|
|
|
setIsSubmitting(true);
|
|
|
|
|
try {
|
|
|
|
|
await createProxmoxVm(clusterId, {
|
|
|
|
|
nodeId,
|
|
|
|
|
vmid,
|
|
|
|
|
name: name.trim(),
|
|
|
|
|
memory,
|
|
|
|
|
cores,
|
|
|
|
|
sockets,
|
|
|
|
|
osType,
|
|
|
|
|
storage,
|
|
|
|
|
diskSize,
|
|
|
|
|
netBridge,
|
|
|
|
|
iso: iso.trim() || undefined,
|
|
|
|
|
});
|
|
|
|
|
toast.success(`VM "${name}" created successfully (VMID: ${vmid})`);
|
|
|
|
|
onCreated();
|
|
|
|
|
handleClose();
|
|
|
|
|
} catch (err) {
|
|
|
|
|
toast.error(`Failed to create VM: ${err}`);
|
|
|
|
|
} finally {
|
|
|
|
|
setIsSubmitting(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
setName('');
|
|
|
|
|
setVmid(100);
|
|
|
|
|
setMemory(2048);
|
|
|
|
|
setCores(2);
|
|
|
|
|
setSockets(1);
|
|
|
|
|
setOsType('l26');
|
|
|
|
|
setDiskSize(20);
|
|
|
|
|
setNetBridge('vmbr0');
|
|
|
|
|
setIso('');
|
2026-06-21 23:16:13 +00:00
|
|
|
setIsoError('');
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
onClose();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Dialog open={isOpen} onOpenChange={handleClose}>
|
|
|
|
|
<DialogContent className="max-w-lg max-h-[90vh] overflow-y-auto">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>Create Virtual Machine</DialogTitle>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4 py-2">
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-node">Node</Label>
|
|
|
|
|
<Select value={nodeId} onValueChange={setNodeId}>
|
|
|
|
|
<SelectTrigger>
|
|
|
|
|
<SelectValue placeholder="Select node" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{nodes.map((n) => (
|
|
|
|
|
<SelectItem key={n} value={n}>{n}</SelectItem>
|
|
|
|
|
))}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-vmid">VM ID</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-vmid"
|
|
|
|
|
type="number"
|
|
|
|
|
min={100}
|
|
|
|
|
max={999999999}
|
|
|
|
|
value={vmid}
|
|
|
|
|
onChange={(e) => setVmid(Number(e.target.value))}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-name">Name</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-name"
|
|
|
|
|
value={name}
|
|
|
|
|
onChange={(e) => setName(e.target.value)}
|
|
|
|
|
placeholder="my-vm"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-ostype">OS Type</Label>
|
|
|
|
|
<Select value={osType} onValueChange={setOsType}>
|
|
|
|
|
<SelectTrigger>
|
|
|
|
|
<SelectValue />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{OS_TYPES.map((o) => (
|
|
|
|
|
<SelectItem key={o.value} value={o.value}>{o.label}</SelectItem>
|
|
|
|
|
))}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-3 gap-4">
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-memory">Memory (MB)</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-memory"
|
|
|
|
|
type="number"
|
|
|
|
|
min={256}
|
|
|
|
|
step={256}
|
|
|
|
|
value={memory}
|
|
|
|
|
onChange={(e) => setMemory(Number(e.target.value))}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-cores">Cores</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-cores"
|
|
|
|
|
type="number"
|
|
|
|
|
min={1}
|
2026-06-21 23:16:13 +00:00
|
|
|
max={512}
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
value={cores}
|
|
|
|
|
onChange={(e) => setCores(Number(e.target.value))}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-sockets">Sockets</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-sockets"
|
|
|
|
|
type="number"
|
|
|
|
|
min={1}
|
|
|
|
|
max={4}
|
|
|
|
|
value={sockets}
|
|
|
|
|
onChange={(e) => setSockets(Number(e.target.value))}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-storage">Storage</Label>
|
|
|
|
|
{storages.length > 0 ? (
|
|
|
|
|
<Select value={storage} onValueChange={setStorage}>
|
|
|
|
|
<SelectTrigger>
|
|
|
|
|
<SelectValue placeholder="Select storage" />
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
<SelectContent>
|
|
|
|
|
{storages.map((s) => (
|
|
|
|
|
<SelectItem key={s} value={s}>{s}</SelectItem>
|
|
|
|
|
))}
|
|
|
|
|
</SelectContent>
|
|
|
|
|
</Select>
|
|
|
|
|
) : (
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-storage"
|
|
|
|
|
value={storage}
|
|
|
|
|
onChange={(e) => setStorage(e.target.value)}
|
|
|
|
|
placeholder="local-lvm"
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-disksize">Disk Size (GB)</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-disksize"
|
|
|
|
|
type="number"
|
|
|
|
|
min={1}
|
|
|
|
|
value={diskSize}
|
|
|
|
|
onChange={(e) => setDiskSize(Number(e.target.value))}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-bridge">Network Bridge</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-bridge"
|
|
|
|
|
value={netBridge}
|
|
|
|
|
onChange={(e) => setNetBridge(e.target.value)}
|
|
|
|
|
placeholder="vmbr0"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="space-y-1">
|
|
|
|
|
<Label htmlFor="vm-iso">ISO Image (optional)</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="vm-iso"
|
|
|
|
|
value={iso}
|
2026-06-21 23:16:13 +00:00
|
|
|
onChange={(e) => handleIsoChange(e.target.value)}
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
placeholder="local:iso/ubuntu-24.04.iso"
|
2026-06-21 23:16:13 +00:00
|
|
|
className={isoError ? 'border-red-500' : ''}
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
/>
|
2026-06-21 23:16:13 +00:00
|
|
|
{isoError ? (
|
|
|
|
|
<p className="text-xs text-red-500">{isoError}</p>
|
|
|
|
|
) : (
|
|
|
|
|
<p className="text-xs text-muted-foreground">Format: storage:iso/filename.iso</p>
|
|
|
|
|
)}
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<DialogFooter>
|
|
|
|
|
<Button variant="outline" onClick={handleClose} disabled={isSubmitting}>
|
|
|
|
|
Cancel
|
|
|
|
|
</Button>
|
2026-06-21 23:16:13 +00:00
|
|
|
<Button onClick={handleSubmit} disabled={isSubmitting || !nodeId || !name.trim() || !!isoError}>
|
fix(proxmox): fix VM actions, remove Disk column, add Create VM
Issue 1 — VM actions silently doing nothing:
The root cause was a missing <Toaster> mount in App.tsx. All
toast.success/error calls were no-ops because the sonner Toaster
component was never rendered. Added it at the App root.
Also added dialog:allow-confirm capability (was missing, caused VM
delete confirmation to throw silently).
Issue 2 — Remove Disk column:
PVE cluster/resources returns only static disk allocation, not actual
usage, making the column misleading. Removed from VMList header, row,
and the diskPercent calculation.
Issue 3 — Add VM creation:
- New list_proxmox_nodes Tauri command (GET /nodes) for real node list
- New create_proxmox_vm Tauri command with server-side input validation:
vmid range, numeric bounds, node/storage/bridge path-safety check,
ISO volume-ID format check to prevent comma-property injection
- CreateVmDialog component with node/storage discovery on open
- "Add VM" button wired into VMsPage
MigrationDialog now fetches real cluster nodes via list_proxmox_nodes
instead of inferring them from the VMs already in the list.
Added suspendProxmoxVm, resumeProxmoxVm, listProxmoxNodes,
createProxmoxVm client wrappers to proxmoxClient.ts.
Tests: 446 Rust + 405 frontend, all pass. 19 new VMList tests (TDD),
7 new Rust tests for security validation logic.
2026-06-21 23:01:37 +00:00
|
|
|
{isSubmitting ? 'Creating...' : 'Create VM'}
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
);
|
|
|
|
|
}
|