From 7db3cd460b64db8e166f98f922bcc0fe3dd12266 Mon Sep 17 00:00:00 2001 From: hayzam Date: Fri, 20 Mar 2026 02:00:55 +0530 Subject: [PATCH] jail: refactor creation from template into separate component --- .../custom/Jail/CreateFromTemplate.svelte | 152 ++++++++++++++++++ .../components/skeleton/BottomPanel.svelte | 115 +++++++++++-- .../skeleton/TreeViewCluster.svelte | 116 ++----------- 3 files changed, 262 insertions(+), 121 deletions(-) create mode 100644 web/src/lib/components/custom/Jail/CreateFromTemplate.svelte diff --git a/web/src/lib/components/custom/Jail/CreateFromTemplate.svelte b/web/src/lib/components/custom/Jail/CreateFromTemplate.svelte new file mode 100644 index 00000000..0eae3f94 --- /dev/null +++ b/web/src/lib/components/custom/Jail/CreateFromTemplate.svelte @@ -0,0 +1,152 @@ + + + + + + Create Jail - Template {templateName} + +
+
+ + +
+ + {#if createMode === 'single'} +
+ + +
+
+ + +
+ {:else} +
+ + +
+
+ + +
+
+ + +
+ {/if} +
+ + + +
+
diff --git a/web/src/lib/components/skeleton/BottomPanel.svelte b/web/src/lib/components/skeleton/BottomPanel.svelte index 6a0a857f..5b407306 100644 --- a/web/src/lib/components/skeleton/BottomPanel.svelte +++ b/web/src/lib/components/skeleton/BottomPanel.svelte @@ -2,6 +2,7 @@ import { storage } from '$lib'; import { getNodes } from '$lib/api/cluster/cluster'; import { getAuditRecords } from '$lib/api/info/audit'; + import { getSimpleJails, getSimpleJailTemplates } from '$lib/api/jail/jail'; import { getActiveLifecycleTasks } from '$lib/api/task/lifecycle'; import { getSimpleVMs } from '$lib/api/vm/vm'; import SimpleSelect from '$lib/components/custom/SimpleSelect.svelte'; @@ -9,6 +10,7 @@ import * as Tabs from '$lib/components/ui/tabs/index.js'; import { reload } from '$lib/stores/api.svelte'; import type { ClusterNode } from '$lib/types/cluster/cluster'; + import type { SimpleJail, SimpleJailTemplate } from '$lib/types/jail/jail'; import type { LifecycleTask } from '$lib/types/task/lifecycle'; import { updateCache } from '$lib/utils/http'; import { convertDbTime } from '$lib/utils/time'; @@ -71,14 +73,38 @@ ); const simpleVmList = resource( - () => 'simple-vm-list', + () => `simple-vm-list-${effectiveHostname || 'default'}`, async (key, prevKey, { signal }) => { - const results = await getSimpleVMs(); + const results = await getSimpleVMs(effectiveHostname || undefined); updateCache(key, results); return results; } ); + const simpleJails = resource( + () => `simple-jail-list-${effectiveHostname || 'default'}`, + async (key, prevKey, { signal }) => { + const results = await getSimpleJails(effectiveHostname || undefined); + updateCache(key, results); + return results; + }, + { + initialValue: [] as SimpleJail[] + } + ); + + const simpleJailTemplates = resource( + () => `simple-jail-template-list-${effectiveHostname || 'default'}`, + async (key, prevKey, { signal }) => { + const results = await getSimpleJailTemplates(effectiveHostname || undefined); + updateCache(key, results); + return results; + }, + { + initialValue: [] as SimpleJailTemplate[] + } + ); + const activeLifecycleTasks = resource( () => `active-lifecycle-tasks-${effectiveHostname || 'default'}`, async () => { @@ -166,8 +192,8 @@ '/api/jail/network/disinheritance': 'Jail - Network Disinherit', '/api/jail/network': 'Jail Network', '/api/jail/description': 'Jail - Update Description', - '/api/jail/templates/convert': 'Jail Template - Convert', - '/api/jail/templates/create': 'Jail Template - Create Jail', + '/api/jail/templates/convert': 'Create Jail Template - From Jail', + '/api/jail/templates/create': 'Create Jail - Template', '/api/jail/templates': 'Jail Template', '/api/jail': 'Jail', '/api/utilities/cloud-init/templates': 'Cloud Init Template', @@ -248,6 +274,15 @@ let activeLifecycleCount = $derived(activeLifecycleTasks.current.length); let lifecycleActive = $derived(activeLifecycleCount > 0); + let vmNameById = $derived.by(() => { + return new Map((simpleVmList.current || []).map((vm) => [vm.rid, vm.name])); + }); + let jailNameByCtId = $derived.by(() => { + return new Map((simpleJails.current || []).map((jail) => [jail.ctId, jail.name])); + }); + let templateNameById = $derived.by(() => { + return new Map((simpleJailTemplates.current || []).map((template) => [template.id, template.name])); + }); watch( () => lifecycleActive, @@ -256,14 +291,67 @@ } ); + function toTitleCase(value: string): string { + return value + .trim() + .split(/\s+/) + .filter(Boolean) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + } + + function lifecycleActionLabel(action: string): string { + return toTitleCase(action.replace(/[_-]+/g, ' ')) || 'Working'; + } + + function lifecycleStatusLabel(status: LifecycleTask['status']): string { + switch (status) { + case 'queued': + return 'Queued'; + case 'running': + return 'Running'; + case 'success': + return 'Success'; + case 'failed': + return 'Failed'; + default: + return toTitleCase(status); + } + } + function lifecycleGuestLabel(task: LifecycleTask): string { - const prefix = - task.guestType === 'vm' - ? 'VM' - : task.guestType === 'jail-template' - ? 'Jail Template' - : 'Jail'; - return `${prefix} ${task.guestId}`; + if (task.guestType === 'vm') { + const name = vmNameById.get(task.guestId); + return name ? `VM ${name} (${task.guestId})` : `VM ${task.guestId}`; + } + + if (task.guestType === 'jail-template') { + const templateName = templateNameById.get(task.guestId); + return templateName + ? `Template ${templateName} (${task.guestId})` + : `Jail Template ${task.guestId}`; + } + + const jailName = jailNameByCtId.get(task.guestId); + return jailName ? `Jail ${jailName} (${task.guestId})` : `Jail ${task.guestId}`; + } + + function lifecycleTaskLabel(task: LifecycleTask): string { + if (task.source.includes('jail/templates/create')) { + const templateName = templateNameById.get(task.guestId); + return templateName + ? `Create Jail - Template ${templateName}` + : `Create Jail - Template ${task.guestId}`; + } + + if (task.source.includes('jail/templates/convert')) { + const jailName = jailNameByCtId.get(task.guestId); + return jailName + ? `Create Jail Template - ${jailName} (Jail CTID ${task.guestId})` + : `Create Jail Template - Jail CTID ${task.guestId}`; + } + + return `${lifecycleActionLabel(task.action)} - ${lifecycleGuestLabel(task)}`; } export function formatStatus(status: string): string { @@ -294,13 +382,12 @@ {activeLifecycleCount} - lifecycle action{activeLifecycleCount === 1 ? '' : 's'} in progress + active lifecycle task{activeLifecycleCount === 1 ? '' : 's'} {#each activeLifecycleTasks.current as task (task.id)} - {lifecycleGuestLabel(task)} - {task.action} ({task.status}) + {lifecycleTaskLabel(task)} ({lifecycleStatusLabel(task.status)}) {/each} diff --git a/web/src/lib/components/skeleton/TreeViewCluster.svelte b/web/src/lib/components/skeleton/TreeViewCluster.svelte index 7c74c342..9e5353f8 100644 --- a/web/src/lib/components/skeleton/TreeViewCluster.svelte +++ b/web/src/lib/components/skeleton/TreeViewCluster.svelte @@ -3,14 +3,10 @@ import { page } from '$app/state'; import { convertJailToTemplate, - createJailFromTemplate, deleteJailTemplate, jailAction } from '$lib/api/jail/jail'; - import { Button } from '$lib/components/ui/button/index.js'; - import * as Dialog from '$lib/components/ui/dialog/index.js'; - import { Input } from '$lib/components/ui/input/index.js'; - import { Label } from '$lib/components/ui/label/index.js'; + import CreateFromTemplate from '$lib/components/custom/Jail/CreateFromTemplate.svelte'; import { actionVm } from '$lib/api/vm/vm'; import * as ContextMenu from '$lib/components/ui/context-menu/index.js'; import { reload } from '$lib/stores/api.svelte'; @@ -77,13 +73,6 @@ return segments[segments.length - 1]; }); let createFromTemplateOpen = $state(false); - let createMode = $state<'single' | 'multiple'>('single'); - let singleCTID = $state(item.sourceCtId || 0); - let singleName = $state(''); - let multipleStartCTID = $state(item.sourceCtId || 0); - let multipleCount = $state(1); - let multipleNamePrefix = $state(item.label.replace(/\s*\(CT\s*\d+\)$/i, '')); - let actionLoading = $state(false); const handleActionClick = async (action: 'start' | 'reboot' | 'shutdown' | 'stop') => { if (item.resourceId === undefined || item.resourceType === undefined) { @@ -109,9 +98,7 @@ const handleConvertToTemplate = async () => { if (!item.resourceId) return; - actionLoading = true; const result = await convertJailToTemplate(item.resourceId, item.nodeHostname); - actionLoading = false; if (result.error) { toast.error('Failed to convert jail to template', { position: 'bottom-center' }); return; @@ -123,9 +110,7 @@ const handleDeleteTemplate = async () => { if (!item.resourceId) return; if (!confirm(`Delete template "${item.label}"?`)) return; - actionLoading = true; const result = await deleteJailTemplate(item.resourceId, item.nodeHostname); - actionLoading = false; if (result.error) { toast.error('Failed to delete template', { position: 'bottom-center' }); return; @@ -134,39 +119,6 @@ toast.success('Template deleted', { position: 'bottom-center' }); }; - const handleCreateFromTemplate = async () => { - if (!item.resourceId) return; - actionLoading = true; - const result = - createMode === 'single' - ? await createJailFromTemplate( - item.resourceId, - { - mode: 'single', - ctid: Number(singleCTID), - name: singleName || undefined - }, - item.nodeHostname - ) - : await createJailFromTemplate( - item.resourceId, - { - mode: 'multiple', - startCtid: Number(multipleStartCTID), - count: Number(multipleCount), - namePrefix: multipleNamePrefix || undefined - }, - item.nodeHostname - ); - actionLoading = false; - if (result.error) { - toast.error('Failed to create jail from template', { position: 'bottom-center' }); - return; - } - createFromTemplateOpen = false; - reload.leftPanel = true; - toast.success('Template restore job queued', { position: 'bottom-center' }); - };
  • @@ -313,62 +265,12 @@ {/if} -{#if item.resourceType === 'jail-template'} - - - - Create Jail From Template - -
    -
    - - -
    - - {#if createMode === 'single'} -
    - - -
    -
    - - -
    - {:else} -
    - - -
    -
    - - -
    -
    - - -
    - {/if} -
    - - - -
    -
    +{#if item.resourceType === 'jail-template' && item.resourceId} + {/if}