diff --git a/aggregator-server/cmd/server/main.go b/aggregator-server/cmd/server/main.go index 956eccb..cceb707 100644 --- a/aggregator-server/cmd/server/main.go +++ b/aggregator-server/cmd/server/main.go @@ -306,7 +306,11 @@ func main() { registrationTokenHandler := handlers.NewRegistrationTokenHandler(registrationTokenQueries, agentQueries, cfg) rateLimitHandler := handlers.NewRateLimitHandler(rateLimiter) downloadHandler := handlers.NewDownloadHandler(filepath.Join("/app"), cfg, packageQueries) - subsystemHandler := handlers.NewSubsystemHandler(subsystemQueries, commandQueries, signingService, securityLogger) + + // Create command factory for consistent command creation + commandFactory := command.NewFactory(commandQueries) + subsystemHandler := handlers.NewSubsystemHandler(subsystemQueries, commandQueries, commandFactory, signingService, securityLogger) + metricsHandler := handlers.NewMetricsHandler(metricsQueries, agentQueries, commandQueries) dockerReportsHandler := handlers.NewDockerReportsHandler(dockerQueries, agentQueries, commandQueries) storageMetricsHandler := handlers.NewStorageMetricsHandler(storageMetricsQueries) diff --git a/aggregator-server/internal/api/handlers/subsystems.go b/aggregator-server/internal/api/handlers/subsystems.go index 2575295..72c3c22 100644 --- a/aggregator-server/internal/api/handlers/subsystems.go +++ b/aggregator-server/internal/api/handlers/subsystems.go @@ -6,6 +6,7 @@ import ( "net/http" "time" + "github.com/Fimeg/RedFlag/aggregator-server/internal/command" "github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/Fimeg/RedFlag/aggregator-server/internal/services" @@ -17,14 +18,16 @@ import ( type SubsystemHandler struct { subsystemQueries *queries.SubsystemQueries commandQueries *queries.CommandQueries + commandFactory *command.Factory signingService *services.SigningService securityLogger *logging.SecurityLogger } -func NewSubsystemHandler(sq *queries.SubsystemQueries, cq *queries.CommandQueries, signingService *services.SigningService, securityLogger *logging.SecurityLogger) *SubsystemHandler { +func NewSubsystemHandler(sq *queries.SubsystemQueries, cq *queries.CommandQueries, cf *command.Factory, signingService *services.SigningService, securityLogger *logging.SecurityLogger) *SubsystemHandler { return &SubsystemHandler{ subsystemQueries: sq, commandQueries: cq, + commandFactory: cf, signingService: signingService, securityLogger: securityLogger, } @@ -249,14 +252,9 @@ func (h *SubsystemHandler) TriggerSubsystem(c *gin.Context) { return } - // Create command for the subsystem + // Create command for the subsystem using factory with idempotency commandType := "scan_" + subsystem - command := &models.AgentCommand{ - AgentID: agentID, - CommandType: commandType, - Status: "pending", - Source: "manual", // Manual trigger from UI (must be 'manual' or 'system' per DB constraint) - } + idempotencyKey := fmt.Sprintf("%s_%s_%d", agentID.String(), subsystem, time.Now().Unix()) // Log command creation attempt log.Printf("[INFO] [server] [command] creating_scan_command agent_id=%s subsystem=%s command_type=%s timestamp=%s", @@ -264,6 +262,23 @@ func (h *SubsystemHandler) TriggerSubsystem(c *gin.Context) { log.Printf("[HISTORY] [server] [scan_%s] command_creation_started agent_id=%s timestamp=%s", subsystem, agentID, time.Now().Format(time.RFC3339)) + command, err := h.commandFactory.CreateWithIdempotency( + agentID, + commandType, + map[string]interface{}{"subsystem": subsystem}, + idempotencyKey, + ) + if err != nil { + log.Printf("[ERROR] [server] [scan_%s] command_creation_failed agent_id=%s error=%v", subsystem, agentID, err) + log.Printf("[HISTORY] [server] [scan_%s] command_creation_failed error=\"%v\" timestamp=%s", + subsystem, err, time.Now().Format(time.RFC3339)) + + c.JSON(http.StatusInternalServerError, gin.H{ + "error": fmt.Sprintf("Failed to create %s scan command: %v", subsystem, err), + }) + return + } + err = h.signAndCreateCommand(command) if err != nil { log.Printf("[ERROR] [server] [scan_%s] command_creation_failed agent_id=%s error=%v", subsystem, agentID, err)