package forgejo import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "net/http" ) type WebhookPayload struct { Action string `json:"action"` Issue WebhookIssue `json:"issue"` Comment WebhookComment `json:"comment"` } type WebhookIssue struct { Number int64 `json:"number"` Title string `json:"title"` State string `json:"state"` } type WebhookComment struct { ID int64 `json:"id"` Body string `json:"body"` User WebhookUser `json:"user"` } type WebhookUser struct { Login string `json:"login"` FullName string `json:"full_name"` } func VerifyWebhookSignature(r *http.Request, secret string) ([]byte, error) { signature := r.Header.Get("X-Forgejo-Signature") if signature == "" { return nil, fmt.Errorf("missing X-Forgejo-Signature header") } body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20)) if err != nil { return nil, fmt.Errorf("read body: %w", err) } mac := hmac.New(sha256.New, []byte(secret)) mac.Write(body) expectedMAC := hex.EncodeToString(mac.Sum(nil)) if !hmac.Equal([]byte(signature), []byte(expectedMAC)) { return nil, fmt.Errorf("invalid signature") } return body, nil } func ParseWebhookPayload(data []byte) (*WebhookPayload, error) { var payload WebhookPayload if err := json.Unmarshal(data, &payload); err != nil { return nil, fmt.Errorf("parse webhook payload: %w", err) } return &payload, nil }