mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-23 03:33:53 +00:00
feat: add yellow status indicator for degraded cluster connectivity (addresses #379)
When some cluster nodes are offline but the main node can still reach others, show a yellow status dot instead of red to indicate partial connectivity. This better represents the actual cluster health state.
This commit is contained in:
@@ -66,7 +66,13 @@ const CompactNodeCard: Component<CompactNodeCardProps> = (props) => {
|
||||
} hover:shadow-sm transition-all cursor-pointer hover:scale-[1.01]`}
|
||||
onClick={props.onClick}>
|
||||
{/* Status dot */}
|
||||
<span class={`w-2 h-2 rounded-full ${isOnline() ? 'bg-green-500' : 'bg-red-500'}`} />
|
||||
<span class={`w-2 h-2 rounded-full ${
|
||||
props.node.connectionHealth === 'degraded'
|
||||
? 'bg-yellow-500'
|
||||
: isOnline()
|
||||
? 'bg-green-500'
|
||||
: 'bg-red-500'
|
||||
}`} />
|
||||
|
||||
{/* Node name */}
|
||||
<a
|
||||
@@ -127,7 +133,13 @@ const CompactNodeCard: Component<CompactNodeCardProps> = (props) => {
|
||||
onClick={props.onClick}>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class={`w-2 h-2 rounded-full ${isOnline() ? 'bg-green-500' : 'bg-red-500'}`} />
|
||||
<span class={`w-2 h-2 rounded-full ${
|
||||
props.node.connectionHealth === 'degraded'
|
||||
? 'bg-yellow-500'
|
||||
: isOnline()
|
||||
? 'bg-green-500'
|
||||
: 'bg-red-500'
|
||||
}`} />
|
||||
<a
|
||||
href={props.node.host || `https://${props.node.name}:8006`}
|
||||
target="_blank"
|
||||
|
||||
@@ -886,11 +886,15 @@ const Settings: Component = () => {
|
||||
// Find the corresponding node in the WebSocket state
|
||||
const stateNode = state.nodes.find(n => n.instance === node.name);
|
||||
// Check if the node has an unhealthy connection or is offline
|
||||
if (stateNode?.connectionHealth === 'unhealthy' || stateNode?.status === 'offline') {
|
||||
if (stateNode?.connectionHealth === 'unhealthy' || stateNode?.connectionHealth === 'error' || stateNode?.status === 'offline') {
|
||||
return 'bg-red-500';
|
||||
}
|
||||
// Check if connection is degraded (partial cluster connectivity)
|
||||
if (stateNode?.connectionHealth === 'degraded') {
|
||||
return 'bg-yellow-500';
|
||||
}
|
||||
// Check if we have a healthy connection
|
||||
if (stateNode && stateNode.status === 'online') {
|
||||
if (stateNode && (stateNode.status === 'online' || stateNode.connectionHealth === 'healthy')) {
|
||||
return 'bg-green-500';
|
||||
}
|
||||
// Default to red if no state data (node is offline/unreachable)
|
||||
@@ -1156,11 +1160,15 @@ const Settings: Component = () => {
|
||||
// Find the corresponding PBS instance in the WebSocket state
|
||||
const statePBS = state.pbs.find(p => p.name === node.name);
|
||||
// Check if the PBS has an unhealthy connection or is offline
|
||||
if (statePBS?.connectionHealth === 'unhealthy' || statePBS?.status === 'offline') {
|
||||
if (statePBS?.connectionHealth === 'unhealthy' || statePBS?.connectionHealth === 'error' || statePBS?.status === 'offline') {
|
||||
return 'bg-red-500';
|
||||
}
|
||||
// Check if connection is degraded (not commonly used for PBS but keeping consistent)
|
||||
if (statePBS?.connectionHealth === 'degraded') {
|
||||
return 'bg-yellow-500';
|
||||
}
|
||||
// Check if we have a healthy connection
|
||||
if (statePBS && statePBS.status === 'online') {
|
||||
if (statePBS && (statePBS.status === 'online' || statePBS.connectionHealth === 'healthy')) {
|
||||
return 'bg-green-500';
|
||||
}
|
||||
// Default to red if no state data (server is offline/unreachable)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import solid from 'vite-plugin-solid';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [solid()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 7655,
|
||||
host: '0.0.0.0',
|
||||
strictPort: true, // FAIL if port 7655 is not available
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:7656',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/ws': {
|
||||
target: 'ws://127.0.0.1:7656',
|
||||
ws: true,
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: 'esnext',
|
||||
},
|
||||
});
|
||||
@@ -665,7 +665,43 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie
|
||||
|
||||
// Reset auth failures on successful connection
|
||||
m.resetAuthFailures(instanceName, "pve")
|
||||
m.state.SetConnectionHealth(instanceName, true)
|
||||
|
||||
// Check if client is a ClusterClient to determine health status
|
||||
connectionHealthStr := "healthy"
|
||||
if clusterClient, ok := client.(*proxmox.ClusterClient); ok {
|
||||
// For cluster clients, check if all endpoints are healthy
|
||||
healthStatus := clusterClient.GetHealthStatus()
|
||||
healthyCount := 0
|
||||
totalCount := len(healthStatus)
|
||||
|
||||
for _, isHealthy := range healthStatus {
|
||||
if isHealthy {
|
||||
healthyCount++
|
||||
}
|
||||
}
|
||||
|
||||
if healthyCount == 0 {
|
||||
// All endpoints are down
|
||||
connectionHealthStr = "error"
|
||||
m.state.SetConnectionHealth(instanceName, false)
|
||||
} else if healthyCount < totalCount {
|
||||
// Some endpoints are down - degraded state
|
||||
connectionHealthStr = "degraded"
|
||||
m.state.SetConnectionHealth(instanceName, true) // Still functional but degraded
|
||||
log.Warn().
|
||||
Str("instance", instanceName).
|
||||
Int("healthy", healthyCount).
|
||||
Int("total", totalCount).
|
||||
Msg("Cluster is in degraded state - some nodes are unreachable")
|
||||
} else {
|
||||
// All endpoints are healthy
|
||||
connectionHealthStr = "healthy"
|
||||
m.state.SetConnectionHealth(instanceName, true)
|
||||
}
|
||||
} else {
|
||||
// Regular client - simple healthy/unhealthy
|
||||
m.state.SetConnectionHealth(instanceName, true)
|
||||
}
|
||||
|
||||
// Convert to models
|
||||
var modelNodes []models.Node
|
||||
@@ -693,7 +729,7 @@ func (m *Monitor) pollPVEInstance(ctx context.Context, instanceName string, clie
|
||||
Uptime: int64(node.Uptime),
|
||||
LoadAverage: []float64{},
|
||||
LastSeen: time.Now(),
|
||||
ConnectionHealth: "healthy",
|
||||
ConnectionHealth: connectionHealthStr, // Use the determined health status
|
||||
IsClusterMember: instanceCfg.IsCluster,
|
||||
ClusterName: instanceCfg.ClusterName,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user