mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat(proxy): CI 添加 Docker 构建推送和 README 自动更新,HardwareTooltip 增强字段兼容
- build-proxy workflow 新增 docker job 构建多架构镜像推送 GHCR/Docker Hub - build-proxy workflow 新增 update-readme job 自动更新下载链接表格 - Dockerfile 改用预编译二进制替代多阶段 Rust 编译 - 新增 docker-compose.yml 简化部署 - .env.example 精简为必要配置项 - README 补充 Docker 部署说明和下载表格标记 - HardwareTooltip 兼容 camelCase/snake_case 字段名,添加 pickRecord 辅助函数和原生 tooltip 回退
This commit is contained in:
@@ -7,55 +7,77 @@ import { computed } from 'vue'
|
||||
const props = defineProps<{ node: ProxyNode }>()
|
||||
|
||||
const hardwareInfo = computed<Record<string, unknown> | null>(() => {
|
||||
const info = props.node.hardware_info
|
||||
if (info == null) return null
|
||||
if (typeof info === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(info)
|
||||
if (parsed && typeof parsed === 'object') return parsed as Record<string, unknown>
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
if (typeof info === 'object') return info as Record<string, unknown>
|
||||
return {}
|
||||
return normalizeHardwareInfo(props.node.hardware_info)
|
||||
})
|
||||
|
||||
const hardwareRows = computed(() => {
|
||||
const info = hardwareInfo.value ?? {}
|
||||
const rows: Array<{ label: string; value: string }> = []
|
||||
|
||||
const cpuObj = info.cpu as Record<string, unknown> | undefined
|
||||
const cpuCores = pickNumber(info.cpu_cores, info.cpu_count, cpuObj?.cores)
|
||||
const cpuObj = pickRecord(info.cpu, info.cpu_info, info.cpuInfo)
|
||||
const cpuCores = pickNumber(
|
||||
info.cpu_cores,
|
||||
info.cpuCores,
|
||||
info.cpu_count,
|
||||
info.cpuCount,
|
||||
cpuObj?.cores,
|
||||
cpuObj?.core_count,
|
||||
cpuObj?.coreCount
|
||||
)
|
||||
if (cpuCores != null) {
|
||||
rows.push({ label: 'CPU', value: `${cpuCores} cores` })
|
||||
}
|
||||
|
||||
const memObj = info.memory as Record<string, unknown> | undefined
|
||||
const memObj = pickRecord(info.memory, info.mem, info.ram)
|
||||
const memoryMb = pickNumber(
|
||||
info.total_memory_mb,
|
||||
info.totalMemoryMb,
|
||||
info.memory_total_mb,
|
||||
info.memoryTotalMb,
|
||||
info.memory_mb,
|
||||
memObj?.total_mb
|
||||
info.memoryMb,
|
||||
memObj?.total_mb,
|
||||
memObj?.totalMb,
|
||||
memObj?.mb
|
||||
)
|
||||
if (memoryMb != null) {
|
||||
rows.push({ label: 'RAM', value: formatMemory(memoryMb) })
|
||||
}
|
||||
|
||||
const osInfo = pickString(info.os_info, info.os, info.platform)
|
||||
const osObj = pickRecord(info.os, info.os_info, info.osInfo, info.platform_info, info.platformInfo)
|
||||
const osInfo = pickString(
|
||||
info.os_info,
|
||||
info.osInfo,
|
||||
info.os,
|
||||
info.platform,
|
||||
osObj?.display,
|
||||
osObj?.name && osObj?.version ? `${String(osObj.name)} ${String(osObj.version)}` : null,
|
||||
osObj?.name
|
||||
)
|
||||
if (osInfo) {
|
||||
rows.push({ label: 'OS', value: osInfo })
|
||||
}
|
||||
|
||||
if (props.node.estimated_max_concurrency != null) {
|
||||
const maxConcurrency = pickNumber(
|
||||
props.node.estimated_max_concurrency,
|
||||
info.estimated_max_concurrency,
|
||||
info.estimatedMaxConcurrency
|
||||
)
|
||||
if (maxConcurrency != null) {
|
||||
rows.push({
|
||||
label: 'Max Concurrency',
|
||||
value: `~${formatNumber(props.node.estimated_max_concurrency)}`,
|
||||
value: `~${formatNumber(maxConcurrency)}`,
|
||||
})
|
||||
}
|
||||
|
||||
const fdLimit = pickNumber(info.fd_limit, info.file_descriptor_limit, info.ulimit_nofile)
|
||||
const fdLimit = pickNumber(
|
||||
info.fd_limit,
|
||||
info.fdLimit,
|
||||
info.file_descriptor_limit,
|
||||
info.fileDescriptorLimit,
|
||||
info.ulimit_nofile,
|
||||
info.ulimitNofile
|
||||
)
|
||||
if (fdLimit != null) {
|
||||
rows.push({ label: 'FD Limit', value: formatNumber(fdLimit) })
|
||||
}
|
||||
@@ -63,6 +85,12 @@ const hardwareRows = computed(() => {
|
||||
return rows
|
||||
})
|
||||
|
||||
const nativeTooltipText = computed(() =>
|
||||
hardwareRows.value.length > 0
|
||||
? hardwareRows.value.map((row) => `${row.label}: ${row.value}`).join('\n')
|
||||
: '暂无硬件信息上报'
|
||||
)
|
||||
|
||||
const showHardwareInfo = computed(
|
||||
() =>
|
||||
!props.node.is_manual
|
||||
@@ -81,6 +109,31 @@ function formatNumber(n: number) {
|
||||
return String(n)
|
||||
}
|
||||
|
||||
function normalizeHardwareInfo(info: unknown): Record<string, unknown> | null {
|
||||
if (info == null) return null
|
||||
|
||||
let current: unknown = info
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (typeof current !== 'string') break
|
||||
const text = current.trim()
|
||||
if (!text) return {}
|
||||
try {
|
||||
current = JSON.parse(text)
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
if (!current || typeof current !== 'object') {
|
||||
return {}
|
||||
}
|
||||
|
||||
const obj = current as Record<string, unknown>
|
||||
const nested = pickRecord(obj.hardware_info, obj.hardwareInfo, obj.hardware)
|
||||
if (nested) return nested
|
||||
return obj
|
||||
}
|
||||
|
||||
function pickNumber(...values: unknown[]): number | null {
|
||||
for (const value of values) {
|
||||
if (value == null) continue
|
||||
@@ -90,6 +143,15 @@ function pickNumber(...values: unknown[]): number | null {
|
||||
return null
|
||||
}
|
||||
|
||||
function pickRecord(...values: unknown[]): Record<string, unknown> | null {
|
||||
for (const value of values) {
|
||||
if (value && typeof value === 'object') {
|
||||
return value as Record<string, unknown>
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function pickString(...values: unknown[]): string {
|
||||
for (const value of values) {
|
||||
if (value == null) continue
|
||||
@@ -99,7 +161,6 @@ function pickString(...values: unknown[]): string {
|
||||
return ''
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -109,12 +170,14 @@ function pickString(...values: unknown[]): string {
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<span
|
||||
<button
|
||||
type="button"
|
||||
aria-label="硬件信息"
|
||||
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors cursor-help"
|
||||
:title="nativeTooltipText"
|
||||
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors cursor-pointer"
|
||||
>
|
||||
<Cpu class="h-3.5 w-3.5 text-muted-foreground" />
|
||||
</span>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="right"
|
||||
|
||||
Reference in New Issue
Block a user