fix(web): redact public host targets
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 }}>
|
||||
|
||||
Reference in New Issue
Block a user