package services import ( "context" "encoding/json" "fmt" "log" "time" "github.com/Fimeg/RedFlag/aggregator-server/internal/config" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/google/uuid" "github.com/jmoiron/sqlx" ) // ConfigService manages agent configuration generation and validation type ConfigService struct { db *sqlx.DB config *config.Config logger *log.Logger } // NewConfigService creates a new configuration service func NewConfigService(db *sqlx.DB, cfg *config.Config, logger *log.Logger) *ConfigService { return &ConfigService{ db: db, config: cfg, logger: logger, } } // AgentConfigData represents agent configuration structure type AgentConfigData struct { AgentID string `json:"agent_id"` Version string `json:"version"` Platform string `json:"platform"` ServerURL string `json:"server_url"` LogLevel string `json:"log_level"` Intervals map[string]int `json:"intervals"` Subsystems map[string]interface{} `json:"subsystems"` MaxRetries int `json:"max_retries"` TimeoutSeconds int `json:"timeout_seconds"` MachineID string `json:"machine_id"` AgentType string `json:"agent_type"` ConfigPath string `json:"config_path"` StatePath string `json:"state_path"` LogPath string `json:"log_path"` ServiceName string `json:"service_name"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // GenerateNewConfig creates configuration for a new agent func (s *ConfigService) GenerateNewConfig(agentCfg *AgentConfig) ([]byte, error) { // Base configuration serverURL := fmt.Sprintf("http://%s:%d", s.config.Server.Host, s.config.Server.Port) if s.config.Server.PublicURL != "" { serverURL = s.config.Server.PublicURL } cfg := &AgentConfigData{ AgentID: agentCfg.AgentID, Version: agentCfg.Version, Platform: agentCfg.Platform, ServerURL: serverURL, LogLevel: "info", Intervals: map[string]int{ "metrics": 300, // 5 minutes "updates": 3600, // 1 hour "commands": 30, // 30 seconds }, Subsystems: map[string]interface{}{ "updates": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 300}, "storage": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 60}, "system": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 60}, "docker": map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 120}, }, MaxRetries: 3, TimeoutSeconds: 30, MachineID: agentCfg.MachineID, AgentType: agentCfg.AgentType, CreatedAt: time.Now(), UpdatedAt: time.Now(), } // Platform-specific customizations s.applyPlatformDefaults(cfg) // Validate configuration if err := s.validateConfig(cfg); err != nil { return nil, fmt.Errorf("validation failed: %w", err) } // Marshal to JSON configJSON, err := json.MarshalIndent(cfg, "", " ") if err != nil { return nil, fmt.Errorf("marshal failed: %w", err) } return configJSON, nil } // LoadExistingConfig retrieves and updates existing agent configuration func (s *ConfigService) LoadExistingConfig(agentID string) ([]byte, error) { // Get existing agent from database var agent models.Agent query := `SELECT * FROM agents WHERE id = $1` err := s.db.Get(&agent, query, agentID) if err != nil { return nil, fmt.Errorf("agent not found: %w", err) } // Generate new config based on agent data agentCfg := &AgentConfig{ AgentID: agentID, Version: agent.CurrentVersion, Platform: agent.OSType, Architecture: agent.OSArchitecture, MachineID: "", AgentType: "", // Could be stored in Metadata Hostname: agent.Hostname, } // Use GenerateNewConfig to create config return s.GenerateNewConfig(agentCfg) } // applyPlatformDefaults applies platform-specific configuration func (s *ConfigService) applyPlatformDefaults(cfg *AgentConfigData) { switch cfg.Platform { case "windows-amd64", "windows-arm64", "windows-386": // Windows-specific paths cfg.ConfigPath = "C:\\ProgramData\\RedFlag\\config.json" cfg.StatePath = "C:\\ProgramData\\RedFlag\\state\\" cfg.LogPath = "C:\\ProgramData\\RedFlag\\logs\\" cfg.ServiceName = "RedFlagAgent" // Windows-specific subsystems cfg.Subsystems["windows"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 300} cfg.Subsystems["winget"] = map[string]interface{}{"enabled": true, "auto_run": true, "timeout": 180} default: // Linux defaults cfg.ConfigPath = "/etc/redflag/config.json" cfg.StatePath = "/var/lib/redflag/" cfg.LogPath = "/var/log/redflag/" cfg.ServiceName = "redflag-agent" } } // validateConfig validates configuration func (s *ConfigService) validateConfig(cfg *AgentConfigData) error { if cfg.AgentID == "" { return fmt.Errorf("agent_id is required") } if cfg.Version == "" { return fmt.Errorf("version is required") } if cfg.Platform == "" { return fmt.Errorf("platform is required") } if cfg.ServerURL == "" { return fmt.Errorf("server_url is required") } return nil } // SaveConfig saves agent configuration to database func (s *ConfigService) SaveConfig(ctx context.Context, agentID uuid.UUID, configJSON []byte) error { query := ` UPDATE agents SET config = $1, updated_at = $2 WHERE id = $3 ` _, err := s.db.ExecContext(ctx, query, configJSON, time.Now(), agentID) return err }