|
10 | 10 | </p> |
11 | 11 | </div> |
12 | 12 |
|
13 | | - <!-- Status indicator --> |
| 13 | + <!-- Status indicator using unified health status --> |
14 | 14 | <div |
15 | 15 | :class="[ |
16 | 16 | 'badge badge-sm flex-shrink-0', |
17 | | - server.connected ? 'badge-success' : |
18 | | - server.connecting ? 'badge-warning' : |
19 | | - 'badge-error' |
| 17 | + statusBadgeClass |
20 | 18 | ]" |
21 | 19 | > |
22 | | - {{ server.connected ? 'Connected' : server.connecting ? 'Connecting' : 'Disconnected' }} |
| 20 | + {{ statusText }} |
23 | 21 | </div> |
24 | 22 | </div> |
25 | 23 |
|
|
65 | 63 | <span class="text-xs">Server is quarantined</span> |
66 | 64 | </div> |
67 | 65 |
|
68 | | - <!-- Actions --> |
| 66 | + <!-- Actions - uses unified health.action when available --> |
69 | 67 | <div class="card-actions justify-end space-x-2"> |
| 68 | + <!-- Primary action button based on health.action --> |
70 | 69 | <button |
71 | | - v-if="server.quarantined" |
| 70 | + v-if="healthAction === 'approve'" |
72 | 71 | @click="unquarantine" |
73 | 72 | :disabled="loading" |
74 | 73 | class="btn btn-sm btn-warning" |
75 | 74 | > |
76 | 75 | <span v-if="loading" class="loading loading-spinner loading-xs"></span> |
77 | | - Unquarantine |
| 76 | + Approve |
78 | 77 | </button> |
79 | 78 |
|
80 | | - <!-- Restart button only for stdio servers (HTTP servers use Login/Reconnect) --> |
81 | 79 | <button |
82 | | - v-if="!server.connected && server.enabled && !isHttpProtocol" |
83 | | - @click="restart" |
| 80 | + v-if="healthAction === 'enable'" |
| 81 | + @click="enableServer" |
84 | 82 | :disabled="loading" |
85 | | - class="btn btn-sm btn-outline" |
| 83 | + class="btn btn-sm btn-primary" |
86 | 84 | > |
87 | 85 | <span v-if="loading" class="loading loading-spinner loading-xs"></span> |
88 | | - Restart |
| 86 | + Enable |
89 | 87 | </button> |
90 | 88 |
|
91 | 89 | <button |
92 | | - v-if="needsOAuth" |
| 90 | + v-if="healthAction === 'login'" |
93 | 91 | @click="triggerOAuth" |
94 | 92 | :disabled="loading" |
95 | | - class="btn btn-sm btn-outline" |
| 93 | + class="btn btn-sm btn-primary" |
96 | 94 | > |
97 | 95 | <span v-if="loading" class="loading loading-spinner loading-xs"></span> |
98 | 96 | Login |
99 | 97 | </button> |
100 | 98 |
|
| 99 | + <button |
| 100 | + v-if="healthAction === 'restart'" |
| 101 | + @click="restart" |
| 102 | + :disabled="loading" |
| 103 | + class="btn btn-sm btn-primary" |
| 104 | + > |
| 105 | + <span v-if="loading" class="loading loading-spinner loading-xs"></span> |
| 106 | + Restart |
| 107 | + </button> |
| 108 | + |
| 109 | + <router-link |
| 110 | + v-if="healthAction === 'view_logs'" |
| 111 | + :to="`/servers/${server.name}?tab=logs`" |
| 112 | + class="btn btn-sm btn-primary" |
| 113 | + > |
| 114 | + View Logs |
| 115 | + </router-link> |
| 116 | + |
| 117 | + <!-- Logout button (only when connected with OAuth) --> |
101 | 118 | <button |
102 | 119 | v-if="canLogout" |
103 | 120 | @click="triggerLogout" |
@@ -178,6 +195,52 @@ const isHttpProtocol = computed(() => { |
178 | 195 | return props.server.protocol === 'http' || props.server.protocol === 'streamable-http' |
179 | 196 | }) |
180 | 197 |
|
| 198 | +// Unified health status computed properties |
| 199 | +const statusBadgeClass = computed(() => { |
| 200 | + const health = props.server.health |
| 201 | + if (health) { |
| 202 | + // Use admin_state for disabled/quarantined, otherwise use health level |
| 203 | + switch (health.admin_state) { |
| 204 | + case 'disabled': |
| 205 | + return 'badge-neutral' // gray |
| 206 | + case 'quarantined': |
| 207 | + return 'badge-secondary' // purple-ish |
| 208 | + default: |
| 209 | + // Use health level |
| 210 | + switch (health.level) { |
| 211 | + case 'healthy': |
| 212 | + return 'badge-success' |
| 213 | + case 'degraded': |
| 214 | + return 'badge-warning' |
| 215 | + case 'unhealthy': |
| 216 | + return 'badge-error' |
| 217 | + default: |
| 218 | + return 'badge-ghost' |
| 219 | + } |
| 220 | + } |
| 221 | + } |
| 222 | + // Fallback to legacy logic |
| 223 | + if (props.server.connected) return 'badge-success' |
| 224 | + if (props.server.connecting) return 'badge-warning' |
| 225 | + return 'badge-error' |
| 226 | +}) |
| 227 | +
|
| 228 | +const statusText = computed(() => { |
| 229 | + const health = props.server.health |
| 230 | + if (health) { |
| 231 | + return health.summary || health.level |
| 232 | + } |
| 233 | + // Fallback to legacy logic |
| 234 | + if (props.server.connected) return 'Connected' |
| 235 | + if (props.server.connecting) return 'Connecting' |
| 236 | + return 'Disconnected' |
| 237 | +}) |
| 238 | +
|
| 239 | +// Suggested action from health status |
| 240 | +const healthAction = computed(() => { |
| 241 | + return props.server.health?.action || '' |
| 242 | +}) |
| 243 | +
|
181 | 244 | const needsOAuth = computed(() => { |
182 | 245 | // Don't show Login button if server is disabled |
183 | 246 | if (!props.server.enabled) return false |
@@ -305,6 +368,26 @@ async function toggleEnabled() { |
305 | 368 | } |
306 | 369 | } |
307 | 370 |
|
| 371 | +async function enableServer() { |
| 372 | + loading.value = true |
| 373 | + try { |
| 374 | + await serversStore.enableServer(props.server.name) |
| 375 | + systemStore.addToast({ |
| 376 | + type: 'success', |
| 377 | + title: 'Server Enabled', |
| 378 | + message: `${props.server.name} has been enabled`, |
| 379 | + }) |
| 380 | + } catch (error) { |
| 381 | + systemStore.addToast({ |
| 382 | + type: 'error', |
| 383 | + title: 'Enable Failed', |
| 384 | + message: error instanceof Error ? error.message : 'Unknown error', |
| 385 | + }) |
| 386 | + } finally { |
| 387 | + loading.value = false |
| 388 | + } |
| 389 | +} |
| 390 | +
|
308 | 391 | async function restart() { |
309 | 392 | loading.value = true |
310 | 393 | try { |
|
0 commit comments