651 lines
17 KiB
Go
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})
|
|
}
|