init
This commit is contained in:
155
handlers/config.go
Normal file
155
handlers/config.go
Normal file
@ -0,0 +1,155 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code-review/config"
|
||||
"code-review/utils"
|
||||
|
||||
"log"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ConfigHandler struct {
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func NewConfigHandler(cfg *config.Config) *ConfigHandler {
|
||||
return &ConfigHandler{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// GetConfig 获取当前配置
|
||||
func (h *ConfigHandler) GetConfig(c *gin.Context) {
|
||||
// 使用 ToMap 方法转换配置
|
||||
configMap := h.cfg.ToMapHtml()
|
||||
c.JSON(http.StatusOK, configMap)
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置
|
||||
func (h *ConfigHandler) UpdateConfig(c *gin.Context) {
|
||||
// 首先解析为 map
|
||||
var configMap map[string]interface{}
|
||||
if err := c.ShouldBindJSON(&configMap); err != nil {
|
||||
log.Printf("解析配置 JSON 失败: %v", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("开始更新配置")
|
||||
|
||||
// 使用当前配置作为基础
|
||||
newConfig := *h.cfg
|
||||
|
||||
// 处理自动禁用配置
|
||||
if autoDisable, ok := configMap["auto_disable"].(map[string]interface{}); ok {
|
||||
log.Printf("更新自动禁用配置")
|
||||
if enabled, exists := autoDisable["enabled"].(bool); exists {
|
||||
newConfig.AutoDisableConfig.Enabled = enabled
|
||||
log.Printf("自动禁用功能状态设置为: %v", enabled)
|
||||
}
|
||||
if maxFailures, ok := autoDisable["max_failures"].(float64); ok {
|
||||
newConfig.AutoDisableConfig.MaxFailures = int(maxFailures)
|
||||
}
|
||||
if resetAfter, ok := autoDisable["reset_after"].(float64); ok {
|
||||
newConfig.AutoDisableConfig.ResetAfter = int(resetAfter)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 AI 配置
|
||||
newConfig.AIs = make([]config.AIConfig, 0)
|
||||
for key, value := range configMap {
|
||||
if strings.HasPrefix(key, "ais[") {
|
||||
if aiMap, ok := value.(map[string]interface{}); ok {
|
||||
aiConfig := config.AIConfig{
|
||||
Name: utils.GetString(aiMap, "name"),
|
||||
Type: utils.GetString(aiMap, "type"),
|
||||
APIKey: utils.GetString(aiMap, "api_key"),
|
||||
APIBase: utils.GetString(aiMap, "url"),
|
||||
Model: utils.GetString(aiMap, "model"),
|
||||
SystemMsg: utils.GetString(aiMap, "system_msg"),
|
||||
Temperature: utils.GetFloat64(aiMap, "temperature"),
|
||||
Stream: utils.GetBool(aiMap, "stream"),
|
||||
Weight: utils.GetInt(aiMap, "weight"),
|
||||
Priority: utils.GetInt(aiMap, "priority"),
|
||||
Enabled: utils.GetBool(aiMap, "enabled"),
|
||||
AutoDisable: utils.GetBool(aiMap, "auto_disable"),
|
||||
MaxFailures: utils.GetIntPtr(aiMap, "max_failures"),
|
||||
ResetAfter: utils.GetIntPtr(aiMap, "reset_after"),
|
||||
}
|
||||
log.Printf("添加 AI 配置: %s, 类型: %s", aiConfig.Name, aiConfig.Type)
|
||||
newConfig.AIs = append(newConfig.AIs, aiConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 Git 平台配置
|
||||
newConfig.Git = make([]config.GitConfig, 0)
|
||||
for key, value := range configMap {
|
||||
if strings.HasPrefix(key, "git[") {
|
||||
if p, ok := value.(map[string]interface{}); ok {
|
||||
platformType := utils.GetString(p, "type")
|
||||
platformConfig := config.GitConfig{
|
||||
Name: utils.GetString(p, "name"),
|
||||
Type: platformType,
|
||||
Token: utils.GetString(p, "token"),
|
||||
Secret: utils.GetString(p, "webhook_secret"),
|
||||
APIBase: utils.GetString(p, "api_base"),
|
||||
}
|
||||
|
||||
// 根据平台类型设置对应的 header
|
||||
switch platformType {
|
||||
case "gitlab":
|
||||
platformConfig.SignatureHeader = "X-Gitlab-Token"
|
||||
platformConfig.EventHeader = "X-Gitlab-Event"
|
||||
case "gogs":
|
||||
platformConfig.SignatureHeader = "X-Gogs-Signature"
|
||||
platformConfig.EventHeader = "X-Gogs-Event"
|
||||
case "github":
|
||||
platformConfig.SignatureHeader = "X-Hub-Signature"
|
||||
platformConfig.EventHeader = "X-GitHub-Event"
|
||||
case "gitee":
|
||||
platformConfig.SignatureHeader = "X-Gitee-Token"
|
||||
platformConfig.EventHeader = "X-Gitee-Event"
|
||||
default:
|
||||
// 如果前端传入了自定义的 header,则使用前端传入的值
|
||||
platformConfig.SignatureHeader = utils.GetString(p, "signature_header")
|
||||
platformConfig.EventHeader = utils.GetString(p, "event_header")
|
||||
}
|
||||
|
||||
log.Printf("添加 Git 平台配置: %s, 类型: %s, SignatureHeader: %s, EventHeader: %s",
|
||||
platformConfig.Name, platformConfig.Type,
|
||||
platformConfig.SignatureHeader, platformConfig.EventHeader)
|
||||
newConfig.Git = append(newConfig.Git, platformConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := config.Save(&newConfig); err != nil {
|
||||
log.Printf("保存配置失败: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("配置更新成功")
|
||||
h.cfg = &newConfig
|
||||
c.JSON(http.StatusOK, gin.H{"message": "配置更新成功"})
|
||||
}
|
||||
|
||||
func AuthRequired() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.GetHeader("X-Admin-Token")
|
||||
cfg := config.GetConfig()
|
||||
|
||||
if token == "" || token != cfg.AdminToken {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未授权访问"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
154
handlers/webhook.go
Normal file
154
handlers/webhook.go
Normal file
@ -0,0 +1,154 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user