Log every webhook request, not just dispatch failures
Some checks failed
CI / test (push) Has been cancelled

The handler was silent on success, unauthorized signatures, and unhandled
events, making it impossible to tell from server logs alone whether a
delivery even arrived. Now every request logs its event type and resulting
status code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
awayatan 2026-07-27 00:06:43 +09:00
parent e433aa3538
commit e95794680b

View file

@ -7,6 +7,7 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"log" "log"
"net/http" "net/http"
@ -30,50 +31,57 @@ func NewHandler(secret string, dispatcher Dispatcher) *Handler {
} }
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost { event := firstHeader(r, "X-Forgejo-Event", "X-Gitea-Event")
http.Error(w, "method not allowed", http.StatusMethodNotAllowed) status, err := h.handle(w, r, event)
if err != nil {
log.Printf("yaju-keisatsu: %s event -> %d: %v", event, status, err)
} else {
log.Printf("yaju-keisatsu: %s event -> %d", event, status)
}
if status != http.StatusNoContent {
http.Error(w, http.StatusText(status), status)
return return
} }
w.WriteHeader(status)
}
func (h *Handler) handle(w http.ResponseWriter, r *http.Request, event string) (int, error) {
if r.Method != http.MethodPost {
return http.StatusMethodNotAllowed, nil
}
body, err := io.ReadAll(io.LimitReader(r.Body, 10<<20)) // 10MiB cap body, err := io.ReadAll(io.LimitReader(r.Body, 10<<20)) // 10MiB cap
if err != nil { if err != nil {
http.Error(w, "read body", http.StatusBadRequest) return http.StatusBadRequest, fmt.Errorf("read body: %w", err)
return
} }
if !h.validSignature(r, body) { if !h.validSignature(r, body) {
http.Error(w, "invalid signature", http.StatusUnauthorized) return http.StatusUnauthorized, fmt.Errorf("invalid signature")
return
} }
event := firstHeader(r, "X-Forgejo-Event", "X-Gitea-Event")
var dispatchErr error
switch event { switch event {
case "issue_comment": case "issue_comment":
var p forgejo.IssueCommentPayload var p forgejo.IssueCommentPayload
if err := json.Unmarshal(body, &p); err != nil { if err := json.Unmarshal(body, &p); err != nil {
http.Error(w, "decode payload", http.StatusBadRequest) return http.StatusBadRequest, fmt.Errorf("decode payload: %w", err)
return }
if err := h.dispatcher.HandleIssueComment(p); err != nil {
return http.StatusInternalServerError, err
} }
dispatchErr = h.dispatcher.HandleIssueComment(p)
case "push": case "push":
var p forgejo.PushPayload var p forgejo.PushPayload
if err := json.Unmarshal(body, &p); err != nil { if err := json.Unmarshal(body, &p); err != nil {
http.Error(w, "decode payload", http.StatusBadRequest) return http.StatusBadRequest, fmt.Errorf("decode payload: %w", err)
return }
if err := h.dispatcher.HandlePush(p); err != nil {
return http.StatusInternalServerError, err
} }
dispatchErr = h.dispatcher.HandlePush(p)
default: default:
// Unhandled event types are not an error — the webhook may be // Unhandled event types are not an error — the webhook may be
// subscribed to more events than this bot reacts to. // subscribed to more events than this bot reacts to.
} }
if dispatchErr != nil { return http.StatusNoContent, nil
log.Printf("yaju-keisatsu: handling %s event: %v", event, dispatchErr)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusNoContent)
} }
// validSignature checks X-Forgejo-Signature (falling back to // validSignature checks X-Forgejo-Signature (falling back to