162 lines
3.8 KiB
Go
162 lines
3.8 KiB
Go
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)
|
|
}
|
|
|
|
// 创建认证配置
|
|
auth := &platforms.AuthConfig{
|
|
Token: platformConfig.Token,
|
|
Username: platformConfig.Username,
|
|
Password: platformConfig.Password,
|
|
SudoUser: platformConfig.SudoUser,
|
|
TOTP: platformConfig.TOTP,
|
|
UseBasicAuth: platformConfig.Username != "" && platformConfig.Password != "",
|
|
UseSudoHeader: platformConfig.SudoUser != "",
|
|
UseSudoParam: platformConfig.SudoUser != "",
|
|
UseTOTPHeader: platformConfig.TOTP != "",
|
|
}
|
|
|
|
var event services.WebhookEvent
|
|
switch platformConfig.Type {
|
|
case "gitea":
|
|
event = platforms.NewGiteaEvent(
|
|
platformConfig.APIBase,
|
|
auth,
|
|
eventType,
|
|
)
|
|
// case "gitee":
|
|
// event = platforms.NewGiteeEvent(
|
|
// platformConfig.APIBase,
|
|
// auth,
|
|
// )
|
|
case "gitlab":
|
|
event = platforms.NewGitlabEvent(
|
|
platformConfig.APIBase,
|
|
auth,
|
|
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
|
|
}
|