package handlers import ( "io" "log/slog" "net/http" "strings" "git.unkin.net/unkin/forgebot/internal/database" "git.unkin.net/unkin/forgebot/internal/provider/gitea" "git.unkin.net/unkin/forgebot/pkg/models" ) type WebhookHandler struct { db *database.DB provider *gitea.Client webhookSecret string } func NewWebhookHandler(db *database.DB, provider *gitea.Client, secret string) *WebhookHandler { return &WebhookHandler{ db: db, provider: provider, webhookSecret: secret, } } func (h *WebhookHandler) HandleGitea(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "failed to read body", http.StatusBadRequest) return } signature := r.Header.Get("X-Gitea-Signature") if !gitea.VerifySignature(body, h.webhookSecret, signature) { http.Error(w, "invalid signature", http.StatusUnauthorized) return } event, err := h.provider.ParseWebhook(body, h.webhookSecret) if err != nil { slog.Error("failed to parse webhook", "error", err) http.Error(w, "failed to parse webhook", http.StatusBadRequest) return } if event == nil { w.WriteHeader(http.StatusOK) return } commands := models.ParseCommands(event.Body) if len(commands) == 0 { w.WriteHeader(http.StatusOK) return } parts := strings.SplitN(event.Repository, "/", 2) if len(parts) != 2 { http.Error(w, "invalid repository format", http.StatusBadRequest) return } for _, cmd := range commands { task, err := h.db.CreateTask(r.Context(), models.CreateTaskRequest{ Command: cmd.Name, Repository: event.Repository, Ref: event.Ref, IssueNumber: event.IssueNum, PRNumber: event.PRNum, CommentID: event.CommentID, Body: cmd.Args, Author: event.Author, }) if err != nil { slog.Error("failed to create task", "error", err, "command", cmd.Name) continue } slog.Info("task created from webhook", "id", task.ID, "command", cmd.Name, "repository", event.Repository, "author", event.Author, ) h.provider.AddReaction(parts[0], parts[1], event.CommentID, "eyes") } w.WriteHeader(http.StatusAccepted) }