feat: workspace endpoint, template/ssh-key dropdowns, noVNC console integration

This commit is contained in:
Philipp
2026-06-12 10:59:02 +02:00
parent e54523040a
commit b554206bbf
11 changed files with 676 additions and 135 deletions
+73 -14
View File
@@ -1,13 +1,20 @@
import { useState } from "react";
import { createVM } from "../api";
import { useEffect, useState } from "react";
import {
createVM,
fetchTemplates,
fetchSSHKeys,
type Template,
type SSHKey,
} from "../api";
interface Props {
token: string;
projectID: string;
tenantID: string;
onCreated: () => void;
}
export function VMCreate({ token, projectID, onCreated }: Props) {
export function VMCreate({ token, projectID, tenantID, onCreated }: Props) {
const [name, setName] = useState("");
const [templateID, setTemplateID] = useState("");
const [node, setNode] = useState("");
@@ -19,21 +26,61 @@ export function VMCreate({ token, projectID, onCreated }: Props) {
const [busy, setBusy] = useState(false);
const [open, setOpen] = useState(false);
const [templates, setTemplates] = useState<Template[]>([]);
const [sshKeys, setSSHKeys] = useState<SSHKey[]>([]);
useEffect(() => {
if (!open) return;
fetchTemplates(token)
.then((res) => {
setTemplates(res.data ?? []);
if (res.data && res.data.length > 0 && !templateID) {
setTemplateID(res.data[0].id);
}
})
.catch(() => {});
if (tenantID) {
fetchSSHKeys(token, tenantID)
.then((res) => setSSHKeys(res.data ?? []))
.catch(() => {});
}
}, [open, token, tenantID]);
useEffect(() => {
if (templateID) {
const tpl = templates.find((t) => t.id === templateID);
if (tpl && !node) {
setNode(tpl.proxmox_node);
}
}
}, [templateID, templates]);
async function handleCreate() {
setError("");
if (!name.trim()) {
setError("Name erforderlich");
return;
}
if (!templateID) {
setError("Template erforderlich");
return;
}
if (!node.trim()) {
setError("Node erforderlich");
return;
}
setBusy(true);
try {
await createVM(token, projectID, {
name,
name: name.trim(),
template_id: templateID,
node,
node: node.trim(),
vcpu,
ram_mb: ramMB,
disk_gb: diskGB,
ssh_key_id: sshKeyID || undefined,
});
setName("");
setTemplateID("");
setOpen(false);
onCreated();
} catch (err) {
@@ -73,12 +120,17 @@ export function VMCreate({ token, projectID, onCreated }: Props) {
/>
</label>
<label>
Template-ID
<input
Template
<select
value={templateID}
onChange={(e) => setTemplateID(e.target.value)}
placeholder="UUID des Templates"
/>
>
{templates.map((t) => (
<option key={t.id} value={t.id}>
{t.name} (VMID {t.proxmox_template_vmid}, {t.proxmox_node})
</option>
))}
</select>
</label>
<label>
Node
@@ -113,18 +165,25 @@ export function VMCreate({ token, projectID, onCreated }: Props) {
/>
</label>
<label>
SSH-Key-ID (optional)
<input
SSH-Key (optional)
<select
value={sshKeyID}
onChange={(e) => setSSHKeyID(e.target.value)}
placeholder="UUID"
/>
>
<option value="">Kein SSH-Key</option>
{sshKeys.map((k) => (
<option key={k.id} value={k.id}>
{k.name}
</option>
))}
</select>
</label>
</div>
<button
className="primary-button"
disabled={busy}
onClick={handleCreate}
style={{ marginTop: 14 }}
>
{busy ? "Erstelle..." : "VM erstellen"}
</button>