package handlers import ( "bytes" "code-review/config" "code-review/services" "code-review/services/platforms" "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "io/ioutil" "log" "github.com/gin-gonic/gin" ) type WebhookHandler struct { reviewService *services.ReviewService } func NewWebhookHandler(reviewService *services.ReviewService) *WebhookHandler { return &WebhookHandler{ reviewService: reviewService, } } func (h *WebhookHandler) HandleWebhook(c *gin.Context) { platform := c.Param("platform") log.Printf("收到 webhook 请求: platform=%s", platform) // 验证 webhook 签名 if !h.verifySignature(c, platform) { log.Printf("webhook 签名验证失败: platform=%s", platform) c.JSON(400, gin.H{"error": "无效的签名"}) return } // 解析请求体 event, err := h.parseWebhookEvent(c, platform) if err != nil { log.Printf("解析 webhook 事件失败: platform=%s, error=%v", platform, err) c.JSON(400, gin.H{"error": err.Error()}) return } // 启动代码审查 if err := h.reviewService.Review(event); err != nil { log.Printf("代码审查失败: platform=%s, error=%v", platform, err) c.JSON(500, gin.H{"error": err.Error()}) return } log.Printf("代码审查完成: platform=%s", platform) c.JSON(200, gin.H{"message": "success"}) } func (h *WebhookHandler) verifySignature(c *gin.Context, platform string) bool { body, err := c.GetRawData() if err != nil { return false } c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) cfg := config.GetConfig() // 查找对应的平台配置 var gitConfig config.GitConfig found := false for _, p := range cfg.Git { if p.Name == platform { gitConfig = p found = true break } } if !found { return false } // 获取签名 signature := c.GetHeader(gitConfig.SignatureHeader) if signature == "" { return false } mac := hmac.New(sha256.New, []byte(gitConfig.Secret)) mac.Write(body) expectedMAC := hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(signature), []byte(expectedMAC)) || hmac.Equal([]byte(signature), []byte(gitConfig.Secret)) } func (h *WebhookHandler) parseWebhookEvent(c *gin.Context, platform string) (services.WebhookEvent, error) { cfg := config.GetConfig() // 查找对应的平台配置 var platformConfig config.GitConfig found := false for _, p := range cfg.Git { if p.Name == platform { platformConfig = p found = true break } } if !found { return nil, fmt.Errorf("不支持的平台: %s", platform) } // 获取事件类型 eventType := c.GetHeader(platformConfig.EventHeader) if eventType == "" { return nil, fmt.Errorf("未找到事件类型 header: %s", platformConfig.EventHeader) } var event services.WebhookEvent switch platformConfig.Type { case "gogs": event = platforms.NewGogsEvent( platformConfig.APIBase, platformConfig.Token, eventType, ) case "gitea": event = platforms.NewGiteaEvent( platformConfig.APIBase, platformConfig.Token, eventType, ) //case "gitee": // event = &platforms.GiteeEvent{ // ApiBase: platformConfig.APIBase, // Token: platformConfig.Token, // } case "gitlab": event = platforms.NewGitlabEvent( platformConfig.APIBase, platformConfig.Token, eventType, ) default: return nil, fmt.Errorf("不支持的平台: %s", platform) } // 只处理 push 和 pull_request 事件 //if eventType != "push" && eventType != "pull_request" { // return nil, fmt.Errorf("不支持的事件类型: %s", eventType) //} if err := c.ShouldBindJSON(event); err != nil { return nil, err } return event, nil }