diff --git a/internal/alerts/alerts.go b/internal/alerts/alerts.go index 9b44df4bd..d4858878a 100644 --- a/internal/alerts/alerts.go +++ b/internal/alerts/alerts.go @@ -10,6 +10,7 @@ import ( "time" "github.com/rcourtman/pulse-go-rewrite/internal/models" + "github.com/rcourtman/pulse-go-rewrite/internal/utils" "github.com/rs/zerolog/log" ) @@ -188,9 +189,10 @@ type Manager struct { // NewManager creates a new alert manager func NewManager() *Manager { + alertsDir := filepath.Join(utils.GetDataDir(), "alerts") m := &Manager{ activeAlerts: make(map[string]*Alert), - historyManager: NewHistoryManager("/var/lib/pulse/alerts"), + historyManager: NewHistoryManager(alertsDir), escalationStop: make(chan struct{}), alertRateLimit: make(map[string][]time.Time), recentAlerts: make(map[string]*Alert), @@ -1216,7 +1218,7 @@ func (m *Manager) SaveActiveAlerts() error { defer m.mu.RUnlock() // Create directory if it doesn't exist - alertsDir := "/var/lib/pulse/alerts" + alertsDir := filepath.Join(utils.GetDataDir(), "alerts") if err := os.MkdirAll(alertsDir, 0755); err != nil { return fmt.Errorf("failed to create alerts directory: %w", err) } @@ -1253,7 +1255,7 @@ func (m *Manager) LoadActiveAlerts() error { m.mu.Lock() defer m.mu.Unlock() - alertsFile := "/var/lib/pulse/alerts/active-alerts.json" + alertsFile := filepath.Join(utils.GetDataDir(), "alerts", "active-alerts.json") data, err := os.ReadFile(alertsFile) if err != nil { if os.IsNotExist(err) { diff --git a/internal/alerts/history.go b/internal/alerts/history.go index e333f69b8..5562de578 100644 --- a/internal/alerts/history.go +++ b/internal/alerts/history.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/rcourtman/pulse-go-rewrite/internal/utils" "github.com/rs/zerolog/log" ) @@ -41,7 +42,7 @@ type HistoryManager struct { // NewHistoryManager creates a new history manager func NewHistoryManager(dataDir string) *HistoryManager { if dataDir == "" { - dataDir = "/var/lib/pulse" + dataDir = utils.GetDataDir() } hm := &HistoryManager{ diff --git a/internal/api/config_handlers.go b/internal/api/config_handlers.go index a0ccec16f..8384ee20b 100644 --- a/internal/api/config_handlers.go +++ b/internal/api/config_handlers.go @@ -302,10 +302,28 @@ func (h *ConfigHandlers) HandleAddNode(w http.ResponseWriter, r *http.Request) { Msg("Added Proxmox cluster with auto-discovered endpoints") } } else { - // Ensure user has realm for PBS + // Parse PBS authentication details pbsUser := req.User - if req.User != "" && !strings.Contains(req.User, "@") { - pbsUser = req.User + "@pbs" // Default to @pbs realm if not specified + pbsTokenName := req.TokenName + + // Handle different token input formats + if req.TokenName != "" && req.TokenValue != "" { + // Check if token name contains the full format (user@realm!tokenname) + if strings.Contains(req.TokenName, "!") { + parts := strings.Split(req.TokenName, "!") + if len(parts) == 2 { + // Extract user from token ID if not already provided + if pbsUser == "" { + pbsUser = parts[0] + } + pbsTokenName = parts[1] + } + } + } + + // Ensure user has realm for PBS (if using user/password or token with user) + if pbsUser != "" && !strings.Contains(pbsUser, "@") { + pbsUser = pbsUser + "@pbs" // Default to @pbs realm if not specified } pbs := config.PBSInstance{ @@ -313,7 +331,7 @@ func (h *ConfigHandlers) HandleAddNode(w http.ResponseWriter, r *http.Request) { Host: req.Host, User: pbsUser, Password: req.Password, - TokenName: req.TokenName, + TokenName: pbsTokenName, TokenValue: req.TokenValue, Fingerprint: req.Fingerprint, VerifySSL: req.VerifySSL, @@ -505,17 +523,35 @@ func (h *ConfigHandlers) HandleTestConnection(w http.ResponseWriter, r *http.Req } // PBS test connection - // Ensure user has realm for PBS + // Parse PBS authentication details pbsUser := user - if user != "" && !strings.Contains(user, "@") { - pbsUser = user + "@pbs" // Default to @pbs realm if not specified + pbsTokenName := tokenName + + // Handle different token input formats + if req.TokenName != "" && req.TokenValue != "" { + // Check if token name contains the full format (user@realm!tokenname) + if strings.Contains(req.TokenName, "!") && tokenName == req.TokenName { + parts := strings.Split(req.TokenName, "!") + if len(parts) == 2 { + // Extract user from token ID if not already provided + if pbsUser == "" { + pbsUser = parts[0] + } + pbsTokenName = parts[1] + } + } + } + + // Ensure user has realm for PBS (if using user/password or token with user) + if pbsUser != "" && !strings.Contains(pbsUser, "@") { + pbsUser = pbsUser + "@pbs" // Default to @pbs realm if not specified } clientConfig := pbs.ClientConfig{ Host: host, User: pbsUser, Password: req.Password, - TokenName: tokenName, + TokenName: pbsTokenName, TokenValue: req.TokenValue, VerifySSL: req.VerifySSL, Fingerprint: req.Fingerprint, diff --git a/internal/config/config.go b/internal/config/config.go index 5c34d39ce..7c67996bc 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,7 +19,7 @@ type Config struct { FrontendHost string `envconfig:"FRONTEND_HOST" default:"0.0.0.0"` FrontendPort int `envconfig:"FRONTEND_PORT" default:"7655"` ConfigPath string `envconfig:"CONFIG_PATH" default:"/etc/pulse"` - DataPath string `envconfig:"DATA_PATH" default:"/data"` + DataPath string `envconfig:"DATA_PATH" default:"/var/lib/pulse"` // Proxmox VE connections PVEInstances []PVEInstance diff --git a/internal/utils/paths.go b/internal/utils/paths.go new file mode 100644 index 000000000..9a580d95a --- /dev/null +++ b/internal/utils/paths.go @@ -0,0 +1,13 @@ +package utils + +import ( + "os" +) + +// GetDataDir returns the data directory path from environment or default +func GetDataDir() string { + if dir := os.Getenv("PULSE_DATA_DIR"); dir != "" { + return dir + } + return "/var/lib/pulse" +} \ No newline at end of file diff --git a/pkg/pbs/client.go b/pkg/pbs/client.go index fa1a1fdc5..866434e6e 100644 --- a/pkg/pbs/client.go +++ b/pkg/pbs/client.go @@ -48,13 +48,31 @@ type auth struct { // NewClient creates a new PBS API client func NewClient(cfg ClientConfig) (*Client, error) { - // Parse user and realm - parts := strings.Split(cfg.User, "@") - if len(parts) != 2 { - return nil, fmt.Errorf("invalid user format, expected user@realm") + var user, realm string + + // For token auth, user might be empty or in a different format + if cfg.TokenName != "" && cfg.TokenValue != "" { + // Token authentication - user is optional + if cfg.User != "" { + parts := strings.Split(cfg.User, "@") + if len(parts) == 2 { + user = parts[0] + realm = parts[1] + } else { + // If no realm specified, default to pbs + user = cfg.User + realm = "pbs" + } + } + } else { + // Password authentication - user@realm format is required + parts := strings.Split(cfg.User, "@") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid user format, expected user@realm") + } + user = parts[0] + realm = parts[1] } - user := parts[0] - realm := parts[1] // Create HTTP client with proper TLS configuration httpClient := tlsutil.CreateHTTPClient(cfg.VerifySSL, cfg.Fingerprint)