vue · 3591 bytes Raw Blame History
1 <template>
2 <div class="network-status">
3 <div class="flex items-center space-x-2">
4 <!-- Status indicator -->
5 <div
6 class="w-2 h-2 rounded-full"
7 :class="{
8 'bg-green-500': status === 'online',
9 'bg-yellow-500': status === 'degraded',
10 'bg-red-500': status === 'offline',
11 'bg-gray-400': status === 'unknown'
12 }"
13 ></div>
14
15 <!-- Status text -->
16 <span class="text-xs text-gray-600 dark:text-gray-400 capitalize">
17 {{ status }}
18 </span>
19 </div>
20
21 <!-- Details -->
22 <div v-if="networkData" class="mt-2 text-xs text-gray-500 dark:text-gray-400 space-y-1">
23 <div class="flex justify-between">
24 <span>Nodes:</span>
25 <span>{{ networkData.nodes.length }}</span>
26 </div>
27 <div class="flex justify-between">
28 <span>Storage:</span>
29 <span>{{ formatBytes(networkData.usedStorage) }} / {{ formatBytes(networkData.totalStorage) }}</span>
30 </div>
31 <div class="flex justify-between">
32 <span>Health:</span>
33 <span>{{ Math.round(networkData.healthScore * 100) }}%</span>
34 </div>
35 </div>
36 </div>
37 </template>
38
39 <script setup lang="ts">
40 import { ref, onMounted, onUnmounted } from 'vue'
41 import { apiClient } from '@/services/api'
42 import type { NetworkStatus } from '@shared/types'
43
44 const status = ref<'online' | 'degraded' | 'offline' | 'unknown'>('unknown')
45 const networkData = ref<NetworkStatus | null>(null)
46
47 let ws: WebSocket | null = null
48 let reconnectTimeout: NodeJS.Timeout | null = null
49
50 function formatBytes(bytes: number): string {
51 if (bytes === 0) return '0 B'
52 const k = 1024
53 const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
54 const i = Math.floor(Math.log(bytes) / Math.log(k))
55 return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
56 }
57
58 function connectWebSocket() {
59 try {
60 ws = apiClient.createStatusWebSocket()
61
62 ws.onopen = () => {
63 status.value = 'online'
64 if (reconnectTimeout) {
65 clearTimeout(reconnectTimeout)
66 reconnectTimeout = null
67 }
68 }
69
70 ws.onmessage = (event) => {
71 try {
72 const data = JSON.parse(event.data)
73
74 if (data.type === 'status_update') {
75 networkData.value = data.data.network
76
77 // Determine overall status
78 if (networkData.value.healthScore > 0.8) {
79 status.value = 'online'
80 } else if (networkData.value.healthScore > 0.5) {
81 status.value = 'degraded'
82 } else {
83 status.value = 'offline'
84 }
85 }
86 } catch (error) {
87 console.error('Failed to parse WebSocket message:', error)
88 }
89 }
90
91 ws.onclose = () => {
92 status.value = 'offline'
93 // Attempt to reconnect after 5 seconds
94 reconnectTimeout = setTimeout(() => {
95 connectWebSocket()
96 }, 5000)
97 }
98
99 ws.onerror = () => {
100 status.value = 'offline'
101 }
102 } catch (error) {
103 console.error('Failed to connect WebSocket:', error)
104 status.value = 'offline'
105
106 // Retry connection
107 reconnectTimeout = setTimeout(() => {
108 connectWebSocket()
109 }, 5000)
110 }
111 }
112
113 async function loadInitialStatus() {
114 try {
115 networkData.value = await apiClient.getNetworkStatus()
116 status.value = 'online'
117 } catch (error) {
118 console.error('Failed to load network status:', error)
119 status.value = 'offline'
120 }
121 }
122
123 onMounted(() => {
124 loadInitialStatus()
125 connectWebSocket()
126 })
127
128 onUnmounted(() => {
129 if (ws) {
130 ws.close()
131 }
132 if (reconnectTimeout) {
133 clearTimeout(reconnectTimeout)
134 }
135 })
136 </script>