diff --git a/frontend-modern/src/components/Alerts/ResourceTable.tsx b/frontend-modern/src/components/Alerts/ResourceTable.tsx
index 4dc96ccc0..cba9c41ee 100644
--- a/frontend-modern/src/components/Alerts/ResourceTable.tsx
+++ b/frontend-modern/src/components/Alerts/ResourceTable.tsx
@@ -250,16 +250,17 @@ export function ResourceTable(props: ResourceTableProps) {
{resource.disabled ? 'Disabled' : 'Enabled'}
-
+
@@ -471,16 +472,17 @@ export function ResourceTable(props: ResourceTableProps) {
{resource.disabled ? 'Disabled' : 'Enabled'}
-
+
diff --git a/frontend-modern/src/components/Alerts/ThresholdsTable.tsx b/frontend-modern/src/components/Alerts/ThresholdsTable.tsx
index df5463441..a5d1b1ab4 100644
--- a/frontend-modern/src/components/Alerts/ThresholdsTable.tsx
+++ b/frontend-modern/src/components/Alerts/ThresholdsTable.tsx
@@ -238,7 +238,8 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
const pbsInstances = props.pbsInstances || [];
const pbsServers = pbsInstances.filter((pbs) => (pbs.cpu || 0) > 0 || (pbs.memory?.usage || 0) > 0).map((pbs) => {
- const pbsId = `pbs-${pbs.id}`;
+ // PBS IDs already have "pbs-" prefix from backend, don't double it
+ const pbsId = pbs.id;
const override = overridesMap.get(pbsId);
// Check if any threshold values actually differ from defaults
@@ -264,6 +265,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
uptime: pbs.uptime,
hasOverride: hasCustomThresholds || false,
disabled: false,
+ disableConnectivity: override?.disableConnectivity || false,
thresholds: override?.thresholds || {},
defaults: {
cpu: props.nodeDefaults.cpu,
@@ -502,15 +504,18 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
};
- const toggleNodeConnectivity = (nodeId: string, forceState?: boolean) => {
- const node = nodesWithOverrides().find(r => r.id === nodeId);
- if (!node || node.type !== 'node') return;
+ const toggleNodeConnectivity = (resourceId: string, forceState?: boolean) => {
+ // Find the resource - could be a node or PBS server
+ const nodes = nodesWithOverrides();
+ const pbsServers = pbsServersWithOverrides();
+ const resource = [...nodes, ...pbsServers].find(r => r.id === resourceId);
+ if (!resource || (resource.type !== 'node' && resource.type !== 'pbs')) return;
// Get existing override if it exists
- const existingOverride = props.overrides().find(o => o.id === nodeId);
+ const existingOverride = props.overrides().find(o => o.id === resourceId);
- // Determine the current state - use the node's computed state, not just the override
- const currentDisableConnectivity = node.disableConnectivity;
+ // Determine the current state - use the resource's computed state, not just the override
+ const currentDisableConnectivity = resource.disableConnectivity;
const newDisableConnectivity = forceState !== undefined ? forceState : !currentDisableConnectivity;
// Clean the thresholds to exclude any unwanted fields
@@ -521,25 +526,25 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
// If enabling connectivity alerts (disableConnectivity = false) and no custom thresholds exist, remove the override entirely
if (!newDisableConnectivity && Object.keys(cleanThresholds).length === 0) {
// Remove the override completely
- props.setOverrides(props.overrides().filter(o => o.id !== nodeId));
+ props.setOverrides(props.overrides().filter(o => o.id !== resourceId));
// Remove from raw config
const newRawConfig = { ...props.rawOverridesConfig() };
- delete newRawConfig[nodeId];
+ delete newRawConfig[resourceId];
props.setRawOverridesConfig(newRawConfig);
} else {
// Update or create the override
const override: Override = {
- id: nodeId,
- name: node.name,
- type: node.type,
- resourceType: node.resourceType,
+ id: resourceId,
+ name: resource.name,
+ type: resource.type as 'node' | 'guest' | 'storage',
+ resourceType: resource.resourceType,
disableConnectivity: newDisableConnectivity,
thresholds: cleanThresholds
};
// Update overrides list
- const existingIndex = props.overrides().findIndex(o => o.id === nodeId);
+ const existingIndex = props.overrides().findIndex(o => o.id === resourceId);
if (existingIndex >= 0) {
const newOverrides = [...props.overrides()];
newOverrides[existingIndex] = override;
@@ -566,7 +571,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
hysteresisThresholds.disableConnectivity = true;
}
- newRawConfig[nodeId] = hysteresisThresholds;
+ newRawConfig[resourceId] = hysteresisThresholds;
props.setRawOverridesConfig(newRawConfig);
}
@@ -915,7 +920,7 @@ export function ThresholdsTable(props: ThresholdsTableProps) {
onSaveEdit={saveEdit}
onCancelEdit={cancelEdit}
onRemoveOverride={removeOverride}
- onToggleDisabled={toggleDisabled}
+ onToggleNodeConnectivity={toggleNodeConnectivity}
editingId={editingId}
editingThresholds={editingThresholds}
setEditingThresholds={setEditingThresholds}
diff --git a/internal/alerts/alerts.go b/internal/alerts/alerts.go
index bc0f40eac..03fa8e432 100644
--- a/internal/alerts/alerts.go
+++ b/internal/alerts/alerts.go
@@ -1408,14 +1408,14 @@ func (m *Manager) checkPBSOffline(pbs models.PBSInstance) {
m.mu.Lock()
defer m.mu.Unlock()
- // Check if PBS offline alerts are disabled
- if override, exists := m.config.Overrides[pbs.ID]; exists && override.Disabled {
- // PBS alerts are disabled, clear any existing alert and return
+ // Check if PBS offline alerts are disabled via disableConnectivity flag
+ if override, exists := m.config.Overrides[pbs.ID]; exists && (override.Disabled || override.DisableConnectivity) {
+ // PBS connectivity alerts are disabled, clear any existing alert and return
if _, alertExists := m.activeAlerts[alertID]; alertExists {
delete(m.activeAlerts, alertID)
log.Debug().
Str("pbs", pbs.Name).
- Msg("PBS offline alert cleared (alerts disabled)")
+ Msg("PBS offline alert cleared (connectivity alerts disabled)")
}
return
}
diff --git a/scripts/hot-dev.sh b/scripts/hot-dev.sh
index 3cf99b771..9481ce4e9 100755
--- a/scripts/hot-dev.sh
+++ b/scripts/hot-dev.sh
@@ -40,8 +40,11 @@ pkill -f vite 2>/dev/null
pkill -f "npm run dev" 2>/dev/null
pkill -f "npm exec" 2>/dev/null
-# Kill Pulse binary (exact match only)
+# Kill Pulse binary - first try gracefully, then force
pkill -x "pulse" 2>/dev/null
+sleep 1
+# Force kill if still running
+pkill -9 -x "pulse" 2>/dev/null
# Force-kill ANYTHING on our ports
kill_port 7655
@@ -158,11 +161,22 @@ EOF
cleanup() {
echo ""
echo "Stopping services..."
- kill $BACKEND_PID 2>/dev/null
+ # Try graceful shutdown first
+ if [ -n "$BACKEND_PID" ] && kill -0 $BACKEND_PID 2>/dev/null; then
+ kill $BACKEND_PID 2>/dev/null
+ sleep 1
+ # Force kill if still running
+ if kill -0 $BACKEND_PID 2>/dev/null; then
+ echo "Backend not responding to SIGTERM, force killing..."
+ kill -9 $BACKEND_PID 2>/dev/null
+ fi
+ fi
rm -f vite.config.dev.ts
# Clean up any leftover Vite processes
pkill -f vite 2>/dev/null
pkill -f "npm run dev" 2>/dev/null
+ # Final cleanup of any stuck pulse processes
+ pkill -9 -x "pulse" 2>/dev/null
echo "Hot-dev stopped. To restart normal service, run: sudo systemctl start pulse-backend"
exit
}