Files
verify/backend/internal/router/developer/extension.go
T
2026-04-27 17:22:56 +08:00

651 lines
17 KiB
Go

package developer
import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
"verification-platform-backend/internal/service"
"verification-platform-backend/pkg/response"
"github.com/gin-gonic/gin"
)
func handleGetWebhooks(c *gin.Context) {
userID := c.GetUint("user_id")
appID := c.Query("application_id")
var webhooks []struct {
model.WebhookConfig
ApplicationName string `json:"application_name"`
}
query := database.DB.Model(&model.WebhookConfig{}).
Select("webhook_configs.*, applications.name as application_name").
Joins("JOIN applications ON webhook_configs.application_id = applications.id").
Where("applications.user_id = ?", userID)
if appID != "" && appID != "all" {
query = query.Where("webhook_configs.application_id = ?", appID)
}
if err := query.Find(&webhooks).Error; err != nil {
response.Error(c, 500, "获取Webhook配置失败")
return
}
response.Success(c, webhooks)
}
func handleCreateWebhook(c *gin.Context) {
userID := c.GetUint("user_id")
var req struct {
ApplicationID uint `json:"application_id" binding:"required"`
Name string `json:"name" binding:"required"`
URL string `json:"url" binding:"required,url"`
SecretKey string `json:"secret_key"`
Events []string `json:"events" binding:"required"`
RetryCount int `json:"retry_count"`
Timeout int `json:"timeout"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
var app model.Application
if err := database.DB.Where("id = ? AND user_id = ?", req.ApplicationID, userID).First(&app).Error; err != nil {
response.Error(c, 404, "应用不存在")
return
}
eventsJSON, _ := json.Marshal(req.Events)
if req.RetryCount == 0 {
req.RetryCount = 3
}
if req.Timeout == 0 {
req.Timeout = 10
}
webhook := model.WebhookConfig{
ApplicationID: req.ApplicationID,
Name: req.Name,
URL: req.URL,
SecretKey: req.SecretKey,
Events: string(eventsJSON),
Status: "active",
RetryCount: req.RetryCount,
Timeout: req.Timeout,
}
if err := database.DB.Create(&webhook).Error; err != nil {
response.Error(c, 500, "创建Webhook配置失败")
return
}
service.LogOperation(c, "create", "webhook", &webhook.ID, fmt.Sprintf("创建Webhook: %s", webhook.Name), nil)
response.Success(c, webhook)
}
func handleUpdateWebhook(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var req struct {
Name string `json:"name"`
URL string `json:"url" binding:"omitempty,url"`
SecretKey string `json:"secret_key"`
Events []string `json:"events"`
Status string `json:"status"`
RetryCount int `json:"retry_count"`
Timeout int `json:"timeout"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
var webhook model.WebhookConfig
if err := database.DB.Joins("JOIN applications ON webhook_configs.application_id = applications.id").
Where("webhook_configs.id = ? AND applications.user_id = ?", id, userID).
First(&webhook).Error; err != nil {
response.Error(c, 404, "Webhook配置不存在")
return
}
updates := make(map[string]interface{})
if req.Name != "" {
updates["name"] = req.Name
}
if req.URL != "" {
updates["url"] = req.URL
}
if req.SecretKey != "" {
updates["secret_key"] = req.SecretKey
}
if len(req.Events) > 0 {
eventsJSON, _ := json.Marshal(req.Events)
updates["events"] = string(eventsJSON)
}
if req.Status != "" {
updates["status"] = req.Status
}
if req.RetryCount > 0 {
updates["retry_count"] = req.RetryCount
}
if req.Timeout > 0 {
updates["timeout"] = req.Timeout
}
if err := database.DB.Model(&webhook).Updates(updates).Error; err != nil {
response.Error(c, 500, "更新Webhook配置失败")
return
}
response.Success(c, webhook)
}
func handleDeleteWebhook(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var webhook model.WebhookConfig
if err := database.DB.Joins("JOIN applications ON webhook_configs.application_id = applications.id").
Where("webhook_configs.id = ? AND applications.user_id = ?", id, userID).
First(&webhook).Error; err != nil {
response.Error(c, 404, "Webhook配置不存在")
return
}
webhookName := webhook.Name
webhookID := webhook.ID
if err := database.DB.Delete(&webhook).Error; err != nil {
response.Error(c, 500, "删除Webhook配置失败")
return
}
service.LogOperation(c, "delete", "webhook", &webhookID, fmt.Sprintf("删除Webhook: %s", webhookName), nil)
response.Success(c, nil)
}
func handleGetWebhookLogs(c *gin.Context) {
userID := c.GetUint("user_id")
webhookID := c.Query("webhook_id")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
var logs []model.WebhookLog
var total int64
query := database.DB.Model(&model.WebhookLog{}).
Joins("JOIN webhook_configs ON webhook_logs.webhook_id = webhook_configs.id").
Joins("JOIN applications ON webhook_configs.application_id = applications.id").
Where("applications.user_id = ?", userID)
if webhookID != "" {
query = query.Where("webhook_logs.webhook_id = ?", webhookID)
}
query.Count(&total)
offset := (page - 1) * pageSize
if err := query.Order("webhook_logs.created_at DESC").
Offset(offset).Limit(pageSize).
Find(&logs).Error; err != nil {
response.Error(c, 500, "获取Webhook日志失败")
return
}
response.Success(c, gin.H{
"logs": logs,
"total": total,
"page": page,
"page_size": pageSize,
"total_page": (total + int64(pageSize) - 1) / int64(pageSize),
})
}
func handleTestWebhook(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var webhook model.WebhookConfig
if err := database.DB.Joins("JOIN applications ON webhook_configs.application_id = applications.id").
Where("webhook_configs.id = ? AND applications.user_id = ?", id, userID).
First(&webhook).Error; err != nil {
response.Error(c, 404, "Webhook配置不存在")
return
}
testData := map[string]interface{}{
"event": "test",
"timestamp": time.Now().Unix(),
"data": map[string]interface{}{
"message": "This is a test webhook",
},
}
go sendWebhook(&webhook, testData)
response.Success(c, gin.H{"message": "测试请求已发送"})
}
func handleGetAPIKeys(c *gin.Context) {
userID := c.GetUint("user_id")
appID := c.Query("application_id")
var apiKeys []struct {
model.ExtensionAPIKey
ApplicationName string `json:"application_name"`
}
query := database.DB.Model(&model.ExtensionAPIKey{}).
Select("extension_api_keys.*, applications.name as application_name").
Joins("JOIN applications ON extension_api_keys.application_id = applications.id").
Where("applications.user_id = ?", userID)
if appID != "" && appID != "all" {
query = query.Where("extension_api_keys.application_id = ?", appID)
}
if err := query.Find(&apiKeys).Error; err != nil {
response.Error(c, 500, "获取API密钥失败")
return
}
response.Success(c, apiKeys)
}
func handleCreateAPIKey(c *gin.Context) {
userID := c.GetUint("user_id")
var req struct {
ApplicationID uint `json:"application_id" binding:"required"`
Name string `json:"name" binding:"required"`
Permissions []string `json:"permissions"`
ExpiresAt *string `json:"expires_at"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
var app model.Application
if err := database.DB.Where("id = ? AND user_id = ?", req.ApplicationID, userID).First(&app).Error; err != nil {
response.Error(c, 404, "应用不存在")
return
}
accessKey := generateRandomKey(32)
secretKey := generateRandomKey(32)
permissionsJSON, _ := json.Marshal(req.Permissions)
apiKey := model.ExtensionAPIKey{
ApplicationID: req.ApplicationID,
Name: req.Name,
AccessKey: accessKey,
SecretKey: secretKey,
Permissions: string(permissionsJSON),
Status: "active",
}
if req.ExpiresAt != nil {
expiresAt, err := time.Parse(time.RFC3339, *req.ExpiresAt)
if err == nil {
apiKey.ExpiresAt = &expiresAt
}
}
if err := database.DB.Create(&apiKey).Error; err != nil {
response.Error(c, 500, "创建API密钥失败")
return
}
response.Success(c, apiKey)
}
func handleUpdateAPIKey(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var req struct {
Name string `json:"name"`
Permissions []string `json:"permissions"`
Status string `json:"status"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
var apiKey model.ExtensionAPIKey
if err := database.DB.Joins("JOIN applications ON extension_api_keys.application_id = applications.id").
Where("extension_api_keys.id = ? AND applications.user_id = ?", id, userID).
First(&apiKey).Error; err != nil {
response.Error(c, 404, "API密钥不存在")
return
}
updates := make(map[string]interface{})
if req.Name != "" {
updates["name"] = req.Name
}
if len(req.Permissions) > 0 {
permissionsJSON, _ := json.Marshal(req.Permissions)
updates["permissions"] = string(permissionsJSON)
}
if req.Status != "" {
updates["status"] = req.Status
}
if err := database.DB.Model(&apiKey).Updates(updates).Error; err != nil {
response.Error(c, 500, "更新API密钥失败")
return
}
response.Success(c, apiKey)
}
func handleDeleteAPIKey(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var apiKey model.ExtensionAPIKey
if err := database.DB.Joins("JOIN applications ON extension_api_keys.application_id = applications.id").
Where("extension_api_keys.id = ? AND applications.user_id = ?", id, userID).
First(&apiKey).Error; err != nil {
response.Error(c, 404, "API密钥不存在")
return
}
if err := database.DB.Delete(&apiKey).Error; err != nil {
response.Error(c, 500, "删除API密钥失败")
return
}
response.Success(c, nil)
}
func handleRegenerateAPIKey(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var apiKey model.ExtensionAPIKey
if err := database.DB.Joins("JOIN applications ON extension_api_keys.application_id = applications.id").
Where("extension_api_keys.id = ? AND applications.user_id = ?", id, userID).
First(&apiKey).Error; err != nil {
response.Error(c, 404, "API密钥不存在")
return
}
newSecretKey := generateRandomKey(32)
if err := database.DB.Model(&apiKey).Update("secret_key", newSecretKey).Error; err != nil {
response.Error(c, 500, "重新生成密钥失败")
return
}
apiKey.SecretKey = newSecretKey
response.Success(c, apiKey)
}
func generateRandomKey(length int) string {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return ""
}
return hex.EncodeToString(bytes)[:length*2]
}
func sendWebhook(webhook *model.WebhookConfig, data map[string]interface{}) {
jsonData, _ := json.Marshal(data)
startTime := time.Now()
client := &http.Client{
Timeout: time.Duration(webhook.Timeout) * time.Second,
}
req, err := http.NewRequest("POST", webhook.URL, bytes.NewReader(jsonData))
if err != nil {
logWebhookError(webhook.ID, data, err, time.Since(startTime).Milliseconds())
return
}
req.Header.Set("Content-Type", "application/json")
if webhook.SecretKey != "" {
req.Header.Set("X-Webhook-Secret", webhook.SecretKey)
}
resp, err := client.Do(req)
duration := time.Since(startTime).Milliseconds()
if err != nil {
logWebhookError(webhook.ID, data, err, duration)
return
}
defer resp.Body.Close()
logWebhookSuccess(webhook.ID, data, resp.StatusCode, duration)
}
func logWebhookSuccess(webhookID uint, requestData map[string]interface{}, statusCode int, duration int64) {
requestJSON, _ := json.Marshal(requestData)
log := model.WebhookLog{
WebhookID: webhookID,
Event: requestData["event"].(string),
RequestData: string(requestJSON),
ResponseCode: statusCode,
Status: "success",
Duration: int(duration),
}
database.DB.Create(&log)
}
func logWebhookError(webhookID uint, requestData map[string]interface{}, err error, duration int64) {
requestJSON, _ := json.Marshal(requestData)
log := model.WebhookLog{
WebhookID: webhookID,
Event: requestData["event"].(string),
RequestData: string(requestJSON),
Status: "failed",
ErrorMessage: err.Error(),
Duration: int(duration),
}
database.DB.Create(&log)
}
func handleUpdateWebhookStatus(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var req struct {
Status string `json:"status" binding:"required,oneof=active inactive"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
var webhook model.WebhookConfig
if err := database.DB.Joins("JOIN applications ON webhook_configs.application_id = applications.id").
Where("webhook_configs.id = ? AND applications.user_id = ?", id, userID).
First(&webhook).Error; err != nil {
response.Error(c, 404, "Webhook配置不存在")
return
}
if err := database.DB.Model(&webhook).Update("status", req.Status).Error; err != nil {
response.Error(c, 500, "更新状态失败")
return
}
response.Success(c, webhook)
}
func handleBatchUpdateWebhookStatus(c *gin.Context) {
userID := c.GetUint("user_id")
var req struct {
IDs []uint `json:"ids" binding:"required"`
Status string `json:"status" binding:"required,oneof=active inactive"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if len(req.IDs) == 0 {
response.Error(c, 400, "请选择要更新的Webhook")
return
}
result := database.DB.Model(&model.WebhookConfig{}).
Joins("JOIN applications ON webhook_configs.application_id = applications.id").
Where("applications.user_id = ? AND webhook_configs.id IN ?", userID, req.IDs).
Update("status", req.Status)
if result.Error != nil {
response.Error(c, 500, "批量更新状态失败")
return
}
response.Success(c, gin.H{"updated_count": result.RowsAffected})
}
func handleBatchDeleteWebhooks(c *gin.Context) {
userID := c.GetUint("user_id")
var req struct {
IDs []uint `json:"ids" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if len(req.IDs) == 0 {
response.Error(c, 400, "请选择要删除的Webhook")
return
}
result := database.DB.Where("id IN (?) AND application_id IN (SELECT id FROM applications WHERE user_id = ?)", req.IDs, userID).
Delete(&model.WebhookConfig{})
if result.Error != nil {
response.Error(c, 500, "批量删除失败")
return
}
response.Success(c, gin.H{"deleted_count": result.RowsAffected})
}
func handleUpdateAPIKeyStatus(c *gin.Context) {
userID := c.GetUint("user_id")
id := c.Param("id")
var req struct {
Status string `json:"status" binding:"required,oneof=active inactive"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
var apiKey model.ExtensionAPIKey
if err := database.DB.Joins("JOIN applications ON extension_api_keys.application_id = applications.id").
Where("extension_api_keys.id = ? AND applications.user_id = ?", id, userID).
First(&apiKey).Error; err != nil {
response.Error(c, 404, "API密钥不存在")
return
}
if err := database.DB.Model(&apiKey).Update("status", req.Status).Error; err != nil {
response.Error(c, 500, "更新状态失败")
return
}
response.Success(c, apiKey)
}
func handleBatchUpdateAPIKeyStatus(c *gin.Context) {
userID := c.GetUint("user_id")
var req struct {
IDs []uint `json:"ids" binding:"required"`
Status string `json:"status" binding:"required,oneof=active inactive"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if len(req.IDs) == 0 {
response.Error(c, 400, "请选择要更新的API密钥")
return
}
result := database.DB.Model(&model.ExtensionAPIKey{}).
Joins("JOIN applications ON extension_api_keys.application_id = applications.id").
Where("applications.user_id = ? AND extension_api_keys.id IN ?", userID, req.IDs).
Update("status", req.Status)
if result.Error != nil {
response.Error(c, 500, "批量更新状态失败")
return
}
response.Success(c, gin.H{"updated_count": result.RowsAffected})
}
func handleBatchDeleteAPIKeys(c *gin.Context) {
userID := c.GetUint("user_id")
var req struct {
IDs []uint `json:"ids" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误: "+err.Error())
return
}
if len(req.IDs) == 0 {
response.Error(c, 400, "请选择要删除的API密钥")
return
}
result := database.DB.Where("id IN (?) AND application_id IN (SELECT id FROM applications WHERE user_id = ?)", req.IDs, userID).
Delete(&model.ExtensionAPIKey{})
if result.Error != nil {
response.Error(c, 500, "批量删除失败")
return
}
response.Success(c, gin.H{"deleted_count": result.RowsAffected})
}