release: v1.1.22

This commit is contained in:
MengMengCode
2026-07-16 16:52:15 +08:00
parent 596bf86477
commit 61137b837d
31 changed files with 996 additions and 171 deletions
@@ -98,6 +98,13 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
const manualIPv4s = form.public_ipv4s || []
const maxVCPU = hostInfo?.cpu.cores || 64
const maxRAMMB = hostInfo?.ram.total_mb ? Number(hostInfo.ram.total_mb) : undefined
const kvmAvailable = !!hostInfo?.runtime?.kvm_available
useEffect(() => {
if (hostInfo && !kvmAvailable && form.virtualization === 'kvm') {
setForm((prev) => applyTemplateDefaults({ ...prev, virtualization: 'lxc', template_id: '' }))
}
}, [hostInfo, kvmAvailable, form.virtualization])
const maxDiskGB = hostInfo?.disk.total_gb ? Math.max(1, Math.floor(hostInfo.disk.total_gb)) : undefined
const resourceErrors = validateResourceInputs(form, maxVCPU, maxRAMMB, maxDiskGB)
const natEnabled = form.assign_nat !== false
@@ -241,8 +248,14 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
</button>
<button
type="button"
onClick={() => setForm((prev) => applyTemplateDefaults({ ...prev, virtualization: 'kvm', template_id: '' }))}
className={`rounded-md border px-3 py-2 text-sm font-medium transition-colors ${form.virtualization === 'kvm' ? 'border-black bg-black text-white' : 'border-gray-300 text-gray-700 hover:bg-gray-50'}`}
disabled={!kvmAvailable}
title={kvmAvailable ? '' : '当前宿主机不支持 KVM'}
onClick={() => {
if (kvmAvailable) {
setForm((prev) => applyTemplateDefaults({ ...prev, virtualization: 'kvm', template_id: '' }))
}
}}
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'}`}
>
KVM
</button>
@@ -408,7 +421,7 @@ export default function CreateContainerModal({ isOpen, onClose, onSuccess, exist
<span className="min-w-0">
<span className="block font-medium text-gray-800">{networkText.publicIPv6}</span>
<span className="block text-xs text-gray-500 truncate">
{ipv6Available ? `${networkText.use} ${ipv6Prefix}` : (ipv6Status?.reason || networkText.checkingIPv6Prefix)}
{ipv6Available ? `${networkText.use} ${ipv6Prefix}` : formatIPv6StatusReason(ipv6Status?.reason, language, networkText.checkingIPv6Prefix)}
</span>
</span>
</label>
@@ -762,7 +775,7 @@ const createNetworkText = {
zh: {
publicIPv4: '公网 IPv4',
noAllocatableIPv4: '未检测到可分配公网 IPv4',
publicIPv6: '公网 IPv6',
publicIPv6: '可分配 IPv6 前缀',
use: '使用',
checkingIPv6Prefix: '正在检测 IPv6 前缀...',
publicNAT: '公网 NAT',
@@ -771,7 +784,7 @@ const createNetworkText = {
en: {
publicIPv4: 'Public IPv4',
noAllocatableIPv4: 'No allocatable public IPv4 detected',
publicIPv6: 'Public IPv6',
publicIPv6: 'Allocatable IPv6 Prefix',
use: 'Use',
checkingIPv6Prefix: 'Checking IPv6 prefix...',
publicNAT: 'Public NAT',
@@ -779,6 +792,21 @@ const createNetworkText = {
},
} as const
function formatIPv6StatusReason(reason: string | undefined, language: Language, fallback: string) {
if (!reason) return fallback
if (reason.includes('/128 single-address IPv6 is not assignable')) {
return language === 'en'
? 'No allocatable IPv6 prefix. The host only has a /128 single IPv6 address.'
: '未检测到可分配 IPv6 前缀;宿主机只有 /128 单个 IPv6 地址,不能分配给容器。'
}
if (reason.includes('outbound IPv6 connectivity test failed')) {
return language === 'en'
? reason
: '宿主机检测到 IPv6 前缀,但 IPv6 出站连通性测试失败。'
}
return reason
}
function formatAllocatableIPv4Count(count: number, language: Language) {
return language === 'en'
? `${count} allocatable address${count === 1 ? '' : 'es'} detected`
+1 -3
View File
@@ -395,10 +395,8 @@ export default function Containers() {
const isPlaceholder = !!container.isPlaceholder
const isPolicyBlocked = !!container.policy_blocked
const usage = usageByName[container.name]
const isKVM = (container.virtualization || 'lxc') === 'kvm'
const cpuPct = isRunning
? clamp((usage?.cpu_usage_pct || 0) / (isKVM ? (container.vcpu || 1) : 1))
? clamp((usage?.cpu_usage_pct || 0) / (container.vcpu || 1))
: 0
const ramTotalBytes = usage?.memory_total_bytes && usage.memory_total_bytes > 0
? usage.memory_total_bytes
+3 -2
View File
@@ -205,7 +205,7 @@ const hostReportText = {
ipv4Address: 'IPv4 地址',
ipv4Prefix: 'IPv4 段',
ipv6Address: 'IPv6 地址',
ipv6Prefix: 'IPv6 ',
ipv6Prefix: '可分配 IPv6 前缀',
gateway: '网关',
memoryModules: '内存条',
noMemoryModules: '未检测到内存条明细,可能缺少 dmidecode 或权限受限',
@@ -277,7 +277,7 @@ const hostReportText = {
ipv4Address: 'IPv4 Addresses',
ipv4Prefix: 'IPv4 Prefixes',
ipv6Address: 'IPv6 Addresses',
ipv6Prefix: 'IPv6 Prefixes',
ipv6Prefix: 'Allocatable IPv6 Prefixes',
gateway: 'Gateway',
memoryModules: 'Memory Modules',
noMemoryModules: 'No memory module details detected. dmidecode may be missing or permissions may be limited.',
@@ -511,6 +511,7 @@ function diskTypeLabel(d: { type?: string; rotational?: boolean; virtual?: boole
}
function gpuTypeLabel(value: string, language: Language) {
if (value === 'virtual') return language === 'en' ? 'Virtual' : '虚拟'
if (value === 'integrated') return language === 'en' ? 'Integrated' : '核显'
if (value === 'discrete') return language === 'en' ? 'Discrete' : '独显'
return value || '-'
+13 -11
View File
@@ -148,17 +148,19 @@ export default function ImageManagement() {
onToggle={handleToggle}
/>
<ImageTable
title="KVM 虚拟机镜像"
images={kvmImages}
actionLoading={actionLoading}
downloadedCount={kvmImages.filter((img) => img.downloaded).length}
totalCount={kvmImages.length}
onDownload={handleDownload}
onCancelDownload={handleCancelDownload}
onDelete={handleDelete}
onToggle={handleToggle}
/>
{kvmImages.length > 0 && (
<ImageTable
title="KVM 虚拟机镜像"
images={kvmImages}
actionLoading={actionLoading}
downloadedCount={kvmImages.filter((img) => img.downloaded).length}
totalCount={kvmImages.length}
onDownload={handleDownload}
onCancelDownload={handleCancelDownload}
onDelete={handleDelete}
onToggle={handleToggle}
/>
)}
</div>
)
}
+1 -1
View File
@@ -128,7 +128,7 @@ export default function Login() {
</form>
</div>
<p className="text-center text-xs text-gray-400 mt-6">CLICD v1.1.21</p>
<p className="text-center text-xs text-gray-400 mt-6">CLICD v1.1.22</p>
</div>
</div>
)
+8
View File
@@ -235,6 +235,14 @@ export interface HostInfo {
}
disk_io: { read_bytes: number; write_bytes: number; read_bps: number; write_bps: number }
load: { load1: number; load5: number; load15: number }
runtime?: {
lxc_available: boolean
kvm_available: boolean
dev_kvm: boolean
nested_virtualization: boolean
nested_detail: string
support_mode: string
}
}
export interface HostProbeReport {