diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 47a17140a..2bfc1afd8 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -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) diff --git a/docs/FAQ.md b/docs/FAQ.md index 4a880be42..6f6c941c1 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -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 diff --git a/frontend-modern/src/components/Settings/Settings.tsx b/frontend-modern/src/components/Settings/Settings.tsx index 771831479..52efb7685 100644 --- a/frontend-modern/src/components/Settings/Settings.tsx +++ b/frontend-modern/src/components/Settings/Settings.tsx @@ -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>({}); // 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 = () => { + {/* Discovery Settings */} +
+ +

Automatically scan for Proxmox/PBS servers on your network

+ + {/* Discovery Toggle */} +
+ Enable Discovery + +
+ + {/* Discovery Subnet */} + +
+ +

Use "auto" for automatic detection or specify CIDR (e.g., 192.168.1.0/24)

+ { + 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 && ( +
+
+ + + + Overridden by DISCOVERY_SUBNET environment variable +
+
+ )} +
+
+ + {envOverrides().discoveryEnabled && ( +
+
+ + + + Overridden by DISCOVERY_ENABLED environment variable +
+
+ )} +
+

Port Configuration: Edit /etc/pulse/.env diff --git a/frontend-modern/src/types/config.ts b/frontend-modern/src/types/config.ts index 116dc6d5e..ef88ad706 100644 --- a/frontend-modern/src/types/config.ts +++ b/frontend-modern/src/types/config.ts @@ -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') } /** diff --git a/internal/api/system_settings.go b/internal/api/system_settings.go index 87b41f929..aefbe8b7c 100644 --- a/internal/api/system_settings.go +++ b/internal/api/system_settings.go @@ -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 { diff --git a/internal/config/config.go b/internal/config/config.go index f325672a4..ab9c55429 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -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 } diff --git a/internal/config/persistence.go b/internal/config/persistence.go index c8593788f..29c460103 100644 --- a/internal/config/persistence.go +++ b/internal/config/persistence.go @@ -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 diff --git a/internal/monitoring/monitor.go b/internal/monitoring/monitor.go index 56c5eb50e..07790bd43 100644 --- a/internal/monitoring/monitor.go +++ b/internal/monitoring/monitor.go @@ -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