All checks were successful
Check / check (pull_request) Successful in 1m50s
Add auto-detection of webhook source (Gitea, GitHub, GitLab) by examining HTTP headers (X-Gitea-Event, X-GitHub-Event, X-Gitlab-Event). Parse push webhook payloads from all three platforms into a normalized PushEvent type for unified processing. Each platform's payload format is handled by dedicated parser functions with correct field mapping and commit URL extraction. The webhook handler now detects the source automatically — existing Gitea webhooks continue to work unchanged, while GitHub and GitLab webhooks are parsed with their respective payload formats. Includes comprehensive tests for source detection, event type extraction, payload parsing for all three platforms, commit URL fallback logic, and integration tests via HandleWebhook.
79 lines
1.9 KiB
Go
79 lines
1.9 KiB
Go
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)
|
|
}
|
|
}
|