package handlers import ( "io" "net/http" "github.com/go-chi/chi/v5" "sneak.berlin/go/upaas/internal/models" "sneak.berlin/go/upaas/internal/service/webhook" ) // maxWebhookBodySize is the maximum allowed size of a webhook request body (1MB). const maxWebhookBodySize = 1 << 20 // HandleWebhook handles incoming webhooks from Gitea, GitHub, or GitLab. // The webhook source is auto-detected from HTTP headers. func (h *Handlers) HandleWebhook() http.HandlerFunc { return func(writer http.ResponseWriter, request *http.Request) { secret := chi.URLParam(request, "secret") if secret == "" { http.NotFound(writer, request) return } // Find app by webhook secret application, findErr := models.FindAppByWebhookSecret( request.Context(), h.db, secret, ) if findErr != nil { h.log.Error("failed to find app by webhook secret", "error", findErr) http.Error(writer, "Internal Server Error", http.StatusInternalServerError) return } if application == nil { http.NotFound(writer, request) return } // Read request body with size limit to prevent memory exhaustion body, readErr := io.ReadAll(io.LimitReader(request.Body, maxWebhookBodySize)) if readErr != nil { h.log.Error("failed to read webhook body", "error", readErr) http.Error(writer, "Bad Request", http.StatusBadRequest) return } // Auto-detect webhook source from headers source := webhook.DetectWebhookSource(request.Header) // Extract event type based on detected source eventType := webhook.DetectEventType(request.Header, source) // Process webhook webhookErr := h.webhook.HandleWebhook( request.Context(), application, source, eventType, body, ) if webhookErr != nil { h.log.Error("failed to process webhook", "error", webhookErr) http.Error(writer, "Internal Server Error", http.StatusInternalServerError) return } writer.WriteHeader(http.StatusOK) } }