package handlers import ( "fmt" "net/http" "os" "path/filepath" "strings" "github.com/Fimeg/RedFlag/aggregator-server/internal/config" "github.com/Fimeg/RedFlag/aggregator-server/internal/services" "github.com/gin-gonic/gin" ) // DownloadHandler handles agent binary downloads type DownloadHandler struct { agentDir string config *config.Config installTemplateService *services.InstallTemplateService } func NewDownloadHandler(agentDir string, cfg *config.Config) *DownloadHandler { return &DownloadHandler{ agentDir: agentDir, config: cfg, installTemplateService: services.NewInstallTemplateService(), } } // getServerURL determines the server URL with proper protocol detection func (h *DownloadHandler) getServerURL(c *gin.Context) string { // Priority 1: Use configured public URL if set if h.config.Server.PublicURL != "" { return h.config.Server.PublicURL } // Priority 2: Construct API server URL from configuration scheme := "http" host := h.config.Server.Host port := h.config.Server.Port // Use HTTPS if TLS is enabled in config if h.config.Server.TLS.Enabled { scheme = "https" } // For default host (0.0.0.0), use localhost for client connections if host == "0.0.0.0" { host = "localhost" } // Only include port if it's not the default for the protocol if (scheme == "http" && port != 80) || (scheme == "https" && port != 443) { return fmt.Sprintf("%s://%s:%d", scheme, host, port) } return fmt.Sprintf("%s://%s", scheme, host) } // DownloadAgent serves agent binaries for different platforms func (h *DownloadHandler) DownloadAgent(c *gin.Context) { platform := c.Param("platform") version := c.Query("version") // Optional version parameter for signed binaries // Validate platform to prevent directory traversal validPlatforms := map[string]bool{ "linux-amd64": true, "linux-arm64": true, "windows-amd64": true, "windows-arm64": true, } if !validPlatforms[platform] { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or unsupported platform"}) return } // Build filename based on platform filename := "redflag-agent" if strings.HasPrefix(platform, "windows") { filename += ".exe" } var agentPath string // Try to serve signed package first if version is specified // TODO: Implement database lookup for signed packages // if version != "" { // signedPackage, err := h.packageQueries.GetSignedPackage(version, platform) // if err == nil && fileExists(signedPackage.BinaryPath) { // agentPath = signedPackage.BinaryPath // } // } // Fallback to unsigned generic binary if agentPath == "" { agentPath = filepath.Join(h.agentDir, "binaries", platform, filename) } // Check if file exists if _, err := os.Stat(agentPath); os.IsNotExist(err) { c.JSON(http.StatusNotFound, gin.H{ "error": "Agent binary not found", "platform": platform, "version": version, }) return } // Handle both GET and HEAD requests if c.Request.Method == "HEAD" { c.Status(http.StatusOK) return } c.File(agentPath) } // DownloadUpdatePackage serves signed agent update packages func (h *DownloadHandler) DownloadUpdatePackage(c *gin.Context) { packageID := c.Param("package_id") // Validate package ID format (UUID) if len(packageID) != 36 { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid package ID format"}) return } // TODO: Implement actual package serving from database/filesystem // For now, return a placeholder response c.JSON(http.StatusNotImplemented, gin.H{ "error": "Update package download not yet implemented", "package_id": packageID, "message": "This will serve the signed update package file", }) } // InstallScript serves the installation script func (h *DownloadHandler) InstallScript(c *gin.Context) { platform := c.Param("platform") // Validate platform validPlatforms := map[string]bool{ "linux": true, "windows": true, } if !validPlatforms[platform] { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or unsupported platform"}) return } serverURL := h.getServerURL(c) scriptContent := h.generateInstallScript(platform, serverURL) c.Header("Content-Type", "text/plain") c.String(http.StatusOK, scriptContent) } func (h *DownloadHandler) generateInstallScript(platform, baseURL string) string { // Use template service to generate install scripts // For generic downloads, use placeholder values script, err := h.installTemplateService.RenderInstallScriptFromBuild( "", // Will be generated during install platform, // Platform (linux/windows) "latest", // Version fmt.Sprintf("%s/downloads/%s", baseURL, platform), // Binary URL fmt.Sprintf("%s/api/v1/config/", baseURL), // Config URL (placeholder) ) if err != nil { return fmt.Sprintf("# Error generating install script: %v", err) } return script }