201 lines
5.4 KiB
Go
201 lines
5.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/Fimeg/RedFlag/aggregator-server/internal/database/queries"
|
|
"github.com/Fimeg/RedFlag/aggregator-server/internal/services"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// AgentBuildHandler handles agent build operations
|
|
type AgentBuildHandler struct {
|
|
agentQueries *queries.AgentQueries
|
|
}
|
|
|
|
// NewAgentBuildHandler creates a new agent build handler
|
|
func NewAgentBuildHandler(agentQueries *queries.AgentQueries) *AgentBuildHandler {
|
|
return &AgentBuildHandler{
|
|
agentQueries: agentQueries,
|
|
}
|
|
}
|
|
|
|
// BuildAgent handles the agent build endpoint
|
|
// Deprecated: Use AgentHandler.Rebuild instead
|
|
func (h *AgentBuildHandler) 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 with database access
|
|
configBuilder := services.NewConfigBuilder(req.ServerURL, h.agentQueries.DB)
|
|
|
|
// 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 (h *AgentBuildHandler) 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 (h *AgentBuildHandler) 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))
|
|
}
|