package handlers import ( "net/http" "os" "path/filepath" "github.com/Fimeg/RedFlag/aggregator-server/internal/services" "github.com/gin-gonic/gin" ) // BuildAgent handles the agent build endpoint func BuildAgent(c *gin.Context) { var req services.AgentSetupRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Create config builder configBuilder := services.NewConfigBuilder(req.ServerURL) // Build agent configuration config, err := configBuilder.BuildAgentConfig(req) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Create agent builder agentBuilder := services.NewAgentBuilder() // Generate build artifacts buildResult, err := agentBuilder.BuildAgentWithConfig(config) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Create response with native binary instructions response := gin.H{ "agent_id": config.AgentID, "config_file": buildResult.ConfigFile, "platform": buildResult.Platform, "config_version": config.ConfigVersion, "agent_version": config.AgentVersion, "build_time": buildResult.BuildTime, "next_steps": []string{ "1. Download native binary from server", "2. Place binary in /usr/local/bin/redflag-agent", "3. Set permissions: chmod 755 /usr/local/bin/redflag-agent", "4. Create config directory: mkdir -p /etc/redflag", "5. Save config to /etc/redflag/config.json", "6. Set config permissions: chmod 600 /etc/redflag/config.json", "7. Start service: systemctl enable --now redflag-agent", }, "configuration": config.PublicConfig, } c.JSON(http.StatusOK, response) } // GetBuildInstructions returns build instructions for manual setup func GetBuildInstructions(c *gin.Context) { agentID := c.Param("agentID") if agentID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "agent ID is required"}) return } instructions := gin.H{ "title": "RedFlag Agent Build Instructions", "agent_id": agentID, "steps": []gin.H{ { "step": 1, "title": "Prepare Build Environment", "commands": []string{ "mkdir -p redflag-build", "cd redflag-build", }, }, { "step": 2, "title": "Copy Agent Source Code", "commands": []string{ "cp -r ../aggregator-agent/* .", "ls -la", }, }, { "step": 3, "title": "Build Docker Image", "commands": []string{ "docker build -t redflag-agent:" + agentID[:8] + " .", }, }, { "step": 4, "title": "Create Docker Network", "commands": []string{ "docker network create redflag 2>/dev/null || true", }, }, { "step": 5, "title": "Deploy Agent", "commands": []string{ "docker compose up -d", }, }, { "step": 6, "title": "Verify Deployment", "commands": []string{ "docker compose logs -f", "docker ps", }, }, }, "troubleshooting": []gin.H{ { "issue": "Build fails with 'go mod download' errors", "solution": "Ensure go.mod and go.sum are copied correctly and internet connectivity is available", }, { "issue": "Container fails to start", "solution": "Check docker-compose.yml and ensure Docker secrets are created with 'echo \"secret-value\" | docker secret create secret-name -'", }, { "issue": "Agent cannot connect to server", "solution": "Verify server URL is accessible from container and firewall rules allow traffic", }, }, } c.JSON(http.StatusOK, instructions) } // DownloadBuildArtifacts provides download links for generated files func DownloadBuildArtifacts(c *gin.Context) { agentID := c.Param("agentID") fileType := c.Param("fileType") buildDir := c.Query("buildDir") // Validate agent ID parameter if agentID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "agent ID is required"}) return } if buildDir == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "build directory is required"}) return } // Security check: ensure the buildDir is within expected path absBuildDir, err := filepath.Abs(buildDir) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid build directory"}) return } // Construct file path based on type var filePath string switch fileType { case "compose": filePath = filepath.Join(absBuildDir, "docker-compose.yml") case "dockerfile": filePath = filepath.Join(absBuildDir, "Dockerfile") case "config": filePath = filepath.Join(absBuildDir, "pkg", "embedded", "config.go") default: c.JSON(http.StatusBadRequest, gin.H{"error": "invalid file type"}) return } // Check if file exists if _, err := os.Stat(filePath); os.IsNotExist(err) { c.JSON(http.StatusNotFound, gin.H{"error": "file not found"}) return } // Serve file for download c.FileAttachment(filePath, filepath.Base(filePath)) }