diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 79fa648..53bea1f 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -17,8 +17,9 @@ builds: archives: - format: tar.gz # this name template makes the OS and Arch compatible with the results of uname. + # {{ .ProjectName }}_ name_template: >- - {{ .ProjectName }}_ + Message-Nest_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 diff --git a/conf/app.example.ini b/conf/app.example.ini index 3363b9c..d7a6d0e 100644 --- a/conf/app.example.ini +++ b/conf/app.example.ini @@ -19,3 +19,4 @@ Host = 123.1.1.1 Name = db_name Port = 3306 TablePrefix = message_ +; SqlDebug = enable diff --git a/go.mod b/go.mod index d6e94eb..c7073e4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module message-nest go 1.20 require ( - github.com/blinkbean/dingtalk v0.0.0-20230927120905-796332ac4ba1 github.com/dgrijalva/jwt-go v3.1.0+incompatible github.com/gin-gonic/gin v1.9.1 github.com/go-ini/ini v1.32.1-0.20180214101753-32e4be5f41bb diff --git a/go.sum b/go.sum index 610e305..9833b3d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/blinkbean/dingtalk v0.0.0-20230927120905-796332ac4ba1 h1:G14RkaB3RRW099aQbfyHm4RFgNxGUOTeHSoN+CZy2YI= -github.com/blinkbean/dingtalk v0.0.0-20230927120905-796332ac4ba1/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= diff --git a/models/models.go b/models/models.go index 2e1618d..5f48c99 100644 --- a/models/models.go +++ b/models/models.go @@ -49,7 +49,9 @@ func Setup() { return setting.DatabaseSetting.TablePrefix + defaultTableName } - //db.LogMode(true) + if setting.DatabaseSetting.SqlDebug == "enable" { + db.LogMode(true) + } db.SingularTable(true) db.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampForCreateCallback) db.Callback().Update().Replace("gorm:update_time_stamp", updateTimeStampForUpdateCallback) diff --git a/models/send_ins.go b/models/send_ins.go index 74f4771..c303aba 100644 --- a/models/send_ins.go +++ b/models/send_ins.go @@ -21,6 +21,10 @@ type InsEmailConfig struct { type InsDtalkConfig struct { } +// InsCustomConfig 实例里面的自定义config +type InsCustomConfig struct { +} + // ManyAddTaskIns 批量添加实例 func ManyAddTaskIns(taskIns []SendTasksIns) error { tx := db.Begin() diff --git a/pkg/constant/release_log.go b/pkg/constant/release_log.go index be67d63..a1beff5 100644 --- a/pkg/constant/release_log.go +++ b/pkg/constant/release_log.go @@ -29,7 +29,21 @@ var V2VersionDesc = ` 9. 支持钉钉消息推送 ` +var V3Version = "v0.0.3" +var V3VersionDesc = ` +1. 单应用打包 +2. 支持邮件发送 +3. 用户密码设置 +4. 支持用户定时任务清理,更新定时时间 +5. 查看定时清理日志 +6. 单应用的html浏览器自动缓存 +7. gin的日志使用logrus +8. 支持异步发送 +9. 支持钉钉消息推送 +10. 支持自定义的webhook推送 +` + func init() { - LatestVersion["version"] = V2Version - LatestVersion["desc"] = strings.TrimSpace(V2VersionDesc) + LatestVersion["version"] = V3Version + LatestVersion["desc"] = strings.TrimSpace(V3VersionDesc) } diff --git a/pkg/message/custom.go b/pkg/message/custom.go new file mode 100644 index 0000000..ffacb06 --- /dev/null +++ b/pkg/message/custom.go @@ -0,0 +1,31 @@ +package message + +import ( + "bytes" + "io" + "net/http" +) + +type CustomWebhook struct { + Webhook string + Body string +} + +func (cw *CustomWebhook) Request(url string, msg string) ([]byte, error) { + resp, err := http.Post(url, "application/json", bytes.NewBuffer([]byte(msg))) + if err != nil { + return nil, err + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + + } + }(resp.Body) + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + return body, err +} diff --git a/pkg/message/dtalk.go b/pkg/message/dtalk.go index abc32f8..fd0e0cf 100644 --- a/pkg/message/dtalk.go +++ b/pkg/message/dtalk.go @@ -33,7 +33,12 @@ func (t *Dtalk) Request(msg interface{}) ([]byte, error) { return nil, err } - defer resp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + + } + }(resp.Body) body, err := io.ReadAll(resp.Body) if err != nil { return nil, err diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 86bae91..4dd8513 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -34,6 +34,7 @@ type Database struct { Port int Name string TablePrefix string + SqlDebug string } var DatabaseSetting = &Database{} diff --git a/service/send_ins_service/send_ins.go b/service/send_ins_service/send_ins.go index 70e361b..a8f18fb 100644 --- a/service/send_ins_service/send_ins.go +++ b/service/send_ins_service/send_ins.go @@ -34,6 +34,15 @@ func (sw *SendTaskInsService) ValidateDiffIns(ins models.SendTasksIns) (string, var Config models.InsDtalkConfig return "", Config } + if ins.WayType == "Custom" { + var Config models.InsCustomConfig + err := json.Unmarshal([]byte(ins.Config), &Config) + if err != nil { + return "自定义webhook反序列化失败!", empty + } + _, Msg := app.CommonPlaygroundValid(Config) + return Msg, Config + } return "未知的渠道的config校验", empty } diff --git a/service/send_message_service/send_custom.go b/service/send_message_service/send_custom.go new file mode 100644 index 0000000..cf0b3a2 --- /dev/null +++ b/service/send_message_service/send_custom.go @@ -0,0 +1,31 @@ +package send_message_service + +import ( + "encoding/json" + "fmt" + + "message-nest/models" + "message-nest/pkg/message" + "message-nest/service/send_way_service" + "strings" +) + +type CustomService struct { +} + +// SendCustomMessage 执行发送钉钉 +func (s *CustomService) SendCustomMessage(auth send_way_service.WayDetailCustom, ins models.SendTasksIns, typeC string, title string, content string) string { + + errMsg := "" + cli := message.CustomWebhook{} + data, _ := json.Marshal(content) + dataStr := string(data) + dataStr = strings.Trim(dataStr, "\"") + bodyStr := strings.Replace(auth.Body, "TEXT", dataStr, -1) + _, err := cli.Request(auth.Webhook, bodyStr) + if err != nil { + errMsg = fmt.Sprintf("发送失败:%s", err) + } + + return errMsg +} diff --git a/service/send_message_service/send_message.go b/service/send_message_service/send_message.go index c024518..f820eeb 100644 --- a/service/send_message_service/send_message.go +++ b/service/send_message_service/send_message.go @@ -47,12 +47,12 @@ func (sm *SendMessageService) Send() string { errStr := "" sm.LogsAndStatusMark(fmt.Sprintf("开始任务[%s]的发送", sm.TaskID), sm.Status) - sm.LogsAndStatusMark(fmt.Sprintf("发送标题:%s \n", sm.Title), sm.Status) - sendTaskService := send_task_service.SendTaskService{ ID: sm.TaskID, } task, err := sendTaskService.GetTaskWithIns() + sm.LogsAndStatusMark(fmt.Sprintf("任务名称:%s", task.Name), sm.Status) + sm.LogsAndStatusMark(fmt.Sprintf("发送标题:%s \n", sm.Title), sm.Status) if err != nil { errStr = fmt.Sprintf("任务[%s]不存在!退出发送!", sm.TaskID) sm.LogsAndStatusMark(errStr, SendFail) @@ -109,6 +109,14 @@ func (sm *SendMessageService) Send() string { sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg)) continue } + // 自定义webhook类型的实例发送 + customAuth, ok := msgObj.(send_way_service.WayDetailCustom) + if ok { + cs := CustomService{} + errMsg := cs.SendCustomMessage(customAuth, ins.SendTasksIns, typeC, sm.Title, content) + sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg)) + continue + } sm.LogsAndStatusMark(fmt.Sprintf("发送失败:未知渠道的发信实例: %s\n", ins.ID), SendFail) } diff --git a/service/send_way_service/send_way.go b/service/send_way_service/send_way.go index 9dbf921..cba4582 100644 --- a/service/send_way_service/send_way.go +++ b/service/send_way_service/send_way.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/blinkbean/dingtalk" "message-nest/models" "message-nest/pkg/app" "message-nest/pkg/message" @@ -39,6 +38,12 @@ type WayDetailDTalk struct { Secret string `json:"secret" validate:"max=100" label:"钉钉加签秘钥"` } +// WayDetailCustom 自定义渠道 +type WayDetailCustom struct { + Webhook string `json:"webhook" validate:"required,max=200" label:"自定义的webhook地址"` + Body string `json:"body" validate:"max=2000" label:"自定义的请求体"` +} + func (sw *SendWay) GetByID() (interface{}, error) { return models.GetWayByID(sw.ID) } @@ -103,6 +108,14 @@ func (sw *SendWay) ValidateDiffWay() (string, interface{}) { } _, Msg := app.CommonPlaygroundValid(dtalk) return Msg, dtalk + } else if sw.Type == "Custom" { + var custom WayDetailCustom + err := json.Unmarshal([]byte(sw.Auth), &custom) + if err != nil { + return "自定义参数反序列化失败!", empty + } + _, Msg := app.CommonPlaygroundValid(custom) + return Msg, custom } return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), empty } @@ -119,8 +132,24 @@ func (sw *SendWay) TestSendWay(msgObj interface{}) string { } dtalkAuth, ok := msgObj.(WayDetailDTalk) if ok { - cli := dingtalk.InitDingTalkWithSecret(dtalkAuth.AccessToken, dtalkAuth.Secret) - err := cli.SendTextMessage(testMsg + dtalkAuth.Keys) + var cli = message.Dtalk{ + AccessToken: dtalkAuth.AccessToken, + Secret: dtalkAuth.Secret, + } + _, err := cli.SendMessageText(testMsg + dtalkAuth.Keys) + if err != nil { + return fmt.Sprintf("发送失败:%s", err) + } + return "" + } + customAuth, ok := msgObj.(WayDetailCustom) + if ok { + var cli = message.CustomWebhook{} + data, _ := json.Marshal(testMsg) + dataStr := string(data) + dataStr = strings.Trim(dataStr, "\"") + bodyStr := strings.Replace(customAuth.Body, "TEXT", dataStr, -1) + _, err := cli.Request(customAuth.Webhook, bodyStr) if err != nil { return fmt.Sprintf("发送失败:%s", err) } diff --git a/web/src/constant.js b/web/src/constant.js index 7c62d03..00c817a 100644 --- a/web/src/constant.js +++ b/web/src/constant.js @@ -34,7 +34,6 @@ const CONSTANT = { label: '钉钉', inputs: [ { subLabel: 'access_token', value: '', col: 'access_token', desc: "钉钉webhook中的access_token" }, - // { subLabel: '关键字过滤', value: '', col: 'keys', desc: "使用的关键字过滤,多个使用|隔开" }, { subLabel: '加签', value: '', col: 'secret', desc: "加签的签名,SEC开头" }, { subLabel: '渠道名', value: '', col: 'name', desc: "想要设置的渠道名字" }, ], @@ -46,8 +45,23 @@ const CONSTANT = { { subLabel: 'markdown', content: 'markdown' }, ], taskInsInputs: [ - // { value: '', col: 'to_account', desc: "目的邮箱账号(发给fsfsf谁)" }, - // { value: '', col: 'title', desc: "邮箱标题fsfsf" }, + ], + }, + { + type: 'Custom', + label: '自定义', + inputs: [ + { subLabel: 'webhook地址', value: '', col: 'webhook', desc: "自定义webhook地址" }, + { subLabel: '请求体', value: '', col: 'body', desc: "请求体, text内容请使用 TEXT 进行占位\n例如:{\"message\": \"TEXT\", \"other_field\": \"field_data\"}", isTextArea: true}, + { subLabel: '渠道名', value: '', col: 'name', desc: "想要设置的渠道名字" }, + ], + tips: { + text: "自定义webhook说明", desc: "自定义webhook暂时只支持text,消息将解析TEXT占位标识进行替换,暂时只支持POST方式" + }, + taskInsRadios: [ + { subLabel: 'text', content: 'text' }, + ], + taskInsInputs: [ ], }, ], diff --git a/web/src/views/tabsTools/sendLogs/sendLogs.vue b/web/src/views/tabsTools/sendLogs/sendLogs.vue index 1f78274..da8c12a 100644 --- a/web/src/views/tabsTools/sendLogs/sendLogs.vue +++ b/web/src/views/tabsTools/sendLogs/sendLogs.vue @@ -7,11 +7,11 @@
- + - + @@ -29,6 +30,10 @@
+ + + +
共{{ total }}条 @@ -53,9 +58,11 @@ export default { const state = reactive({ search: '', optionValue: '', + logText: '', + drawer: false, tableData: [], total: CONSTANT.TOTAL, - pageSize: CONSTANT.PAGE_SIZE, + pageSize: CONSTANT.PAGE_SIZE, currPage: CONSTANT.PAGE, }); diff --git a/web/src/views/tabsTools/sendWays/view/addWayPopUp.vue b/web/src/views/tabsTools/sendWays/view/addWayPopUp.vue index ca785d3..1c86360 100644 --- a/web/src/views/tabsTools/sendWays/view/addWayPopUp.vue +++ b/web/src/views/tabsTools/sendWays/view/addWayPopUp.vue @@ -8,7 +8,9 @@ - + + diff --git a/web/src/views/tabsTools/sendWays/view/editWayPopUp.vue b/web/src/views/tabsTools/sendWays/view/editWayPopUp.vue index d580fa9..042db39 100644 --- a/web/src/views/tabsTools/sendWays/view/editWayPopUp.vue +++ b/web/src/views/tabsTools/sendWays/view/editWayPopUp.vue @@ -9,7 +9,9 @@ - + +