feat: add toggle to disable network discovery

Addresses #343 - users can now disable Proxmox/PBS server discovery through:
- UI toggle in Settings > System > Network Settings
- Environment variable DISCOVERY_ENABLED=false
- system.json configuration

Discovery runs by default but can be completely disabled for environments where automatic scanning causes issues (e.g., shared hosting networks).
This commit is contained in:
Pulse Monitor
2025-08-21 21:13:29 +00:00
parent 45f974e026
commit 1d7ad85bfd
8 changed files with 122 additions and 12 deletions
+4 -1
View File
@@ -67,7 +67,9 @@ ENABLE_AUDIT_LOG=true # Enable security audit logging
"autoUpdateTime": "03:00", // Time for automatic updates (24hr format)
"allowedOrigins": "", // CORS allowed origins (empty = same-origin only)
"backendPort": 7655, // Backend API port
"frontendPort": 7655 // Frontend UI port (same as backend in embedded mode)
"frontendPort": 7655, // Frontend UI port (same as backend in embedded mode)
"discoveryEnabled": true, // Enable/disable network discovery for Proxmox/PBS servers
"discoverySubnet": "auto" // Subnet to scan ("auto" or CIDR like "192.168.1.0/24")
}
```
@@ -130,6 +132,7 @@ Settings are loaded in this order (later overrides earlier):
#### Configuration Variables (override system.json)
These env vars override system.json values. When set, the UI will show a warning and disable the affected fields:
- `DISCOVERY_ENABLED` - Enable/disable network discovery (default: true)
- `DISCOVERY_SUBNET` - Custom network to scan (default: auto-scans common networks)
- `CONNECTION_TIMEOUT` - API timeout in seconds (default: 10)
- `ALLOWED_ORIGINS` - CORS origins (default: same-origin only)
+4
View File
@@ -17,6 +17,10 @@ bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/c
**Auto-discovery (Easiest)**: Settings → Nodes → Click "Setup Script" on discovered node → Run on Proxmox
**Manual**: Settings → Nodes → Add Node → Enter credentials → Save
### How do I disable network discovery?
Settings → System → Network Settings → Toggle "Enable Discovery" off → Save
Or set environment variable `DISCOVERY_ENABLED=false`
### How do I change the port?
Systemd: `sudo systemctl edit pulse-backend`, add `Environment="FRONTEND_PORT=8080"`, restart
Docker: Use `-e FRONTEND_PORT=8080` in your run command
@@ -98,6 +98,8 @@ const Settings: Component = () => {
// System settings
// PBS polling interval removed - fixed at 10 seconds
const [allowedOrigins, setAllowedOrigins] = createSignal('*');
const [discoveryEnabled, setDiscoveryEnabled] = createSignal(true);
const [discoverySubnet, setDiscoverySubnet] = createSignal('auto');
const [envOverrides, setEnvOverrides] = createSignal<Record<string, boolean>>({});
// Connection timeout removed - backend-only setting
@@ -354,6 +356,9 @@ const Settings: Component = () => {
// PBS polling interval is now fixed at 10 seconds
setAllowedOrigins(systemSettings.allowedOrigins || '*');
// Connection timeout is backend-only
// Load discovery settings
setDiscoveryEnabled(systemSettings.discoveryEnabled !== false); // Default to true
setDiscoverySubnet(systemSettings.discoverySubnet || 'auto');
// Load auto-update settings
setAutoUpdateEnabled(systemSettings.autoUpdateEnabled || false);
setAutoUpdateCheckInterval(systemSettings.autoUpdateCheckInterval || 24);
@@ -396,6 +401,8 @@ const Settings: Component = () => {
// PBS polling interval is now fixed at 10 seconds
allowedOrigins: allowedOrigins(),
// Connection timeout is backend-only
discoveryEnabled: discoveryEnabled(),
discoverySubnet: discoverySubnet(),
updateChannel: updateChannel(),
autoUpdateEnabled: autoUpdateEnabled(),
autoUpdateCheckInterval: autoUpdateCheckInterval(),
@@ -1202,6 +1209,78 @@ const Settings: Component = () => {
</div>
</div>
{/* Discovery Settings */}
<div class="mt-4 pt-4 border-t border-gray-200 dark:border-gray-600">
<label class="text-sm font-medium text-gray-900 dark:text-gray-100">Network Discovery</label>
<p class="text-xs text-gray-600 dark:text-gray-400 mb-2">Automatically scan for Proxmox/PBS servers on your network</p>
{/* Discovery Toggle */}
<div class="flex items-center justify-between mb-3">
<span class="text-sm text-gray-700 dark:text-gray-300">Enable Discovery</span>
<label class="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={discoveryEnabled()}
onChange={(e) => {
if (!envOverrides().discoveryEnabled) {
setDiscoveryEnabled(e.currentTarget.checked);
setHasUnsavedChanges(true);
}
}}
disabled={envOverrides().discoveryEnabled}
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600 peer-disabled:opacity-50 peer-disabled:cursor-not-allowed"></div>
</label>
</div>
{/* Discovery Subnet */}
<Show when={discoveryEnabled()}>
<div>
<label class="text-sm text-gray-700 dark:text-gray-300">Discovery Subnet</label>
<p class="text-xs text-gray-600 dark:text-gray-400 mb-1">Use "auto" for automatic detection or specify CIDR (e.g., 192.168.1.0/24)</p>
<input
type="text"
value={discoverySubnet()}
onChange={(e) => {
if (!envOverrides().discoverySubnet) {
setDiscoverySubnet(e.currentTarget.value);
setHasUnsavedChanges(true);
}
}}
disabled={envOverrides().discoverySubnet}
placeholder="auto"
class={`w-full px-3 py-1.5 text-sm border rounded-lg ${
envOverrides().discoverySubnet
? 'border-amber-300 dark:border-amber-600 bg-amber-50 dark:bg-amber-900/20 cursor-not-allowed opacity-75'
: 'border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800'
}`}
/>
{envOverrides().discoverySubnet && (
<div class="mt-2 p-2 bg-amber-100 dark:bg-amber-900/30 border border-amber-300 dark:border-amber-700 rounded text-xs text-amber-800 dark:text-amber-200">
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span>Overridden by DISCOVERY_SUBNET environment variable</span>
</div>
</div>
)}
</div>
</Show>
{envOverrides().discoveryEnabled && (
<div class="mt-2 p-2 bg-amber-100 dark:bg-amber-900/30 border border-amber-300 dark:border-amber-700 rounded text-xs text-amber-800 dark:text-amber-200">
<div class="flex items-center gap-1">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span>Overridden by DISCOVERY_ENABLED environment variable</span>
</div>
</div>
)}
</div>
<div class="mt-3 p-3 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg">
<p class="text-xs text-amber-800 dark:text-amber-200 mb-2">
<strong>Port Configuration:</strong> Edit <code class="font-mono bg-amber-100 dark:bg-amber-800 px-1 rounded">/etc/pulse/.env</code>
+2
View File
@@ -37,6 +37,8 @@ export interface SystemConfig {
backendPort?: number; // Backend API port (default: 7655)
frontendPort?: number; // Frontend UI port (default: 7655)
theme?: string; // Theme preference: 'light' | 'dark' | undefined (system default)
discoveryEnabled?: boolean; // Enable/disable network discovery
discoverySubnet?: string; // Subnet to scan for discovery (default: 'auto')
}
/**
+6
View File
@@ -108,6 +108,12 @@ func (h *SystemSettingsHandler) HandleUpdateSystemSettings(w http.ResponseWriter
http.Error(w, "Invalid theme value. Must be 'light', 'dark', or empty", http.StatusBadRequest)
return
}
// Update discovery settings
h.config.DiscoveryEnabled = settings.DiscoveryEnabled
if settings.DiscoverySubnet != "" {
h.config.DiscoverySubnet = settings.DiscoverySubnet
}
// Save to persistence
if err := h.persistence.SaveSystemSettings(settings); err != nil {
+11 -1
View File
@@ -102,7 +102,8 @@ type Config struct {
AutoUpdateTime string `envconfig:"AUTO_UPDATE_TIME" default:"03:00"`
// Discovery settings
DiscoverySubnet string `envconfig:"DISCOVERY_SUBNET" default:"auto"`
DiscoveryEnabled bool `envconfig:"DISCOVERY_ENABLED" default:"true"`
DiscoverySubnet string `envconfig:"DISCOVERY_SUBNET" default:"auto"`
// Deprecated - for backward compatibility
Port int `envconfig:"PORT"` // Maps to BackendPort
@@ -209,6 +210,7 @@ func Load() (*Config, error) {
PollingInterval: 10 * time.Second, // Deprecated - not used
PVEPollingInterval: 10 * time.Second, // Deprecated - not used
PBSPollingInterval: 60 * time.Second, // Default PBS polling (slower)
DiscoveryEnabled: true,
DiscoverySubnet: "auto",
EnvOverrides: make(map[string]bool),
}
@@ -271,6 +273,8 @@ func Load() (*Config, error) {
if systemSettings.LogLevel != "" {
cfg.LogLevel = systemSettings.LogLevel
}
// Always load DiscoveryEnabled even if false
cfg.DiscoveryEnabled = systemSettings.DiscoveryEnabled
if systemSettings.DiscoverySubnet != "" {
cfg.DiscoverySubnet = systemSettings.DiscoverySubnet
}
@@ -409,6 +413,11 @@ func Load() (*Config, error) {
}
// Support env vars for important settings (override system.json)
// NOTE: Environment variables always take precedence over UI/system.json settings
if discoveryEnabled := os.Getenv("DISCOVERY_ENABLED"); discoveryEnabled != "" {
cfg.DiscoveryEnabled = discoveryEnabled == "true" || discoveryEnabled == "1"
cfg.EnvOverrides["discoveryEnabled"] = true
log.Info().Bool("enabled", cfg.DiscoveryEnabled).Msg("Discovery enabled overridden by DISCOVERY_ENABLED env var")
}
if discoverySubnet := os.Getenv("DISCOVERY_SUBNET"); discoverySubnet != "" {
cfg.DiscoverySubnet = discoverySubnet
cfg.EnvOverrides["discoverySubnet"] = true
@@ -477,6 +486,7 @@ func SaveConfig(cfg *Config) error {
AllowedOrigins: cfg.AllowedOrigins,
ConnectionTimeout: int(cfg.ConnectionTimeout.Seconds()),
LogLevel: cfg.LogLevel,
DiscoveryEnabled: cfg.DiscoveryEnabled,
DiscoverySubnet: cfg.DiscoverySubnet,
// APIToken removed - now handled via .env only
}
+1
View File
@@ -313,6 +313,7 @@ type SystemSettings struct {
AutoUpdateCheckInterval int `json:"autoUpdateCheckInterval,omitempty"`
AutoUpdateTime string `json:"autoUpdateTime,omitempty"`
LogLevel string `json:"logLevel,omitempty"`
DiscoveryEnabled bool `json:"discoveryEnabled"`
DiscoverySubnet string `json:"discoverySubnet,omitempty"`
Theme string `json:"theme,omitempty"` // User theme preference: "light", "dark", or empty for system default
// APIToken removed - now handled via .env file only
+15 -10
View File
@@ -294,17 +294,22 @@ func (m *Monitor) Start(ctx context.Context, wsHub *websocket.Hub) {
Dur("pollingInterval", 10*time.Second).
Msg("Starting monitoring loop")
// Initialize and start discovery service
discoverySubnet := m.config.DiscoverySubnet
if discoverySubnet == "" {
discoverySubnet = "auto"
}
m.discoveryService = discovery.NewService(wsHub, 5*time.Minute, discoverySubnet)
if m.discoveryService != nil {
m.discoveryService.Start(ctx)
log.Info().Msg("Discovery service initialized and started")
// Initialize and start discovery service if enabled
if m.config.DiscoveryEnabled {
discoverySubnet := m.config.DiscoverySubnet
if discoverySubnet == "" {
discoverySubnet = "auto"
}
m.discoveryService = discovery.NewService(wsHub, 5*time.Minute, discoverySubnet)
if m.discoveryService != nil {
m.discoveryService.Start(ctx)
log.Info().Msg("Discovery service initialized and started")
} else {
log.Error().Msg("Failed to initialize discovery service")
}
} else {
log.Error().Msg("Failed to initialize discovery service")
log.Info().Msg("Discovery service disabled by configuration")
m.discoveryService = nil
}
// Set up alert callbacks