fix(web): redact public host targets

This commit is contained in:
Your Name
2026-06-13 02:01:13 +08:00
parent 0d30e1b256
commit f71c2779a8
7 changed files with 132 additions and 37 deletions

View File

@@ -46,7 +46,12 @@ const _getApiBaseUrl = () => {
return url
}
const HOST_IPS = (process.env.NEXT_PUBLIC_HOST_IPS ?? '192.168.0.110,192.168.0.112,192.168.0.120,192.168.0.188').split(',')
const HOST_SLOT_IDS = [
'host:public-gateway',
'host:kali-readonly',
'host:k3s-control-a',
'host:observability-a',
]
// =============================================================================
// Component
@@ -70,7 +75,7 @@ export function LiveDashboard({ locale: _locale }: LiveDashboardProps) {
// Host fallback data with i18n
const HOST_FALLBACKS: Record<string, { name: string; role: string; services: Array<{ name: string; status: 'idle'; port?: number }> }> = {
'192.168.0.110': {
'host:public-gateway': {
name: tHost('devops.name'),
role: 'devops',
services: [
@@ -79,7 +84,7 @@ export function LiveDashboard({ locale: _locale }: LiveDashboardProps) {
{ name: 'Docker', status: 'idle', port: 2375 },
],
},
'192.168.0.112': {
'host:kali-readonly': {
name: tHost('security.name'),
role: 'security',
services: [
@@ -88,7 +93,7 @@ export function LiveDashboard({ locale: _locale }: LiveDashboardProps) {
{ name: 'Nuclei', status: 'idle' },
],
},
'192.168.0.120': {
'host:k3s-control-a': {
name: tHost('k3s.name'),
role: 'k3s',
services: [
@@ -97,7 +102,7 @@ export function LiveDashboard({ locale: _locale }: LiveDashboardProps) {
{ name: 'Traefik', status: 'idle', port: 80 },
],
},
'192.168.0.188': {
'host:observability-a': {
name: tHost('aiWeb.name'),
role: 'ai_web',
services: [
@@ -118,6 +123,9 @@ export function LiveDashboard({ locale: _locale }: LiveDashboardProps) {
// return () => disconnect()
// }, [connect, disconnect])
const liveHostKeys = Array.from(new Set(hosts.map((host) => host.ip).filter(Boolean)))
const hostCardKeys = liveHostKeys.length > 0 ? liveHostKeys : HOST_SLOT_IDS
return (
<div className="space-y-6">
{/* Header Stats */}
@@ -139,7 +147,7 @@ export function LiveDashboard({ locale: _locale }: LiveDashboardProps) {
{/* Four Host Cards */}
<div className="lg:col-span-2">
<HostCardGrid>
{HOST_IPS.map((ip) => (
{hostCardKeys.map((ip) => (
<LiveHostCard
key={ip}
ip={ip}

View File

@@ -66,6 +66,23 @@ function formatPercent(value: number): string {
return `${Math.round(value)}%`
}
const HOST_ALIAS_BY_LAST_OCTET: Record<string, string> = {
'110': 'host:public-gateway',
'111': 'host:dev-a',
'112': 'host:kali-readonly',
'120': 'host:k3s-control-a',
'121': 'host:k3s-control-b',
'125': 'host:edge-vip',
'168': 'host:dev-b',
'188': 'host:observability-a',
}
function toPublicHostId(value: string): string {
const match = value.trim().match(/^(?:\d{1,3}\.){3}(\d{1,3})$/)
if (!match) return value
return HOST_ALIAS_BY_LAST_OCTET[match[1]] ?? 'host:internal-node'
}
// =============================================================================
// Component
// =============================================================================
@@ -91,6 +108,7 @@ export function LiveHostCard({
const tDashboard = useTranslations('dashboard')
const tCommon = useTranslations('common')
const host = useHostByIp(ip)
const publicHostId = toPublicHostId(ip)
// Track status changes for flash effect
const [isFlashing, setIsFlashing] = useState(false)
@@ -113,7 +131,7 @@ export function LiveHostCard({
<GlassCard className={cn('h-full animate-pulse', className)}>
<div className="h-40 flex items-center justify-center">
<span className="text-nothing-gray-400 font-body text-sm">
{tCommon('loading')} {ip}
{tCommon('loading')} {publicHostId}
</span>
</div>
</GlassCard>
@@ -127,7 +145,7 @@ export function LiveHostCard({
<div className="flex items-center gap-3">
<StatusOrb status="idle" size="md" />
<span className="inline-flex items-center px-2 py-0.5 rounded-md bg-nothing-gray-100 text-nothing-gray-600 text-xs font-body">
{ip}
{publicHostId}
</span>
</div>
</GlassCardHeader>
@@ -199,7 +217,7 @@ export function LiveHostCard({
<div className="flex flex-col gap-1">
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-nothing-gray-50 border border-nothing-gray-100 text-nothing-gray-700 text-xs font-body">
<Wifi className="w-3 h-3" />
{host.ip}
{toPublicHostId(host.ip)}
</span>
</div>
</div>

View File

@@ -32,6 +32,23 @@ export interface HostGridProps {
hosts: HostInfo[]
}
const HOST_ALIAS_BY_LAST_OCTET: Record<string, string> = {
'110': 'host:public-gateway',
'111': 'host:dev-a',
'112': 'host:kali-readonly',
'120': 'host:k3s-control-a',
'121': 'host:k3s-control-b',
'125': 'host:edge-vip',
'168': 'host:dev-b',
'188': 'host:observability-a',
}
function toPublicHostId(value: string): string {
const match = value.trim().match(/^(?:\d{1,3}\.){3}(\d{1,3})$/)
if (!match) return value
return HOST_ALIAS_BY_LAST_OCTET[match[1]] ?? 'host:internal-node'
}
function MetricBar({ value, warn = 80, critical = 90 }: { value: number | null; warn?: number; critical?: number }) {
if (value === null) return <span style={{ color: '#b0ad9f', fontSize: 11 }}>--</span>
const color = value >= critical ? '#cc2200' : value >= warn ? '#F59E0B' : '#22C55E'
@@ -61,6 +78,7 @@ function HostCard({ host }: { host: HostInfo }) {
const borderColor = isK3s ? '#93c5fd' : '#e0ddd4'
const headerBg = isK3s ? '#eff6ff' : '#f5f4ed'
const accentColor = isK3s ? '#3b7de8' : '#d97757'
const publicHostId = toPublicHostId(host.ip)
return (
<div
@@ -80,8 +98,7 @@ function HostCard({ host }: { host: HostInfo }) {
<div style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
<div style={{ width: 6, height: 6, borderRadius: '50%', background: accentColor, flexShrink: 0 }} />
<span style={{ fontSize: 12, fontWeight: 700, color: '#141413', fontFamily: 'var(--font-body), monospace' }}>
{/* 顯示末段 IP 作為簡短標識,完整名稱放 IP 欄位 */}
{host.ip.includes('.') ? host.ip.split('.').pop() ?? host.ip : host.hostname}
{publicHostId.startsWith('host:') ? publicHostId.replace('host:', '') : host.hostname}
</span>
{isK3s && (
<span style={{
@@ -92,7 +109,7 @@ function HostCard({ host }: { host: HostInfo }) {
</span>
)}
</div>
<span style={{ fontSize: 10, color: '#b0ad9f', fontFamily: 'monospace' }}>{host.ip}</span>
<span style={{ fontSize: 10, color: '#b0ad9f', fontFamily: 'monospace' }}>{publicHostId}</span>
</div>
<div style={{ display: 'flex', gap: 10, marginTop: 2 }}>
<span style={{ fontSize: 10, color: '#b0ad9f' }}>CPU <MetricBar value={host.cpuPct} /></span>
@@ -118,7 +135,7 @@ function HostCard({ host }: { host: HostInfo }) {
}
const K8S_VIP_INFO_FALLBACK =
'K8S VIP topology (ops-only) · kubectl:6443 · web:32335 · api:32334'
'K8S HA topology redacted · kubectl:6443 · web:32335 · api:32334'
export function HostGrid({ hosts }: HostGridProps) {
if (hosts.length === 0) {
@@ -153,7 +170,7 @@ export function HostGrid({ hosts }: HostGridProps) {
K3S CLUSTER (HA)
</span>
<span style={{ fontSize: 10, color: '#3b7de8' }}>
{(process.env.NEXT_PUBLIC_K8S_VIP_INFO ?? '').trim() || K8S_VIP_INFO_FALLBACK}
{K8S_VIP_INFO_FALLBACK}
</span>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, padding: 6 }}>