【新增】【通知】WebHOOK通知类型

This commit is contained in:
wantoper
2025-05-28 14:39:33 +08:00
parent 39f66a981a
commit b177c20cb4
5 changed files with 276 additions and 49 deletions

View File

@@ -124,6 +124,8 @@ func NotifyTest(id string) error {
switch providerData["type"] {
case "mail":
err = NotifyMail(params)
case "webhook":
err = NotifyWebHook(params)
}
return err
}
@@ -141,6 +143,8 @@ func Notify(params map[string]any) error {
return NotifyMail(params)
// case "btpanel-site":
// return NotifyBt(params)
case "webhook":
return NotifyWebHook(params)
default:
return fmt.Errorf("不支持的通知类型")
}

View File

@@ -0,0 +1,190 @@
package report
import (
"ALLinSSL/backend/public"
"context"
"crypto/tls"
"encoding/json"
"fmt"
"github.com/go-resty/resty/v2"
"net/http"
"strings"
"time"
)
type ReportConfig 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"`
}
type WebHookReporter struct {
config *ReportConfig
logger *public.Logger
httpClient *resty.Client
}
func NewWebHookReporter(config *ReportConfig, logger *public.Logger) *WebHookReporter {
client := resty.New()
client.SetTimeout(30 * time.Second)
if config.IgnoreSSL {
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
}
return &WebHookReporter{
config: config,
logger: logger,
httpClient: client,
}
}
func (w *WebHookReporter) Send(ctx context.Context) error {
// 确定HTTP方法
method := strings.ToUpper(w.config.Method)
if method == "" {
method = http.MethodPost // 默认使用POST方法
}
// 创建基础请求
req := w.httpClient.R().
SetContext(ctx)
// 设置请求头
if w.config.Headers != "" {
reqHeader, err := w.ParseHeaders(w.config.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.config.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.config.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.config.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.config.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.config.Url)
if err != nil {
if w.logger != nil {
w.logger.Error(fmt.Sprintf("Webhook请求失败%s %v", w.config.Url, err))
}
return fmt.Errorf("webhook请求失败: %w", err)
}
// 处理响应
if resp.IsError() {
if w.logger != nil {
w.logger.Error(fmt.Sprintf("Webhook返回错误响应%s %d", w.config.Url, resp.StatusCode()))
}
return fmt.Errorf("webhook返回错误状态码: %d", resp.StatusCode())
}
if w.logger != nil {
w.logger.Debug(fmt.Sprintf("Webhook请求成功 %s", w.config.Url))
}
return nil
}
func (w *WebHookReporter) 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 NotifyWebHook(params map[string]any) error {
if params == nil {
return fmt.Errorf("缺少参数")
}
providerID := params["provider_id"].(string)
var logger *public.Logger
if params["logger"] != nil {
logger = params["logger"].(*public.Logger)
}
providerData, err := GetReport(providerID)
if err != nil {
return err
}
configStr := providerData["config"].(string)
var config ReportConfig
err = json.Unmarshal([]byte(configStr), &config)
if err != nil {
return fmt.Errorf("解析配置失败: %v", err)
}
reporter := NewWebHookReporter(&config, logger)
httpctx := context.Background()
err = reporter.Send(httpctx)
if err != nil {
return fmt.Errorf("webhook发送失败: %w", err)
}
return nil
}

View File

@@ -0,0 +1,76 @@
package report
import (
"ALLinSSL/backend/public"
"context"
"testing"
)
func TestSend(test *testing.T) {
logger, _ := public.NewLogger("/tmp/test.log")
jsonConfig := &ReportConfig{
Url: "http://localhost:9939/demo/any",
Method: "GET",
Headers: `X-Auth-Token: secret123`,
Data: `{"username": "zszs", "password": "get"}`,
}
jsonConfig1 := &ReportConfig{
Url: "http://localhost:9939/demo/any",
Method: "post",
Headers: `
Content-Type: application/json
X-Auth-Token: secret123`,
Data: `{"username": "zszs", "password": "post-json"}`,
}
jsonConfig2 := &ReportConfig{
Url: "http://localhost:9939/demo/any",
Method: "post",
Headers: `
Content-Type: application/x-www-form-urlencoded
X-Auth-Token: secret123`,
Data: `{"username": "zszs", "password": "post-form-urlencoded"}`,
}
jsonConfig3 := &ReportConfig{
Url: "http://localhost:9939/demo/any",
Method: "post",
Headers: `
Content-Type: multipart/form-data
X-Auth-Token: secret123`,
Data: `{"username": "zszs", "password": "post-form-data"}`,
}
reqs := []*ReportConfig{jsonConfig, jsonConfig1, jsonConfig2, jsonConfig3}
for _, req := range reqs {
// 创建报告器
jsonReporter := NewWebHookReporter(req, logger)
// 发送请求
ctx := context.Background()
if err := jsonReporter.Send(ctx); err != nil {
test.Error("JSON Webhook发送失败", "error", err)
continue
}
test.Log("JSON Webhook发送成功", "url", req.Url, "method", req.Method)
}
}
func TestNotifyWebHook(test *testing.T) {
params := map[string]any{
"provider_id": "2",
"body": "测试消息通道",
"subject": "测试消息通道",
}
err := NotifyWebHook(params)
if err != nil {
test.Error("NotifyWebHook failed", "error", err)
} else {
test.Log("NotifyWebHook success")
}
}