refactor(config): rename BackendHost/BackendPort to BindAddress

Simplify server config by consolidating BackendHost and BackendPort into
a single BindAddress field. The port is now solely controlled by FrontendPort.

Changes:
- Replace BackendHost/BackendPort with BindAddress in Config struct
- Add deprecation warning for BACKEND_HOST env var (use BIND_ADDRESS)
- Update connection timeout default from 45s to 60s
- Remove backendPort from SystemSettings and frontend types
- Update server.go to use cfg.BindAddress
- Update all tests to use new config field names
This commit is contained in:
rcourtman
2026-02-01 23:26:32 +00:00
parent b0aaca863d
commit 4af5fc4246
12 changed files with 23 additions and 44 deletions

View File

@@ -909,7 +909,7 @@ func TestRunServer_FrontendFail(t *testing.T) {
// Keep l open
defer l.Close()
t.Setenv("BACKEND_HOST", "127.0.0.1")
t.Setenv("BIND_ADDRESS", "127.0.0.1")
// Set frontend port to busy port
t.Setenv("FRONTEND_PORT", fmt.Sprintf("%d", port))

View File

@@ -37,7 +37,6 @@ export interface SystemConfig {
temperatureMonitoringEnabled?: boolean; // Collect CPU/NVMe temperatures via SSH
sshPort?: number; // SSH port for temperature monitoring (default: 22)
allowedOrigins?: string; // CORS allowed origins
backendPort?: number; // Backend API port (default: 7655)
frontendPort?: number; // Frontend UI port (default: 7655)
theme?: string; // Theme preference: 'light' | 'dark' | undefined (system default)
fullWidthMode?: boolean; // Full-width layout mode preference
@@ -181,7 +180,7 @@ export const DEFAULT_CONFIG: {
system: SystemConfig;
} = {
system: {
connectionTimeout: 10,
connectionTimeout: 60,
autoUpdateEnabled: false,
updateChannel: 'stable',
autoUpdateCheckInterval: 24,
@@ -191,7 +190,6 @@ export const DEFAULT_CONFIG: {
temperatureMonitoringEnabled: true,
sshPort: 22,
allowedOrigins: '',
backendPort: 7655,
frontendPort: 7655,
},
};

View File

@@ -934,7 +934,7 @@ func TestAISettingsHandler_SetConfig(t *testing.T) {
handler.SetConfig(nil)
// SetConfig with new config should update the handler's config
newCfg := &config.Config{DataPath: tmp, BackendPort: 9999}
newCfg := &config.Config{DataPath: tmp}
handler.SetConfig(newCfg)
// No assertion needed - just verifying it doesn't panic
}

View File

@@ -3268,7 +3268,6 @@ func (h *ConfigHandlers) HandleGetSystemSettings(w http.ResponseWriter, r *http.
settings.PVEPollingInterval = int(h.getConfig(r.Context()).PVEPollingInterval.Seconds())
settings.PBSPollingInterval = int(h.getConfig(r.Context()).PBSPollingInterval.Seconds())
settings.BackupPollingInterval = int(h.getConfig(r.Context()).BackupPollingInterval.Seconds())
settings.BackendPort = h.getConfig(r.Context()).BackendPort
settings.FrontendPort = h.getConfig(r.Context()).FrontendPort
settings.AllowedOrigins = h.getConfig(r.Context()).AllowedOrigins
settings.ConnectionTimeout = int(h.getConfig(r.Context()).ConnectionTimeout.Seconds())

View File

@@ -334,7 +334,6 @@ func TestHandleGetSystemSettings_ConfigOverrides(t *testing.T) {
PVEPollingInterval: 30 * time.Second,
PBSPollingInterval: 90 * time.Second,
BackupPollingInterval: 12 * time.Second,
BackendPort: 8081,
FrontendPort: 3000,
AllowedOrigins: "https://example.com",
ConnectionTimeout: 15 * time.Second,

View File

@@ -15,18 +15,16 @@ func TestRouter_ConfigUpdates(t *testing.T) {
// Initialize Router with minimal dependencies
tmpDir := t.TempDir()
cfg := &config.Config{
BackendPort: 8080,
DataPath: tmpDir,
ConfigPath: tmpDir,
DataPath: tmpDir,
ConfigPath: tmpDir,
}
router := NewRouter(cfg, nil, nil, nil, nil, "1.0.0")
// Test SetConfig
newCfg := &config.Config{
BackendPort: 9090,
DataPath: tmpDir,
ConfigPath: tmpDir,
DataPath: tmpDir,
ConfigPath: tmpDir,
}
router.SetConfig(newCfg)

View File

@@ -44,7 +44,6 @@ func newIntegrationServerWithConfig(t *testing.T, customize func(*config.Config)
tmpDir := t.TempDir()
cfg := &config.Config{
BackendPort: 7655,
ConfigPath: tmpDir,
DataPath: tmpDir,
DemoMode: false,

View File

@@ -79,8 +79,7 @@ func IsPasswordHashed(password string) bool {
// NOTE: The envconfig tags are legacy and not used - configuration is loaded from encrypted JSON files
type Config struct {
// Server settings
BackendPort int
BackendHost string
BindAddress string
FrontendPort int `envconfig:"FRONTEND_PORT" default:"7655"`
ConfigPath string
DataPath string
@@ -101,7 +100,7 @@ type Config struct {
PVEPollingInterval time.Duration `envconfig:"PVE_POLLING_INTERVAL"` // PVE polling interval (10s default)
PBSPollingInterval time.Duration `envconfig:"PBS_POLLING_INTERVAL"` // PBS polling interval (60s default)
PMGPollingInterval time.Duration `envconfig:"PMG_POLLING_INTERVAL"` // PMG polling interval (60s default)
ConnectionTimeout time.Duration `envconfig:"CONNECTION_TIMEOUT" default:"45s"` // Increased for slow storage operations
ConnectionTimeout time.Duration `envconfig:"CONNECTION_TIMEOUT" default:"60s"` // Default 60s for slow storage operations
BackupPollingCycles int `envconfig:"BACKUP_POLLING_CYCLES" default:"10"`
BackupPollingInterval time.Duration `envconfig:"BACKUP_POLLING_INTERVAL"`
EnableBackupPolling bool `envconfig:"ENABLE_BACKUP_POLLING" default:"true"`
@@ -620,8 +619,7 @@ func Load() (*Config, error) {
// Initialize config with defaults
cfg := &Config{
BackendPort: 3000,
BackendHost: "0.0.0.0",
BindAddress: "0.0.0.0",
FrontendPort: 7655,
ConfigPath: dataDir,
DataPath: dataDir,
@@ -1121,9 +1119,14 @@ func Load() (*Config, error) {
cfg.EnvOverrides["ALLOWED_ORIGINS"] = true
}
if backendHost := utils.GetenvTrim("BACKEND_HOST"); backendHost != "" {
cfg.BackendHost = backendHost
cfg.EnvOverrides["BACKEND_HOST"] = true
if bindAddr := utils.GetenvTrim("BIND_ADDRESS"); bindAddr != "" {
cfg.BindAddress = bindAddr
cfg.EnvOverrides["BIND_ADDRESS"] = true
} else if backendHost := utils.GetenvTrim("BACKEND_HOST"); backendHost != "" {
// Deprecated: BACKEND_HOST is the old name for BIND_ADDRESS
log.Warn().Msg("BACKEND_HOST is deprecated, use BIND_ADDRESS instead")
cfg.BindAddress = backendHost
cfg.EnvOverrides["BIND_ADDRESS"] = true
}
if sshPort := utils.GetenvTrim("SSH_PORT"); sshPort != "" {
@@ -1628,9 +1631,6 @@ func SaveOIDCConfig(settings *OIDCConfig) error {
// Validate checks if the configuration is valid
func (c *Config) Validate() error {
// Validate server settings
if c.BackendPort <= 0 || c.BackendPort > 65535 {
return fmt.Errorf("invalid backend port: %d", c.BackendPort)
}
if c.FrontendPort <= 0 || c.FrontendPort > 65535 {
return fmt.Errorf("invalid frontend port: %d", c.FrontendPort)
}

View File

@@ -10,7 +10,6 @@ import (
func getValidConfig() *Config {
return &Config{
FrontendPort: 7655,
BackendPort: 7656,
PVEPollingInterval: 30 * time.Second,
ConnectionTimeout: 10 * time.Second,
AdaptivePollingMinInterval: 10 * time.Second,
@@ -32,18 +31,6 @@ func TestConfig_Validate(t *testing.T) {
mutate: func(c *Config) {},
isValid: true,
},
{
name: "Invalid Backend Port Low",
mutate: func(c *Config) { c.BackendPort = 0 },
isValid: false,
errMsg: "invalid backend port",
},
{
name: "Invalid Backend Port High",
mutate: func(c *Config) { c.BackendPort = 65536 },
isValid: false,
errMsg: "invalid backend port",
},
{
name: "Invalid Frontend Port Low",
mutate: func(c *Config) { c.FrontendPort = 0 },

View File

@@ -1058,7 +1058,6 @@ type SystemSettings struct {
AdaptivePollingBaseInterval int `json:"adaptivePollingBaseInterval,omitempty"`
AdaptivePollingMinInterval int `json:"adaptivePollingMinInterval,omitempty"`
AdaptivePollingMaxInterval int `json:"adaptivePollingMaxInterval,omitempty"`
BackendPort int `json:"backendPort,omitempty"`
FrontendPort int `json:"frontendPort,omitempty"`
AllowedOrigins string `json:"allowedOrigins,omitempty"`
ConnectionTimeout int `json:"connectionTimeout,omitempty"`

View File

@@ -126,7 +126,7 @@ func Run(ctx context.Context, version string) error {
defer cancel()
// Metrics port is configurable via MetricsPort variable
metricsAddr := fmt.Sprintf("%s:%d", cfg.BackendHost, MetricsPort)
metricsAddr := fmt.Sprintf("%s:%d", cfg.BindAddress, MetricsPort)
startMetricsServer(ctx, metricsAddr)
// Initialize WebSocket hub first
@@ -250,7 +250,7 @@ func Run(ctx context.Context, version string) error {
// Create HTTP server with unified configuration
srv := &http.Server{
Addr: fmt.Sprintf("%s:%d", cfg.BackendHost, cfg.FrontendPort),
Addr: fmt.Sprintf("%s:%d", cfg.BindAddress, cfg.FrontendPort),
Handler: router.Handler(),
ReadHeaderTimeout: 15 * time.Second,
WriteTimeout: 0, // Disabled to support SSE/streaming
@@ -291,7 +291,7 @@ func Run(ctx context.Context, version string) error {
go func() {
if cfg.HTTPSEnabled && cfg.TLSCertFile != "" && cfg.TLSKeyFile != "" {
log.Info().
Str("host", cfg.BackendHost).
Str("host", cfg.BindAddress).
Int("port", cfg.FrontendPort).
Str("protocol", "HTTPS").
Msg("Server listening")
@@ -303,7 +303,7 @@ func Run(ctx context.Context, version string) error {
log.Warn().Msg("HTTPS_ENABLED is true but TLS_CERT_FILE or TLS_KEY_FILE not configured, falling back to HTTP")
}
log.Info().
Str("host", cfg.BackendHost).
Str("host", cfg.BindAddress).
Int("port", cfg.FrontendPort).
Str("protocol", "HTTP").
Msg("Server listening")

View File

@@ -80,7 +80,7 @@ func TestServerRun_Shutdown(t *testing.T) {
// Create a dummy config.yaml
configFile := filepath.Join(tmpDir, "config.yaml")
// Use 0 port to try to avoid conflicts, though Run() might default it.
if err := os.WriteFile(configFile, []byte("backendHost: 127.0.0.1\nfrontendPort: 0"), 0644); err != nil {
if err := os.WriteFile(configFile, []byte("bindAddress: 127.0.0.1\nfrontendPort: 0"), 0644); err != nil {
t.Fatal(err)
}