package handlers import ( "encoding/json" "fmt" "log" "net/http" "sync" "time" "git.linuxforward.com/byop/byop-engine/models" "github.com/gin-gonic/gin" ) // AutoDeployHandler handles auto-deployment related operations type AutoDeployHandler struct { autoDeploySettings *models.AutoDeploySettings settingsMutex sync.RWMutex deploymentQueue chan *models.AutoDeployRequest } // NewAutoDeployHandler creates a new AutoDeployHandler func NewAutoDeployHandler() *AutoDeployHandler { return &AutoDeployHandler{} } // RegisterRoutes registers routes for auto-deployment operations func (h *AutoDeployHandler) RegisterRoutes(r *gin.RouterGroup) { r.POST("/client", h.AutoDeployClient) r.POST("/webhook", h.AutoDeployWebhook) r.PUT("/settings", h.UpdateAutoDeploySettings) r.GET("/settings", h.GetAutoDeploySettings) } // AutoDeployClient handles requests to deploy for a client func (h *AutoDeployHandler) AutoDeployClient(c *gin.Context) { // Parse the request var request *models.AutoDeployRequest if err := c.ShouldBindJSON(&request); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid request body", }) return } // Check if auto-deployment is enabled h.settingsMutex.RLock() enabled := h.autoDeploySettings.Enabled h.settingsMutex.RUnlock() if !enabled { c.JSON(http.StatusServiceUnavailable, gin.H{ "error": "Auto-deployment is currently disabled", }) return } // Queue the deployment request select { case h.deploymentQueue <- request: // Successfully queued default: // Queue is full c.JSON(http.StatusServiceUnavailable, gin.H{ "error": "Deployment queue is full, try again later", }) return } // Return a success response c.JSON(http.StatusAccepted, gin.H{ "status": "queued", "message": "Deployment request has been queued", "clientId": request.ClientID, "queuedAt": time.Now(), }) } // AutoDeployWebhook handles webhook triggers for auto-deployment func (h *AutoDeployHandler) AutoDeployWebhook(c *gin.Context) { // Parse the webhook payload var payload *models.WebhookPayload if err := c.ShouldBindJSON(&payload); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid webhook payload", }) return } // Validate the webhook signature h.settingsMutex.RLock() secret := h.autoDeploySettings.WebhookSecret enabled := h.autoDeploySettings.Enabled h.settingsMutex.RUnlock() // This is a simplified signature check, in production use HMAC if c.GetHeader("X-Webhook-Signature") != secret { c.JSON(http.StatusUnauthorized, gin.H{ "error": "Invalid webhook signature", }) return } if !enabled { c.JSON(http.StatusServiceUnavailable, gin.H{ "error": "Auto-deployment is currently disabled", }) return } // Handle different event types switch payload.Event { case "client.created": // Check if we should auto-deploy for new clients h.settingsMutex.RLock() autoDeploy := h.autoDeploySettings.AutoDeployNewClients h.settingsMutex.RUnlock() if autoDeploy && payload.ClientID != "" { // Queue a deployment request request := &models.AutoDeployRequest{ ClientID: payload.ClientID, // Use defaults for other fields } select { case h.deploymentQueue <- request: // Successfully queued default: // Queue is full log.Printf("Deployment queue is full, skipping auto-deployment for client %s", payload.ClientID) } } case "deployment.requested": // Handle explicit deployment requests if payload.ClientID == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "client_id is required for deployment requests", }) return } // Parse the deployment data var deployData struct { TemplateID string `json:"template_id"` ProviderID string `json:"provider_id"` Region string `json:"region"` Tags map[string]string `json:"tags"` } if err := json.Unmarshal(payload.Data, &deployData); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid deployment data", }) return } // Queue the deployment request := &models.AutoDeployRequest{ ClientID: payload.ClientID, TemplateID: deployData.TemplateID, ProviderID: deployData.ProviderID, Region: deployData.Region, Tags: deployData.Tags, } select { case h.deploymentQueue <- request: // Successfully queued default: // Queue is full c.JSON(http.StatusServiceUnavailable, gin.H{ "error": "Deployment queue is full, try again later", }) return } default: // Unknown event type c.JSON(http.StatusBadRequest, gin.H{ "error": fmt.Sprintf("Unsupported event type: %s", payload.Event), }) return } // Return a success response c.JSON(http.StatusOK, gin.H{ "status": "received", "event": payload.Event, "timestamp": time.Now(), "requestId": payload.RequestID, }) } // UpdateAutoDeploySettings updates the auto-deployment settings func (h *AutoDeployHandler) UpdateAutoDeploySettings(c *gin.Context) { // Parse the settings var settings *models.AutoDeploySettings if err := c.ShouldBindJSON(&settings); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid settings format", }) return } // Validate settings if settings.DefaultProviderID == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "default_provider_id is required", }) return } if settings.DefaultTemplateID == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "default_template_id is required", }) return } // Update the settings h.settingsMutex.Lock() // Keep the webhook secret if not provided if settings.WebhookSecret == "" { settings.WebhookSecret = h.autoDeploySettings.WebhookSecret } h.autoDeploySettings = settings h.settingsMutex.Unlock() // Return the updated settings (without sensitive fields) h.settingsMutex.RLock() responseSettings := h.autoDeploySettings responseSettings.WebhookSecret = "********" // Hide the actual secret h.settingsMutex.RUnlock() c.JSON(http.StatusOK, responseSettings) } // GetAutoDeploySettings returns the current auto-deployment settings func (h *AutoDeployHandler) GetAutoDeploySettings(c *gin.Context) { // Get a copy of the settings h.settingsMutex.RLock() settings := h.autoDeploySettings settings.WebhookSecret = "********" // Hide the actual secret h.settingsMutex.RUnlock() // Return the settings c.JSON(http.StatusOK, settings) }