支持限制用户可选择的系统

This commit is contained in:
MengMengCode
2026-07-16 20:47:26 +08:00
parent 58d86b5d08
commit fdd83977fc
14 changed files with 707 additions and 190 deletions
@@ -43,6 +43,8 @@ const defaultForm: CreateContainerRequest = {
ssh_auth_mode: 'auto_password',
ssh_password: '',
ssh_public_key: '',
allowed_image_ids: [],
image_limit_configured: false,
expires_at: '',
}
@@ -67,7 +69,14 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
setTemplates(data)
setForm((prev) => {
const templateID = data.some((item) => item.id === prev.template_id) ? prev.template_id : (data[0]?.id || '')
return applyTemplateDefaults({ ...prev, template_id: templateID })
const allowed = new Set(data.map((item) => item.id))
const selectedAllowedIDs = (prev.allowed_image_ids || []).filter((id) => allowed.has(id))
return applyTemplateDefaults({
...prev,
template_id: templateID,
allowed_image_ids: prev.image_limit_configured ? selectedAllowedIDs : (templateID ? [templateID] : []),
image_limit_configured: true,
})
})
})
.catch(console.error)
@@ -197,7 +206,7 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
await onSuccess(containers)
onClose()
setBatchCount(1)
setForm({ ...defaultForm, template_id: templates[0]?.id || '' })
setForm({ ...defaultForm, template_id: templates[0]?.id || '', allowed_image_ids: templates[0]?.id ? [templates[0].id] : [], image_limit_configured: true })
} catch (err: unknown) {
const error = err as { response?: { data?: { message?: string } } }
dialog.alert('创建失败', error.response?.data?.message || '请稍后重试')
@@ -241,7 +250,7 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
<div className="grid grid-cols-2 gap-2">
<button
type="button"
onClick={() => setForm((prev) => applyTemplateDefaults({ ...prev, virtualization: 'lxc', template_id: '' }))}
onClick={() => setForm((prev) => applyTemplateDefaults({ ...prev, virtualization: 'lxc', template_id: '', allowed_image_ids: [], image_limit_configured: false }))}
className={`rounded-md border px-3 py-2 text-sm font-medium transition-colors ${form.virtualization === 'lxc' ? 'border-black bg-black text-white' : 'border-gray-300 text-gray-700 hover:bg-gray-50'}`}
>
LXC
@@ -252,7 +261,7 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
title={kvmAvailable ? '' : '当前宿主机不支持 KVM'}
onClick={() => {
if (kvmAvailable) {
setForm((prev) => applyTemplateDefaults({ ...prev, virtualization: 'kvm', template_id: '' }))
setForm((prev) => applyTemplateDefaults({ ...prev, virtualization: 'kvm', template_id: '', allowed_image_ids: [], image_limit_configured: false }))
}
}}
className={`rounded-md border px-3 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:border-gray-200 disabled:bg-gray-50 disabled:text-gray-400 ${form.virtualization === 'kvm' ? 'border-black bg-black text-white' : 'border-gray-300 text-gray-700 hover:bg-gray-50'}`}
@@ -270,7 +279,12 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
) : (
<select
value={form.template_id}
onChange={(event) => setForm(applyTemplateDefaults({ ...form, template_id: event.target.value }))}
onChange={(event) => {
const templateID = event.target.value
const allowed = new Set(form.allowed_image_ids || [])
if (templateID) allowed.add(templateID)
setForm(applyTemplateDefaults({ ...form, template_id: templateID, allowed_image_ids: Array.from(allowed), image_limit_configured: true }))
}}
className={inputClass}
>
{templates.map((template) => (
@@ -283,6 +297,38 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
</Field>
{templates.length > 0 && (
<Field label="子用户可用镜像">
<div className="rounded-md border border-gray-200 bg-gray-50 p-3">
<div className="mb-2 text-xs text-gray-500"></div>
<div className="grid gap-2 sm:grid-cols-2">
{templates.map((template) => {
const checked = (form.allowed_image_ids || []).includes(template.id)
const current = template.id === form.template_id
return (
<label key={template.id} className={`flex cursor-pointer items-start gap-2 rounded border px-2.5 py-2 text-xs ${checked ? 'border-black bg-white' : 'border-gray-200 bg-white hover:bg-gray-50'}`}>
<input
type="checkbox"
checked={checked}
onChange={() => {
const currentIDs = form.allowed_image_ids || []
const next = checked ? currentIDs.filter((id) => id !== template.id) : [...currentIDs, template.id]
setForm({ ...form, allowed_image_ids: next, image_limit_configured: true })
}}
className="mt-0.5 h-4 w-4 rounded border-gray-300 text-black focus:ring-black"
/>
<span className="min-w-0">
<span className="block truncate font-medium text-gray-800">{template.name}{current ? '(当前系统)' : ''}</span>
<span className="block text-gray-500">{template.arch} · {template.distro} {template.release}</span>
</span>
</label>
)
})}
</div>
</div>
</Field>
)}
{linuxTemplate && (
<div className="rounded-md border border-gray-200 bg-white px-3 py-3 text-sm">
<div className="mb-2 font-medium text-gray-800"></div>