mirror of
https://gitee.com/mirrors/AllinSSL.git
synced 2026-03-16 03:12:02 +08:00
【调整】暗色主题样式
This commit is contained in:
105
frontend/allinssl/backend/internal/report/dingtalk.go
Normal file
105
frontend/allinssl/backend/internal/report/dingtalk.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DingtalkReport struct {
|
||||
webHookUrl string
|
||||
secret string
|
||||
}
|
||||
|
||||
func NewDingtalkReport(webHookUrl, secret string) *DingtalkReport {
|
||||
return &DingtalkReport{webHookUrl: webHookUrl, secret: secret}
|
||||
}
|
||||
|
||||
func (d *DingtalkReport) sign() (string, error) {
|
||||
timestamp := time.Now().UnixNano() / 1000000
|
||||
stringToSign := fmt.Sprintf("%d\n%s", timestamp, d.secret)
|
||||
hash := hmac.New(sha256.New, []byte(d.secret))
|
||||
hash.Write([]byte(stringToSign))
|
||||
sum := hash.Sum(nil)
|
||||
signature := base64.StdEncoding.EncodeToString(sum)
|
||||
|
||||
webhookurl, _ := url.Parse(d.webHookUrl)
|
||||
query := webhookurl.Query()
|
||||
query.Set("timestamp", fmt.Sprint(timestamp))
|
||||
query.Set("sign", signature)
|
||||
webhookurl.RawQuery = query.Encode()
|
||||
|
||||
return webhookurl.String(), nil
|
||||
}
|
||||
|
||||
func (d *DingtalkReport) SendText(msg string) error {
|
||||
data := map[string]any{
|
||||
"text": map[string]any{
|
||||
"content": msg,
|
||||
},
|
||||
"msgtype": "text",
|
||||
}
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signUrl, err := d.sign()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, _ := http.NewRequest("POST", signUrl, bytes.NewReader(jsonData))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("请求钉钉失败: %v", err)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
var res map[string]interface{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("返回值解析失败: %v", err)
|
||||
}
|
||||
|
||||
if res["errcode"].(float64) != 0 {
|
||||
return fmt.Errorf("发送失败: %s", res["errmsg"].(string))
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func NotifyDingtalk(params map[string]any) error {
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerID := params["provider_id"].(string)
|
||||
|
||||
providerData, err := GetReport(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := providerData["config"].(string)
|
||||
var config map[string]string
|
||||
err = json.Unmarshal([]byte(configStr), &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置失败: %v", err)
|
||||
}
|
||||
notifyMsg := fmt.Sprintf("%s : %s", params["subject"].(string), params["body"].(string))
|
||||
|
||||
report := NewDingtalkReport(config["webhook"], config["secret"])
|
||||
err = report.SendText(notifyMsg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Dingtalk发送失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
28
frontend/allinssl/backend/internal/report/dingtalk_test.go
Normal file
28
frontend/allinssl/backend/internal/report/dingtalk_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDingdingSend(test *testing.T) {
|
||||
report := NewDingtalkReport("", "")
|
||||
err := report.SendText("test msg")
|
||||
if err != nil {
|
||||
test.Errorf("Dingding failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotifyDingding(test *testing.T) {
|
||||
params := map[string]any{
|
||||
"provider_id": "4",
|
||||
"body": "测试消息通道",
|
||||
"subject": "测试消息通道",
|
||||
}
|
||||
|
||||
err := NotifyDingtalk(params)
|
||||
if err != nil {
|
||||
test.Error("NotifyDingtalk failed", "error", err)
|
||||
} else {
|
||||
test.Log("NotifyDingtalk success")
|
||||
}
|
||||
}
|
||||
106
frontend/allinssl/backend/internal/report/feishu.go
Normal file
106
frontend/allinssl/backend/internal/report/feishu.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FeishuReport struct {
|
||||
webHookUrl string
|
||||
secret string
|
||||
}
|
||||
|
||||
func NewFeishuReport(webHookUrl, secret string) *FeishuReport {
|
||||
return &FeishuReport{webHookUrl: webHookUrl, secret: secret}
|
||||
}
|
||||
|
||||
func (f *FeishuReport) sign(params *map[string]any) error {
|
||||
timestamp := time.Now().Unix()
|
||||
stringToSign := fmt.Sprintf("%v", timestamp) + "\n" + f.secret
|
||||
var data []byte
|
||||
h := hmac.New(sha256.New, []byte(stringToSign))
|
||||
_, err := h.Write(data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成签名失败: %v", err)
|
||||
}
|
||||
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
(*params)["timestamp"] = timestamp
|
||||
(*params)["sign"] = signature
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FeishuReport) SendText(msg string) error {
|
||||
data := map[string]any{
|
||||
"timestamp": 0,
|
||||
"content": map[string]any{
|
||||
"text": msg,
|
||||
},
|
||||
"msg_type": "text",
|
||||
"sign": "",
|
||||
}
|
||||
if err := f.sign(&data); err != nil {
|
||||
return err
|
||||
}
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", f.webHookUrl, bytes.NewReader(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建请求失败: %v", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
client := &http.Client{}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("请求飞书失败: %v", err)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
defer resp.Body.Close()
|
||||
var res map[string]interface{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("返回值解析失败: %v", err)
|
||||
}
|
||||
|
||||
if res["code"].(float64) != 0 {
|
||||
return fmt.Errorf("发送失败: %s", res["msg"].(string))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NotifyFeishu(params map[string]any) error {
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerID := params["provider_id"].(string)
|
||||
|
||||
providerData, err := GetReport(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := providerData["config"].(string)
|
||||
var config map[string]string
|
||||
err = json.Unmarshal([]byte(configStr), &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置失败: %v", err)
|
||||
}
|
||||
notifyMsg := fmt.Sprintf("%s : %s", params["subject"].(string), params["body"].(string))
|
||||
|
||||
report := NewFeishuReport(config["webhook"], config["secret"])
|
||||
err = report.SendText(notifyMsg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("飞书发送失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
28
frontend/allinssl/backend/internal/report/feishu_test.go
Normal file
28
frontend/allinssl/backend/internal/report/feishu_test.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFeishuSend(test *testing.T) {
|
||||
report := NewFeishuReport("", "")
|
||||
err := report.SendText("test msg")
|
||||
if err != nil {
|
||||
test.Errorf("FeishuSend failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotifyFeishu(test *testing.T) {
|
||||
params := map[string]any{
|
||||
"provider_id": "3",
|
||||
"body": "测试消息通道",
|
||||
"subject": "测试消息通道",
|
||||
}
|
||||
|
||||
err := NotifyFeishu(params)
|
||||
if err != nil {
|
||||
test.Error("NotifyWebHook failed", "error", err)
|
||||
} else {
|
||||
test.Log("NotifyWebHook success")
|
||||
}
|
||||
}
|
||||
221
frontend/allinssl/backend/internal/report/report.go
Normal file
221
frontend/allinssl/backend/internal/report/report.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/jordan-wright/email"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func GetSqlite() (*public.Sqlite, error) {
|
||||
s, err := public.NewSqlite("data/data.db", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.TableName = "report"
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||||
var data []map[string]any
|
||||
var count int64
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
var limits []int64
|
||||
if p >= 0 && limit >= 0 {
|
||||
limits = []int64{0, limit}
|
||||
if p > 1 {
|
||||
limits[0] = (p - 1) * limit
|
||||
limits[1] = limit
|
||||
}
|
||||
}
|
||||
|
||||
if search != "" {
|
||||
count, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Count()
|
||||
data, err = s.Where("name like ?", []interface{}{"%" + search + "%"}).Limit(limits).Order("update_time", "desc").Select()
|
||||
} else {
|
||||
count, err = s.Count()
|
||||
data, err = s.Order("update_time", "desc").Limit(limits).Select()
|
||||
}
|
||||
if err != nil {
|
||||
return data, 0, err
|
||||
}
|
||||
return data, int(count), nil
|
||||
}
|
||||
|
||||
func GetReport(id string) (map[string]any, error) {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer s.Close()
|
||||
data, err := s.Where("id=?", []interface{}{id}).Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("没有找到此通知配置")
|
||||
}
|
||||
return data[0], nil
|
||||
|
||||
}
|
||||
|
||||
func AddReport(Type, config, name string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
now := time.Now().Format("2006-01-02 15:04:05")
|
||||
_, err = s.Insert(map[string]interface{}{
|
||||
"name": name,
|
||||
"type": Type,
|
||||
"config": config,
|
||||
"create_time": now,
|
||||
"update_time": now,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdReport(id, config, name string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
_, err = s.Where("id=?", []interface{}{id}).Update(map[string]interface{}{
|
||||
"name": name,
|
||||
"config": config,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func DelReport(id string) error {
|
||||
s, err := GetSqlite()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.Close()
|
||||
_, err = s.Where("id=?", []interface{}{id}).Delete()
|
||||
return err
|
||||
}
|
||||
|
||||
func NotifyTest(id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerData, err := GetReport(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
params := map[string]any{
|
||||
"provider_id": id,
|
||||
"body": "测试消息通道",
|
||||
"subject": "测试消息通道",
|
||||
}
|
||||
switch providerData["type"] {
|
||||
case "mail":
|
||||
err = NotifyMail(params)
|
||||
case "webhook":
|
||||
err = NotifyWebHook(params)
|
||||
case "feishu":
|
||||
err = NotifyFeishu(params)
|
||||
case "dingtalk":
|
||||
err = NotifyDingtalk(params)
|
||||
case "workwx":
|
||||
err = NotifyWorkWx(params)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Notify(params map[string]any) error {
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerName, ok := params["provider"].(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("通知类型错误")
|
||||
}
|
||||
switch providerName {
|
||||
case "mail":
|
||||
return NotifyMail(params)
|
||||
// case "btpanel-site":
|
||||
// return NotifyBt(params)
|
||||
case "webhook":
|
||||
return NotifyWebHook(params)
|
||||
case "feishu":
|
||||
return NotifyFeishu(params)
|
||||
case "dingtalk":
|
||||
return NotifyDingtalk(params)
|
||||
case "workwx":
|
||||
return NotifyWorkWx(params)
|
||||
default:
|
||||
return fmt.Errorf("不支持的通知类型")
|
||||
}
|
||||
}
|
||||
|
||||
func NotifyMail(params map[string]any) error {
|
||||
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerID := params["provider_id"].(string)
|
||||
// fmt.Println(providerID)
|
||||
providerData, err := GetReport(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := providerData["config"].(string)
|
||||
var config map[string]string
|
||||
err = json.Unmarshal([]byte(configStr), &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置失败: %v", err)
|
||||
}
|
||||
|
||||
e := email.NewEmail()
|
||||
e.From = config["sender"]
|
||||
e.To = []string{config["receiver"]}
|
||||
e.Subject = params["subject"].(string)
|
||||
|
||||
e.Text = []byte(params["body"].(string))
|
||||
|
||||
addr := fmt.Sprintf("%s:%s", config["smtpHost"], config["smtpPort"])
|
||||
|
||||
auth := smtp.PlainAuth("", config["sender"], config["password"], config["smtpHost"])
|
||||
|
||||
// 使用 SSL(通常是 465)
|
||||
if config["smtpPort"] == "465" {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true, // 开发阶段跳过证书验证,生产建议关闭
|
||||
ServerName: config["smtpHost"],
|
||||
}
|
||||
err = e.SendWithTLS(addr, auth, tlsConfig)
|
||||
if err != nil {
|
||||
if err.Error() == "EOF" || strings.Contains(err.Error(), "short response") || err.Error() == "server response incomplete" {
|
||||
// 忽略短响应错误
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 普通明文发送(25端口,非推荐)
|
||||
err = e.Send(addr, auth)
|
||||
if err != nil {
|
||||
if err.Error() == "EOF" || strings.Contains(err.Error(), "short response") || err.Error() == "server response incomplete" {
|
||||
// 忽略短响应错误
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
17
frontend/allinssl/backend/internal/report/report_test.go
Normal file
17
frontend/allinssl/backend/internal/report/report_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMail(t *testing.T) {
|
||||
config := map[string]any{
|
||||
"provider": "mail",
|
||||
"provider_id": "4",
|
||||
"subject": "执行结束",
|
||||
"body": "执行结束",
|
||||
}
|
||||
err := NotifyMail(config)
|
||||
fmt.Println(err)
|
||||
}
|
||||
198
frontend/allinssl/backend/internal/report/webhook.go
Normal file
198
frontend/allinssl/backend/internal/report/webhook.go
Normal file
@@ -0,0 +1,198 @@
|
||||
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})
|
||||
}
|
||||
|
||||
if config.Data == "" {
|
||||
config.Data = "{}" // 默认数据为空JSON对象
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
config.Data, err = public.ReplaceJSONPlaceholders(config.Data, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("替换JSON占位符失败: %w", err)
|
||||
}
|
||||
|
||||
reporter := NewWebHookReporter(&config, logger)
|
||||
httpctx := context.Background()
|
||||
err = reporter.Send(httpctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("webhook发送失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
76
frontend/allinssl/backend/internal/report/webhook_test.go
Normal file
76
frontend/allinssl/backend/internal/report/webhook_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
88
frontend/allinssl/backend/internal/report/workwx.go
Normal file
88
frontend/allinssl/backend/internal/report/workwx.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"ALLinSSL/backend/public"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func PostHeader(url string, msg []byte, headers map[string]string) (string, error) {
|
||||
client := &http.Client{}
|
||||
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(string(msg)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for key, header := range headers {
|
||||
req.Header.Set(key, header)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func PostJson(url string, msg []byte) (string, error) {
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json;charset=utf-8"
|
||||
res, err := PostHeader(url, msg, headers)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func NotifyWorkWx(params map[string]any) error {
|
||||
if params == nil {
|
||||
return fmt.Errorf("缺少参数")
|
||||
}
|
||||
providerID := params["provider_id"].(string)
|
||||
// fmt.Println(providerID)
|
||||
providerData, err := GetReport(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr := providerData["config"].(string)
|
||||
//fmt.Println(configStr)
|
||||
var config map[string]string
|
||||
err = json.Unmarshal([]byte(configStr), &config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("解析配置失败: %v", err)
|
||||
}
|
||||
url := config["url"]
|
||||
if url == "" {
|
||||
return fmt.Errorf("缺少企业微信URL配置")
|
||||
}
|
||||
if config["data"] == "" {
|
||||
config["data"] = `
|
||||
{
|
||||
"msgtype": "news",
|
||||
"news": {
|
||||
"articles" : [
|
||||
{
|
||||
"title" : "__subject__",
|
||||
"description" : "__body__。",
|
||||
"url" : "https://allinssl.com/",
|
||||
"picurl" : "https://allinssl.com/logo.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
msg, err := public.ReplaceJSONPlaceholders(config["data"], params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("替换JSON占位符失败: %v", err)
|
||||
}
|
||||
_, err = PostJson(url, []byte(msg))
|
||||
if err != nil {
|
||||
return fmt.Errorf("发送企业微信消息失败: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user