feat: workspace endpoint, template/ssh-key dropdowns, noVNC console integration
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user