mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
fix: 迁移脚本补充 inspect 导入,HardwareTooltip 兼容多种硬件字段格式
- 迁移脚本缺少 inspect 导入导致运行时报错 - HardwareTooltip 使用 pickNumber/pickString 兼容不同节点上报的字段名 - 添加 tooltip 点击交互和 title 属性作为 fallback
This commit is contained in:
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import text
|
from sqlalchemy import inspect, text
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
|
||||||
@@ -69,6 +69,7 @@ def upgrade() -> None:
|
|||||||
"""))
|
"""))
|
||||||
|
|
||||||
# 创建索引
|
# 创建索引
|
||||||
|
inspector = inspect(conn)
|
||||||
existing_indexes = {idx["name"] for idx in inspector.get_indexes("usage")}
|
existing_indexes = {idx["name"] for idx in inspector.get_indexes("usage")}
|
||||||
for idx_name, table, columns in NEW_INDEXES:
|
for idx_name, table, columns in NEW_INDEXES:
|
||||||
if idx_name not in existing_indexes:
|
if idx_name not in existing_indexes:
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
import type { ProxyNode } from '@/api/proxy-nodes'
|
import type { ProxyNode } from '@/api/proxy-nodes'
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
|
||||||
import { Cpu } from 'lucide-vue-next'
|
import { Cpu } from 'lucide-vue-next'
|
||||||
import { computed } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
const props = defineProps<{ node: ProxyNode }>()
|
const props = defineProps<{ node: ProxyNode }>()
|
||||||
|
const open = ref(false)
|
||||||
|
|
||||||
const hardwareInfo = computed<Record<string, any> | null>(() => {
|
const hardwareInfo = computed<Record<string, any> | null>(() => {
|
||||||
const info = props.node.hardware_info
|
const info = props.node.hardware_info
|
||||||
@@ -26,16 +27,24 @@ const hardwareRows = computed(() => {
|
|||||||
const info = hardwareInfo.value ?? {}
|
const info = hardwareInfo.value ?? {}
|
||||||
const rows: Array<{ label: string; value: string }> = []
|
const rows: Array<{ label: string; value: string }> = []
|
||||||
|
|
||||||
if (info.cpu_cores != null) {
|
const cpuCores = pickNumber(info.cpu_cores, info.cpu_count, info.cpu?.cores)
|
||||||
rows.push({ label: 'CPU', value: `${info.cpu_cores} cores` })
|
if (cpuCores != null) {
|
||||||
|
rows.push({ label: 'CPU', value: `${cpuCores} cores` })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.total_memory_mb != null) {
|
const memoryMb = pickNumber(
|
||||||
rows.push({ label: 'RAM', value: formatMemory(info.total_memory_mb) })
|
info.total_memory_mb,
|
||||||
|
info.memory_total_mb,
|
||||||
|
info.memory_mb,
|
||||||
|
info.memory?.total_mb
|
||||||
|
)
|
||||||
|
if (memoryMb != null) {
|
||||||
|
rows.push({ label: 'RAM', value: formatMemory(memoryMb) })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.os_info) {
|
const osInfo = pickString(info.os_info, info.os, info.platform)
|
||||||
rows.push({ label: 'OS', value: String(info.os_info) })
|
if (osInfo) {
|
||||||
|
rows.push({ label: 'OS', value: osInfo })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.node.estimated_max_concurrency != null) {
|
if (props.node.estimated_max_concurrency != null) {
|
||||||
@@ -45,13 +54,19 @@ const hardwareRows = computed(() => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.fd_limit != null) {
|
const fdLimit = pickNumber(info.fd_limit, info.file_descriptor_limit, info.ulimit_nofile)
|
||||||
rows.push({ label: 'FD Limit', value: formatNumber(info.fd_limit) })
|
if (fdLimit != null) {
|
||||||
|
rows.push({ label: 'FD Limit', value: formatNumber(fdLimit) })
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const tooltipTitle = computed(() => {
|
||||||
|
if (hardwareRows.value.length === 0) return '暂无硬件信息上报'
|
||||||
|
return hardwareRows.value.map(row => `${row.label}: ${row.value}`).join(' | ')
|
||||||
|
})
|
||||||
|
|
||||||
const showHardwareInfo = computed(
|
const showHardwareInfo = computed(
|
||||||
() =>
|
() =>
|
||||||
!props.node.is_manual
|
!props.node.is_manual
|
||||||
@@ -69,6 +84,32 @@ function formatNumber(n: number) {
|
|||||||
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
|
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
|
||||||
return String(n)
|
return String(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pickNumber(...values: unknown[]): number | null {
|
||||||
|
for (const value of values) {
|
||||||
|
if (value == null) continue
|
||||||
|
const parsed = typeof value === 'number' ? value : Number(value)
|
||||||
|
if (Number.isFinite(parsed)) return parsed
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickString(...values: unknown[]): string {
|
||||||
|
for (const value of values) {
|
||||||
|
if (value == null) continue
|
||||||
|
const text = String(value).trim()
|
||||||
|
if (text) return text
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTooltip() {
|
||||||
|
open.value = !open.value
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOpenChange(value: boolean) {
|
||||||
|
open.value = value
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -76,12 +117,19 @@ function formatNumber(n: number) {
|
|||||||
v-if="showHardwareInfo"
|
v-if="showHardwareInfo"
|
||||||
:delay-duration="0"
|
:delay-duration="0"
|
||||||
>
|
>
|
||||||
<Tooltip>
|
<Tooltip
|
||||||
|
:open="open"
|
||||||
|
@update:open="handleOpenChange"
|
||||||
|
>
|
||||||
<TooltipTrigger as-child>
|
<TooltipTrigger as-child>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-label="硬件信息"
|
aria-label="硬件信息"
|
||||||
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors"
|
:title="tooltipTitle"
|
||||||
|
class="inline-flex items-center justify-center rounded-sm p-0.5 hover:bg-muted/60 transition-colors cursor-help"
|
||||||
|
@click.stop="toggleTooltip"
|
||||||
|
@keydown.enter.prevent.stop="toggleTooltip"
|
||||||
|
@keydown.space.prevent.stop="toggleTooltip"
|
||||||
>
|
>
|
||||||
<Cpu class="h-3.5 w-3.5 text-muted-foreground" />
|
<Cpu class="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Reference in New Issue
Block a user