guardia-messenger/components/ZeroTrustBadge.tsx

77 lines
2.4 KiB
TypeScript

/**
* #37 Zero Trust 상태 뱃지
* GET /api/auth/network-status → { via: 'vpn'|'opennet'|'internal', level: 1|2|3 }
* 🟢 Internal / 🟡 VPN / 🟠 OpenNet
*/
import { useEffect, useState } from 'react'
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native'
import { getNetworkStatus } from '../services/api'
type Via = 'vpn' | 'opennet' | 'internal' | string
interface NetStatus {
via: Via
level: 1 | 2 | 3 | number
}
const META: Record<string, { dot: string; label: string; color: string; bg: string }> = {
internal: { dot: '🟢', label: 'Internal', color: '#15803d', bg: '#dcfce7' },
vpn: { dot: '🟡', label: 'VPN', color: '#a16207', bg: '#fef9c3' },
opennet: { dot: '🟠', label: 'OpenNet', color: '#c2410c', bg: '#ffedd5' },
}
export default function ZeroTrustBadge() {
const [status, setStatus] = useState<NetStatus | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
let active = true
;(async () => {
try {
const r = await getNetworkStatus()
if (active) setStatus(r.data)
} catch {
// 서버 미응답 시 알 수 없음 처리
if (active) setStatus(null)
} finally {
if (active) setLoading(false)
}
})()
return () => { active = false }
}, [])
if (loading) {
return (
<View style={[s.badge, { backgroundColor: '#f1f5f9' }]}>
<ActivityIndicator size="small" color="#94a3b8" />
<Text style={[s.label, { color: '#94a3b8' }]}> </Text>
</View>
)
}
const meta = (status && META[status.via]) || {
dot: '⚪', label: '알 수 없음', color: '#64748b', bg: '#f1f5f9',
}
return (
<View style={[s.badge, { backgroundColor: meta.bg }]}>
<Text style={s.dot}>{meta.dot}</Text>
<Text style={[s.label, { color: meta.color }]}>Zero Trust · {meta.label}</Text>
{status?.level != null && (
<Text style={[s.level, { color: meta.color }]}>Lv.{status.level}</Text>
)}
</View>
)
}
const s = StyleSheet.create({
badge: {
flexDirection: 'row', alignItems: 'center', gap: 6,
paddingHorizontal: 12, paddingVertical: 8, borderRadius: 12,
marginHorizontal: 16, marginTop: 12,
},
dot: { fontSize: 13 },
label: { fontSize: 13, fontWeight: '700', flex: 1 },
level: { fontSize: 11, fontWeight: '700', opacity: 0.8 },
})