mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-12 17:40:54 +08:00
【修复】长期持有tcp连接未关闭
【新增】支持通过webhook调用自己的服务解析dns记录 【新增】支持通过webhook推送证书和密钥 【新增】导入导出工作流、通知、证书、api授权数据 【新增】支持自定义插件目录
This commit is contained in:
147
backend/public/webhook.go
Normal file
147
backend/public/webhook.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WebhookConfig struct {
|
||||
Url string `json:"url"`
|
||||
Data string `json:"data,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Headers string `json:"headers,omitempty"`
|
||||
IgnoreSSL bool `json:"ignore_ssl,omitempty"`
|
||||
}
|
||||
|
||||
func (w *WebhookConfig) Send() error {
|
||||
// 确定HTTP方法
|
||||
method := strings.ToUpper(w.Method)
|
||||
if method == "" {
|
||||
method = http.MethodPost // 默认使用POST方法
|
||||
}
|
||||
|
||||
client := resty.New()
|
||||
client.SetTimeout(30 * time.Second)
|
||||
|
||||
if w.IgnoreSSL {
|
||||
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||
}
|
||||
|
||||
req := client.R()
|
||||
|
||||
// 设置请求头
|
||||
if w.Headers != "" {
|
||||
reqHeader, err := w.ParseHeaders(w.Headers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析请求头错误: %w", err)
|
||||
}
|
||||
req.Header = reqHeader
|
||||
}
|
||||
|
||||
switch method {
|
||||
case http.MethodPost:
|
||||
{
|
||||
contentType := req.Header.Get("application/json")
|
||||
if contentType == "" {
|
||||
contentType = "application/json"
|
||||
}
|
||||
switch contentType {
|
||||
case "application/json":
|
||||
req.SetHeader("Content-Type", "application/json")
|
||||
var reqData interface{}
|
||||
err := json.Unmarshal([]byte(w.Data), &reqData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("webhook数据解析失败err: %w", err)
|
||||
}
|
||||
|
||||
req.SetBody(reqData)
|
||||
case "application/x-www-form-urlencoded":
|
||||
req.SetHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
reqData := make(map[string]string)
|
||||
err := json.Unmarshal([]byte(w.Data), &reqData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("webhook数据解析失败err: %w", err)
|
||||
}
|
||||
req.SetFormData(reqData)
|
||||
case "multipart/form-data":
|
||||
req.SetHeader("Content-Type", "multipart/form-data")
|
||||
reqData := make(map[string]string)
|
||||
err := json.Unmarshal([]byte(w.Data), &reqData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("webhook数据解析失败err: %w", err)
|
||||
}
|
||||
req.SetMultipartFormData(reqData)
|
||||
}
|
||||
}
|
||||
case http.MethodGet:
|
||||
{
|
||||
reqData := make(map[string]string)
|
||||
err := json.Unmarshal([]byte(w.Data), &reqData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("webhook数据解析失败err: %w", err)
|
||||
}
|
||||
req.SetQueryParams(reqData)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("暂不支持的HTTP方法: %s", method)
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
resp, err := req.Execute(method, w.Url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("webhook请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 处理响应
|
||||
if resp.IsError() {
|
||||
return fmt.Errorf("webhook返回错误状态码: %d, msg: %s", resp.StatusCode(), resp.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WebhookConfig) ParseHeaders(headerStr string) (http.Header, error) {
|
||||
headers := make(http.Header)
|
||||
lines := strings.Split(headerStr, "\n")
|
||||
|
||||
for i, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("解析请求头错误 第%d行: %s", i+1, line)
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
if key == "" || value == "" {
|
||||
return nil, fmt.Errorf("请求头Key第%d行为空", i+1)
|
||||
}
|
||||
canonicalKey := http.CanonicalHeaderKey(key)
|
||||
headers.Add(canonicalKey, value)
|
||||
}
|
||||
|
||||
return headers, nil
|
||||
}
|
||||
|
||||
func ReplaceJSONPlaceholders(jsonStr string, vars map[string]any) (string, error) {
|
||||
re := regexp.MustCompile(`__([a-zA-Z0-9_]+)__`)
|
||||
result := re.ReplaceAllStringFunc(jsonStr, func(match string) string {
|
||||
key := re.FindStringSubmatch(match)[1]
|
||||
if val, ok := vars[key]; ok {
|
||||
escaped := strconv.Quote(fmt.Sprintf("%v", val)) // 将 any 类型转换为字符串
|
||||
return escaped[1 : len(escaped)-1]
|
||||
}
|
||||
return match // 未匹配到变量则保留原样
|
||||
})
|
||||
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user