From 5c24cb9d509ed31f582c8013464ba4b2578e7686 Mon Sep 17 00:00:00 2001 From: zhenorzz Date: Wed, 12 Jun 2024 10:39:38 +0800 Subject: [PATCH] Added: notification template --- CHANGELOG.md | 16 ++ build.sh | 7 +- cmd/server/api/notification/handler.go | 73 +++++++ cmd/server/main.go | 12 +- cmd/server/task/deploy/deploy.go | 1 - cmd/server/task/deploy/notify_stage.go | 178 +----------------- cmd/server/task/deploy/server_stage.go | 3 +- cmd/server/task/monitor.go | 8 +- cmd/server/task/server_monitor.go | 12 +- config/permission.go | 5 +- database/1.17.2.sql | 104 ++++++++++ database/goploy.sql | 127 +++++++++++-- docker/Dockerfile | 2 +- internal/model/monitor.go | 106 ----------- internal/model/notification_template.go | 112 +++++++++++ internal/model/server_monitor.go | 102 ---------- internal/notify/notify.go | 167 ++++++++++++++++ web/package.json | 2 +- web/src/api/notification.ts | 33 ++++ web/src/icons/svg/notifySetting.svg | 1 + web/src/lang/en.json | 5 +- web/src/lang/zh-cn.json | 9 +- web/src/permission.ts | 5 +- web/src/router/asyncRoutes.ts | 50 +++-- web/src/views/deploy/index.vue | 29 ++- .../components/TheUserDialog.vue | 0 .../{member/index.vue => setting/member.vue} | 2 +- .../index.vue => setting/namespace.vue} | 2 +- web/src/views/setting/notification.vue | 170 +++++++++++++++++ web/src/views/{namespace => setting}/role.vue | 2 +- 30 files changed, 892 insertions(+), 453 deletions(-) create mode 100644 cmd/server/api/notification/handler.go create mode 100644 database/1.17.2.sql create mode 100644 internal/model/notification_template.go create mode 100644 internal/notify/notify.go create mode 100644 web/src/api/notification.ts create mode 100644 web/src/icons/svg/notifySetting.svg rename web/src/views/{namespace => setting}/components/TheUserDialog.vue (100%) rename web/src/views/{member/index.vue => setting/member.vue} (99%) rename web/src/views/{namespace/index.vue => setting/namespace.vue} (99%) create mode 100644 web/src/views/setting/notification.vue rename web/src/views/{namespace => setting}/role.vue (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2d9dea..5d4cdcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to the "goploy" extension will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +## [1.17.2] - 2024-06-12 + +### Added + +- notification template + + +### Changed + +- router order + +### Bug fixed + +- fix unknown driver +- fix deploy page css + ## [1.17.1] - 2024-06-06 ### Changed diff --git a/build.sh b/build.sh index efa4f0b..bbab6d1 100755 --- a/build.sh +++ b/build.sh @@ -23,16 +23,19 @@ then fi -echo "Building goploy"; - +echo "env GOOS=linux go build -o goploy cmd/server/main.go"; env GOOS=linux go build -o goploy cmd/server/main.go +echo "env GOOS=linux GOARCH=arm64 go build -o goploy_arm64 cmd/server/main.go"; env GOOS=linux GOARCH=arm64 go build -o goploy_arm64 cmd/server/main.go +echo "env GOOS=darwin GOARCH=arm64 go build -o goploy_arm64.mac cmd/server/main.go"; env GOOS=darwin GOARCH=arm64 go build -o goploy_arm64.mac cmd/server/main.go +echo "env GOOS=darwin go build -o goploy.mac cmd/server/main.go"; env GOOS=darwin go build -o goploy.mac cmd/server/main.go +echo "env GOOS=windows go build -o goploy.exe cmd/server/main.go"; env GOOS=windows go build -o goploy.exe cmd/server/main.go diff --git a/cmd/server/api/notification/handler.go b/cmd/server/api/notification/handler.go new file mode 100644 index 0000000..2ffbcff --- /dev/null +++ b/cmd/server/api/notification/handler.go @@ -0,0 +1,73 @@ +// Copyright 2022 The Goploy Authors. All rights reserved. +// Use of this source code is governed by a GPLv3-style +// license that can be found in the LICENSE file. + +package notification + +import ( + "github.com/zhenorzz/goploy/cmd/server/api" + "github.com/zhenorzz/goploy/cmd/server/api/middleware" + "github.com/zhenorzz/goploy/config" + "github.com/zhenorzz/goploy/internal/model" + "github.com/zhenorzz/goploy/internal/server" + "github.com/zhenorzz/goploy/internal/server/response" + "net/http" +) + +type Notification api.API + +func (n Notification) Handler() []server.Route { + return []server.Route{ + server.NewRoute("/notification/getList", http.MethodGet, n.GetList).Permissions(config.ShowNotificationPage), + server.NewRoute("/notification/edit", http.MethodPut, n.Edit).Permissions(config.EditNotification).LogFunc(middleware.AddOPLog), + } +} + +// GetList lists notification template +// @Summary List notification template +// @Tags Notification +// @Produce json +// @Security ApiKeyHeader || ApiKeyQueryParam || NamespaceHeader || NamespaceQueryParam +// @Success 200 {object} response.JSON{data=notification.GetList.RespData} +// @Router /notification/getList [get] +func (Notification) GetList(*server.Goploy) server.Response { + list, err := model.NotificationTemplate{}.GetList() + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + + type RespData struct { + List model.NotificationTemplates `json:"list"` + } + + return response.JSON{ + Data: RespData{List: list}, + } +} + +// Edit edits the notification template +// @Summary Edit the notification template +// @Tags Notification +// @Produce json +// @Security ApiKeyHeader || ApiKeyQueryParam || NamespaceHeader || NamespaceQueryParam +// @Param request body notification.Edit.ReqData true "body params" +// @Success 200 {object} response.JSON +// @Router /notification/edit [put] +func (Notification) Edit(gp *server.Goploy) server.Response { + type ReqData struct { + ID int64 `json:"id" validate:"required,gt=0"` + Title string `json:"title" validate:"required"` + Template string `json:"template" validate:"required"` + } + var reqData ReqData + if err := gp.Decode(&reqData); err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + + err := model.NotificationTemplate{ID: reqData.ID, Title: reqData.Title, Template: reqData.Template}.EditRow() + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + + return response.JSON{} +} diff --git a/cmd/server/main.go b/cmd/server/main.go index 0e01445..eda7b89 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -16,6 +16,7 @@ import ( logApi "github.com/zhenorzz/goploy/cmd/server/api/log" "github.com/zhenorzz/goploy/cmd/server/api/monitor" "github.com/zhenorzz/goploy/cmd/server/api/namespace" + "github.com/zhenorzz/goploy/cmd/server/api/notification" "github.com/zhenorzz/goploy/cmd/server/api/project" "github.com/zhenorzz/goploy/cmd/server/api/repository" "github.com/zhenorzz/goploy/cmd/server/api/role" @@ -49,7 +50,7 @@ var ( s string ) -const appVersion = "1.17.1" +const appVersion = "1.17.2" func init() { flag.StringVar(&config.AssetDir, "asset-dir", "", "default: ./") @@ -64,7 +65,7 @@ func init() { } // @title Goploy -// @version 1.17.1 +// @version 1.17.2 // @description A web deployment system tool! // @contact.name zhenorzz // @contact.url https://github.com/zhenorzz/goploy @@ -130,6 +131,7 @@ func main() { srv.Router.Register(cron.Cron{}) srv.Router.Register(agent.Agent{}) srv.Router.Register(template.Template{}) + srv.Router.Register(notification.Notification{}) srv.Router.Register(ws.GetHub()) go func() { c := make(chan os.Signal, 1) @@ -188,9 +190,9 @@ func install() { println("Start to install the database...") // open connection without database - runner, err := model.Open(config.DBConfig{ - Host: cfg.DB.Host, User: cfg.DB.User, Password: cfg.DB.Password, Port: cfg.DB.Port, - }) + dbConfig := cfg.DB + dbConfig.Database = "" + runner, err := model.Open(dbConfig) if err != nil { panic(err) } diff --git a/cmd/server/task/deploy/deploy.go b/cmd/server/task/deploy/deploy.go index aceb583..60f95d0 100644 --- a/cmd/server/task/deploy/deploy.go +++ b/cmd/server/task/deploy/deploy.go @@ -197,7 +197,6 @@ func (gsync *Gsync) exec() { log.Errorf(projectLogFormat, gsync.Project.ID, err) } gsync.notify(model.ProjectFail, err.Error()) - }() err = gsync.repoStage() diff --git a/cmd/server/task/deploy/notify_stage.go b/cmd/server/task/deploy/notify_stage.go index ea60ffd..1ae0b18 100644 --- a/cmd/server/task/deploy/notify_stage.go +++ b/cmd/server/task/deploy/notify_stage.go @@ -1,14 +1,8 @@ package deploy import ( - "bytes" - "encoding/json" "fmt" - log "github.com/sirupsen/logrus" - "github.com/zhenorzz/goploy/internal/model" - "io" - "net/http" - "strings" + "github.com/zhenorzz/goploy/internal/notify" ) // commit id @@ -16,172 +10,18 @@ import ( // server ip & name // deploy user name // deploy time -func (gsync *Gsync) notify(deployState int, detail string) { +func (gsync *Gsync) notify(deployState uint8, detail string) { if gsync.Project.NotifyType == 0 { return } - serverList := "" - for _, projectServer := range gsync.ProjectServers { - if projectServer.Server.Name != projectServer.Server.IP { - serverList += projectServer.Server.Name + "(" + projectServer.Server.IP + ")" - } else { - serverList += projectServer.Server.IP - } - serverList += ", " - } - serverList = strings.TrimRight(serverList, ", ") project := gsync.Project - commitInfo := gsync.CommitInfo - var err error - var resp *http.Response - if project.NotifyType == model.NotifyWeiXin { - type markdown struct { - Content string `json:"content"` - } - type message struct { - Msgtype string `json:"msgtype"` - Markdown markdown `json:"markdown"` - } - content := "Deploy: " + project.Name + "\n" - content += "Publisher: " + project.PublisherName + "\n" - content += "Author: " + commitInfo.Author + "\n" - if commitInfo.Tag != "" { - content += "Tag: " + commitInfo.Tag + "\n" - } - content += "Branch: " + commitInfo.Branch + "\n" - content += "CommitSHA: " + commitInfo.Commit + "\n" - content += "CommitMessage: " + commitInfo.Message + "\n" - content += "ServerList: " + serverList + "\n" - if deployState == model.ProjectFail { - content += "State: fail \n" - content += "> Detail: " + detail + "" - } else { - content += "State: success" - } - msg := message{ - Msgtype: "markdown", - Markdown: markdown{ - Content: content, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if project.NotifyType == model.NotifyDingTalk { - type markdown struct { - Title string `json:"title"` - Text string `json:"text"` - } - type message struct { - Msgtype string `json:"msgtype"` - Markdown markdown `json:"markdown"` - } - text := "#### Deploy:" + project.Name + " \n " - text += "#### Publisher:" + project.PublisherName + " \n " - text += "#### Author:" + commitInfo.Author + " \n " - if commitInfo.Tag != "" { - text += "#### Tag:" + commitInfo.Tag + " \n " - } - text += "#### Branch:" + commitInfo.Branch + " \n " - text += "#### CommitSHA:" + commitInfo.Commit + " \n " - text += "#### CommitMessage:" + commitInfo.Message + " \n " - text += "#### ServerList:" + serverList + " \n " - if deployState == model.ProjectFail { - text += "#### State: fail \n " - text += "> Detail: " + detail + "" - } else { - text += "#### State: success" - } + _ = notify.Send(fmt.Sprintf("project%d-deploy", project.ID), notify.UseByDeploy, notify.DeployData{ + DeployState: deployState, + Project: project, + ProjectServers: gsync.ProjectServers, + CommitInfo: gsync.CommitInfo, + DeployDetail: detail, + }, project.NotifyType, project.NotifyTarget) - msg := message{ - Msgtype: "markdown", - Markdown: markdown{ - Title: project.Name, - Text: text, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if project.NotifyType == model.NotifyFeiShu { - type content struct { - Text string `json:"text"` - } - type message struct { - MsgType string `json:"msg_type"` - Content content `json:"content"` - } - text := "" - text += "Deploy:" + project.Name + "\n" - text += "Publisher: " + project.PublisherName + "\n" - text += "Author: " + commitInfo.Author + "\n" - if commitInfo.Tag != "" { - text += "Tag: " + commitInfo.Tag + "\n" - } - text += "Branch: " + commitInfo.Branch + "\n" - text += "CommitSHA: " + commitInfo.Commit + "\n" - text += "CommitMessage: " + commitInfo.Message + "\n" - text += "ServerList: " + serverList + "\n" - if deployState == model.ProjectFail { - text += "State: fail\n " - text += "Detail: " + detail - } else { - text += "State: success" - } - - msg := message{ - MsgType: "text", - Content: content{ - Text: text, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if project.NotifyType == model.NotifyCustom { - type message struct { - Code int `json:"code"` - Message string `json:"message"` - Data struct { - ProjectID int64 `json:"projectId"` - ProjectName string `json:"projectName"` - Publisher string `json:"publisher"` - Author string `json:"author"` - Branch string `json:"branch"` - Tag string `json:"tag"` - CommitSHA string `json:"commitSHA"` - CommitMessage string `json:"commitMessage"` - ServerList string `json:"serverList"` - } `json:"data"` - } - code := 0 - if deployState == model.ProjectFail { - code = 1 - } - msg := message{ - Code: code, - Message: detail, - } - msg.Data.ProjectID = project.ID - msg.Data.ProjectName = project.Name - msg.Data.Publisher = project.PublisherName - msg.Data.Author = commitInfo.Author - msg.Data.Branch = commitInfo.Branch - msg.Data.Tag = commitInfo.Tag - msg.Data.CommitSHA = commitInfo.Commit - msg.Data.CommitMessage = commitInfo.Message - msg.Data.ServerList = serverList - b, _ := json.Marshal(msg) - resp, err = http.Post(project.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } - - if err != nil { - log.Error(fmt.Sprintf("projectID: %d notify exec err: %s", project.ID, err)) - } else { - responseData, err := io.ReadAll(resp.Body) - if err != nil { - log.Error(fmt.Sprintf("projectID: %d notify read body err: %s", project.ID, err)) - } else { - log.Trace(fmt.Sprintf("projectID: %d notify success: %s", project.ID, string(responseData))) - } - _ = resp.Body.Close() - } } diff --git a/cmd/server/task/deploy/server_stage.go b/cmd/server/task/deploy/server_stage.go index cf71bab..0855bb5 100644 --- a/cmd/server/task/deploy/server_stage.go +++ b/cmd/server/task/deploy/server_stage.go @@ -270,9 +270,10 @@ func (gsync *Gsync) serverStage() error { for i := 0; i < len(gsync.ProjectServers); i++ { msg := <-ch if msg.state == model.ProjectFail { - message += msg.serverName + " error message: " + msg.detail + message += msg.serverName + " error message: " + msg.detail + ", " } } + message = strings.TrimRight(message, ", ") close(ch) if message != "" { diff --git a/cmd/server/task/monitor.go b/cmd/server/task/monitor.go index 9a626a9..d580b0a 100644 --- a/cmd/server/task/monitor.go +++ b/cmd/server/task/monitor.go @@ -7,10 +7,12 @@ package task import ( "database/sql" "errors" + "fmt" log "github.com/sirupsen/logrus" "github.com/zhenorzz/goploy/cmd/server/ws" "github.com/zhenorzz/goploy/internal/model" "github.com/zhenorzz/goploy/internal/monitor" + "github.com/zhenorzz/goploy/internal/notify" "strconv" "sync" "sync/atomic" @@ -98,12 +100,14 @@ func monitorTask() { monitorCache.errorTimes++ log.Error("m " + m.Name + " encounter error, " + monitorErrorContent) if m.Times <= uint16(monitorCache.errorTimes) { - if body, err := m.Notify(monitorErrorContent); err != nil { + if err := notify.Send(fmt.Sprintf("monitor%d", m.ID), notify.UseByMonitor, notify.MonitorData{ + Monitor: m, + ErrorMsg: monitorErrorContent, + }, m.NotifyType, m.NotifyTarget); err != nil { log.Error("m " + m.Name + " notify error, " + err.Error()) } else { monitorCache.errorTimes = 0 monitorCache.silentCycle = 1 - log.Trace("m " + m.Name + " notify return " + body) _ = m.UpdateLatestErrorContent(monitorErrorContent) ws.Send(ws.Data{ Type: ws.TypeMonitor, diff --git a/cmd/server/task/server_monitor.go b/cmd/server/task/server_monitor.go index 81742dd..2a7d194 100644 --- a/cmd/server/task/server_monitor.go +++ b/cmd/server/task/server_monitor.go @@ -10,6 +10,7 @@ import ( "fmt" log "github.com/sirupsen/logrus" "github.com/zhenorzz/goploy/internal/model" + "github.com/zhenorzz/goploy/internal/notify" "strings" "sync" "sync/atomic" @@ -108,12 +109,11 @@ func serverMonitorTask() { } serverCaches[serverMonitor.ServerID] = server } - body, err := serverMonitor.Notify(serverCaches[serverMonitor.ServerID], cycleValue) - if err != nil { - log.Error(fmt.Sprintf("monitor task %d notify error, %s", serverMonitor.ID, err.Error())) - } else { - log.Trace(fmt.Sprintf("monitor task %d notify return %s", serverMonitor.ID, body)) - } + _ = notify.Send(fmt.Sprintf("server-monitor%d", serverMonitor.ID), notify.UseByServerMonitor, notify.ServerMonitorData{ + Server: serverCaches[serverMonitor.ServerID], + ServerMonitor: serverMonitor, + CycleValue: cycleValue, + }, serverMonitor.NotifyType, serverMonitor.NotifyTarget) } serverMonitorCaches[serverMonitor.ID] = monitorCache } diff --git a/config/permission.go b/config/permission.go index 6c57392..7b2fdfd 100644 --- a/config/permission.go +++ b/config/permission.go @@ -11,12 +11,11 @@ const ( ShowSFTPLogPage = 4 ShowTerminalLogPage = 5 ShowTerminalRecord = 6 - Member = 7 + Setting = 7 ShowMemberPage = 8 AddMember = 9 EditMember = 10 DeleteMember = 11 - Namespace = 12 ShowNamespacePage = 13 AddNamespace = 14 EditNamespace = 15 @@ -90,4 +89,6 @@ const ( DeleteNginxConfig = 83 UnbindServerProject = 84 ManageRepository = 85 + ShowNotificationPage = 85 + EditNotification = 85 ) diff --git a/database/1.17.2.sql b/database/1.17.2.sql new file mode 100644 index 0000000..d63e7fb --- /dev/null +++ b/database/1.17.2.sql @@ -0,0 +1,104 @@ +CREATE TABLE IF NOT EXISTS `notification_template` ( + `id` int unsigned auto_increment, + `type` tinyint unsigned default 0 not null, + `use_by` varchar(255) default '' not null, + `title` varchar(255) default '' not null, + `template` text not null, + `insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 collate = utf8mb4_general_ci; + +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (86, 12, 'ShowNotificationPage', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (87, 12, 'EditNotification', 0, ''); + +UPDATE `permission` set `name` = 'Setting' WHERE `id` = 7; +UPDATE `permission` set `pid` = 7 WHERE `pid` = 12; + +DELETE FROM `permission` WHERE `id` = 12; + +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (1, 1, 'deploy', '{{ .Project.Name }}', 'Deploy: {{ .Project.Name }} +Publisher: {{ .Project.PublisherName }} +Author: {{ .CommitInfo.Author }} +{{ if ne .CommitInfo.Tag }}Tag: {{ .CommitInfo.Tag }}{{ end }} +Branch: {{ .CommitInfo.Branch }} +CommitSHA: {{ .CommitInfo.Commit }} +CommitMessage: {{ .CommitInfo.Message }} +ServerList: +{{- range .ProjectServers}} + {{- if ne .Server.Name .Server.IP}} + {{- .Server.Name}}({{.Server.IP}}) + {{- else}} + {{- .Server.IP}} + {{- end}} +{{- end}} + +{{- if eq .DeployState 2 }} +State: success +{{- else }} +State: fail +{{- end }} +{{- if ne .DeployDetail ""}} +Detail: {{.DeployDetail}} +{{- end }}'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (2, 2, 'deploy', '{{ .Project.Name }}', '#### Deploy:{{ .Project.Name }} +#### Publisher:{{ .Project.PublisherName }} +#### Author:{{ .CommitInfo.Author }} +#### {{ if ne .CommitInfo.Tag }}Tag: {{ .CommitInfo.Tag }}{{ end }} +#### Branch:{{ .CommitInfo.Branch }} +#### CommitSHA:{{ .CommitInfo.Commit }} +#### CommitMessage: {{ .CommitInfo.Message }} +#### ServerList: +{{- range .ProjectServers}} + {{- if ne .Server.Name .Server.IP}} + {{- .Server.Name}}({{.Server.IP}}) + {{- else}} + {{- .Server.IP}} + {{- end}} +{{- end}} +#### +{{- if eq .DeployState 2 }}State: success +{{- else }}State: fail +{{- end }} +{{- if ne .DeployDetail ""}} +> Detail: {{.DeployDetail}} +{{- end }} +'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (3, 3, 'deploy', 'Deploy: {{ .Project.Name }}', 'Publisher: {{ .Project.PublisherName }} +Author: {{ .CommitInfo.Author }} +{{ if ne .CommitInfo.Tag "" }}Tag: {{ .CommitInfo.Tag }}{{ end }} +Branch: {{ .CommitInfo.Branch }} +CommitSHA: {{ .CommitInfo.Commit }} +CommitMessage: {{ .CommitInfo.Message }} +ServerList: +{{- range .ProjectServers}} + {{- if ne .Server.Name .Server.IP}} + {{- .Server.Name}}({{.Server.IP}}), + {{- else}} + {{- .Server.IP}}, + {{- end}} +{{- end}} +{{- if eq .DeployState 2 }} +State: success +{{- else }} +State: fail +{{- end }} +{{- if ne .DeployDetail ""}} +Detail: {{.DeployDetail }} +{{- end }}'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (4, 1, 'monitor', '{{ .Monitor.Name }}', 'Monitor: {{ .Monitor.Name }} +> can not access +> {{ .ErrorMsg }}'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (5, 2, 'monitor', '{{ .Monitor.Name }}', '#### Monitor: {{ .Monitor.Name }} can not access +{{ .ErrorMsg }}'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (6, 3, 'monitor', '{{ .Monitor.Name }}', 'can not access +detail: {{ .ErrorMsg }}'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (7, 1, 'server_monitor', '{{ .Server.Name }} {{ .ServerMonitor.Item }} Warning', 'Server: {{ .Server.Name }}({{ .Server.Description }}) +Item: {{ .ServerMonitor.Item }} warning +Event: {{ .ServerMonitor.Formula }} value: {{ .CycleValue }}, {{ .ServerMonitor.Operator }} {{ .ServerMonitor.Value }}'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (8, 2, 'server_monitor', '{{ .Server.Name }} {{ .ServerMonitor.Item }} Warning', 'Server: {{ .Server.Name }}({{ .Server.Description }}) +Item: {{ .ServerMonitor.Item }} warning +Event: {{ .ServerMonitor.Formula }} value: {{ .CycleValue }}, {{ .ServerMonitor.Operator }} {{ .ServerMonitor.Value }}'); +INSERT INTO goploy.notification_template (id, type, use_by, title, template) VALUES (9, 3, 'server_monitor', '{{ .Server.Name }} {{ .ServerMonitor.Item }} Warning', 'Server: {{ .Server.Name }}({{ .Server.Description }}) +Item: {{ .ServerMonitor.Item }} warning +Event: {{ .ServerMonitor.Formula }} value: {{ .CycleValue }}, {{ .ServerMonitor.Operator }} {{ .ServerMonitor.Value }}'); diff --git a/database/goploy.sql b/database/goploy.sql index c635391..472c64c 100644 --- a/database/goploy.sql +++ b/database/goploy.sql @@ -389,10 +389,21 @@ CREATE TABLE IF NOT EXISTS `terminal_log` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; +CREATE TABLE IF NOT EXISTS `notification_template` ( + `id` int unsigned auto_increment, + `type` tinyint unsigned default 0 not null, + `use_by` varchar(255) default '' not null, + `title` varchar(255) default '' not null, + `template` text not null, + `insert_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 collate = utf8mb4_general_ci; + INSERT IGNORE INTO `user`(`id`, `account`, `password`, `name`, `contact`, `state`, `super_manager`) VALUES (1, 'admin', '$2a$10$89ZJ2xeJj35GOw11Qiucr.phaEZP4.kBX6aKTs7oWFp1xcGBBgijm', '超管', '', 1, 1); INSERT IGNORE INTO `namespace`(`id`, `name`) VALUES (1, 'goploy'); INSERT IGNORE INTO `namespace_user`(`id`, `namespace_id`, `user_id`, `role_id`) VALUES (1, 1, 1, 0); -INSERT IGNORE INTO `system_config` (`id`, `key`, `value`) VALUES (1, 'version', '1.17.1'); +INSERT IGNORE INTO `system_config` (`id`, `key`, `value`) VALUES (1, 'version', '1.17.2'); INSERT IGNORE INTO `role`(`id`, `name`, `description`) VALUES (1, 'manager', ''); INSERT IGNORE INTO `role`(`id`, `name`, `description`) VALUES (2, 'member', ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (1, 0, 'Log', 0, ''); @@ -401,22 +412,21 @@ INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALU INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (4, 1, 'ShowSFTPLogPage', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (5, 1, 'ShowTerminalLogPage', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (6, 1, 'ShowTerminalRecord', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (7, 0, 'Member', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (7, 0, 'Setting', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (8, 7, 'ShowMemberPage', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (9, 7, 'AddMember', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (10, 7, 'EditMember', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (11, 7, 'DeleteMember', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (12, 0, 'Namespace', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (13, 12, 'ShowNamespacePage', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (14, 12, 'AddNamespace', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (15, 12, 'EditNamespace', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (16, 12, 'AddNamespaceUser', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (17, 12, 'DeleteNamespaceUser', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (18, 12, 'ShowRolePage', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (19, 12, 'AddRole', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (20, 12, 'EditRole', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (21, 12, 'DeleteRole', 0, ''); -INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (22, 12, 'EditPermission', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (13, 7, 'ShowNamespacePage', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (14, 7, 'AddNamespace', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (15, 7, 'EditNamespace', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (16, 7, 'AddNamespaceUser', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (17, 7, 'DeleteNamespaceUser', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (18, 7, 'ShowRolePage', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (19, 7, 'AddRole', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (20, 7, 'EditRole', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (21, 7, 'DeleteRole', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (22, 7, 'EditPermission', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (23, 0, 'Server', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (24, 23, 'ShowServerPage', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (25, 23, 'AddServer', 0, ''); @@ -479,6 +489,9 @@ INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALU INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (83, 23, 'DeleteNginxConfig', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (84, 23, 'UnbindServerProject', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (85, 43, 'ManageRepository', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (86, 7, 'ShowNotificationPage', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (87, 7, 'EditNotification', 0, ''); + INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (1, 14); INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (1, 15); INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (1, 16); @@ -534,4 +547,90 @@ INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (2, 57); INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (2, 59); INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (2, 60); INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (2, 61); -INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (2, 67); \ No newline at end of file +INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (2, 67); + +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (1, 1, 'deploy', '{{ .Project.Name }}', 'Deploy: {{ .Project.Name }} +Publisher: {{ .Project.PublisherName }} +Author: {{ .CommitInfo.Author }} +{{ if ne .CommitInfo.Tag }}Tag: {{ .CommitInfo.Tag }}{{ end }} +Branch: {{ .CommitInfo.Branch }} +CommitSHA: {{ .CommitInfo.Commit }} +CommitMessage: {{ .CommitInfo.Message }} +ServerList: +{{- range .ProjectServers}} + {{- if ne .Server.Name .Server.IP}} + {{- .Server.Name}}({{.Server.IP}}) + {{- else}} + {{- .Server.IP}} + {{- end}} +{{- end}} + +{{- if eq .DeployState 2 }} +State: success +{{- else }} +State: fail +{{- end }} +{{- if ne .DeployDetail ""}} +Detail: {{.DeployDetail}} +{{- end }}'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (2, 2, 'deploy', '{{ .Project.Name }}', '#### Deploy:{{ .Project.Name }} +#### Publisher:{{ .Project.PublisherName }} +#### Author:{{ .CommitInfo.Author }} +#### {{ if ne .CommitInfo.Tag }}Tag: {{ .CommitInfo.Tag }}{{ end }} +#### Branch:{{ .CommitInfo.Branch }} +#### CommitSHA:{{ .CommitInfo.Commit }} +#### CommitMessage: {{ .CommitInfo.Message }} +#### ServerList: +{{- range .ProjectServers}} + {{- if ne .Server.Name .Server.IP}} + {{- .Server.Name}}({{.Server.IP}}) + {{- else}} + {{- .Server.IP}} + {{- end}} +{{- end}} +#### +{{- if eq .DeployState 2 }}State: success +{{- else }}State: fail +{{- end }} +{{- if ne .DeployDetail ""}} +> Detail: {{.DeployDetail}} +{{- end }} +'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (3, 3, 'deploy', 'Deploy: {{ .Project.Name }}', 'Publisher: {{ .Project.PublisherName }} +Author: {{ .CommitInfo.Author }} +{{ if ne .CommitInfo.Tag "" }}Tag: {{ .CommitInfo.Tag }}{{ end }} +Branch: {{ .CommitInfo.Branch }} +CommitSHA: {{ .CommitInfo.Commit }} +CommitMessage: {{ .CommitInfo.Message }} +ServerList: +{{- range .ProjectServers}} + {{- if ne .Server.Name .Server.IP}} + {{- .Server.Name}}({{.Server.IP}}), + {{- else}} + {{- .Server.IP}}, + {{- end}} +{{- end}} +{{- if eq .DeployState 2 }} +State: success +{{- else }} +State: fail +{{- end }} +{{- if ne .DeployDetail ""}} +Detail: {{.DeployDetail }} +{{- end }}'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (4, 1, 'monitor', '{{ .Monitor.Name }}', 'Monitor: {{ .Monitor.Name }} +> can not access +> {{ .ErrorMsg }}'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (5, 2, 'monitor', '{{ .Monitor.Name }}', '#### Monitor: {{ .Monitor.Name }} can not access +{{ .ErrorMsg }}'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (6, 3, 'monitor', '{{ .Monitor.Name }}', 'can not access +detail: {{ .ErrorMsg }}'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (7, 1, 'server_monitor', '{{ .Server.Name }} {{ .ServerMonitor.Item }} Warning', 'Server: {{ .Server.Name }}({{ .Server.Description }}) +Item: {{ .ServerMonitor.Item }} warning +Event: {{ .ServerMonitor.Formula }} value: {{ .CycleValue }}, {{ .ServerMonitor.Operator }} {{ .ServerMonitor.Value }}'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (8, 2, 'server_monitor', '{{ .Server.Name }} {{ .ServerMonitor.Item }} Warning', 'Server: {{ .Server.Name }}({{ .Server.Description }}) +Item: {{ .ServerMonitor.Item }} warning +Event: {{ .ServerMonitor.Formula }} value: {{ .CycleValue }}, {{ .ServerMonitor.Operator }} {{ .ServerMonitor.Value }}'); +INSERT INTO `notification_template` (id, type, use_by, title, template) VALUES (9, 3, 'server_monitor', '{{ .Server.Name }} {{ .ServerMonitor.Item }} Warning', 'Server: {{ .Server.Name }}({{ .Server.Description }}) +Item: {{ .ServerMonitor.Item }} warning +Event: {{ .ServerMonitor.Formula }} value: {{ .CycleValue }}, {{ .ServerMonitor.Operator }} {{ .ServerMonitor.Value }}'); \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 8e5858b..56bd693 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,7 +1,7 @@ # Import sql manually https://github.com/zhenorzz/goploy/blob/master/model/sql/goploy.sql FROM alpine LABEL maintainer="zhenorzz@gmail.com" -ARG GOPLOY_VER=v1.17.1 +ARG GOPLOY_VER=v1.17.2 ENV GOPLOY_VER=${GOPLOY_VER} RUN echo "https://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories diff --git a/internal/model/monitor.go b/internal/model/monitor.go index 45a1939..3e29da0 100644 --- a/internal/model/monitor.go +++ b/internal/model/monitor.go @@ -5,10 +5,7 @@ package model import ( - "bytes" "encoding/json" - "io" - "net/http" "time" sq "github.com/Masterminds/squirrel" @@ -237,106 +234,3 @@ func (m Monitor) UpdateLatestErrorContent(errorContent string) error { Exec() return err } - -func (m Monitor) Notify(errMsg string) (string, error) { - var err error - var resp *http.Response - if m.NotifyType == NotifyWeiXin { - type markdown struct { - Content string `json:"content"` - } - type message struct { - Msgtype string `json:"msgtype"` - Markdown markdown `json:"markdown"` - } - content := "Monitor: " + m.Name + "\n " - content += "> can not access \n " - content += "> " + errMsg + " \n " - - msg := message{ - Msgtype: "markdown", - Markdown: markdown{ - Content: content, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(m.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if m.NotifyType == NotifyDingTalk { - type markdown struct { - Title string `json:"title"` - Text string `json:"text"` - } - type message struct { - Msgtype string `json:"msgtype"` - Markdown markdown `json:"markdown"` - } - text := "#### Monitor: " + m.Name + " can not access \n >" + errMsg - - msg := message{ - Msgtype: "markdown", - Markdown: markdown{ - Title: m.Name, - Text: text, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(m.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if m.NotifyType == NotifyFeiShu { - type content struct { - Text string `json:"text"` - } - type message struct { - MsgType string `json:"msg_type"` - Content content `json:"content"` - } - - text := m.Name + " can not access\n " - text += "detail: " + errMsg - - msg := message{ - MsgType: "text", - Content: content{ - Text: text, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(m.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if m.NotifyType == NotifyCustom { - type message struct { - Code int `json:"code"` - Message string `json:"message"` - Data struct { - MonitorName string `json:"monitorName"` - Type int `json:"type"` - Target MonitorTarget `json:"target"` - Second int `json:"second"` - Times uint16 `json:"times"` - Error string `json:"error"` - } `json:"data"` - } - code := 0 - msg := message{ - Code: code, - Message: m.Name + " can not access", - } - msg.Data.MonitorName = m.Name - msg.Data.Type = m.Type - msg.Data.Target = m.Target - msg.Data.Second = m.Second - msg.Data.Times = m.Times - msg.Data.Error = errMsg - b, _ := json.Marshal(msg) - resp, err = http.Post(m.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } - if err != nil { - return "", err - } - - defer resp.Body.Close() - responseData, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } else { - return string(responseData), err - } -} diff --git a/internal/model/notification_template.go b/internal/model/notification_template.go new file mode 100644 index 0000000..d9ce952 --- /dev/null +++ b/internal/model/notification_template.go @@ -0,0 +1,112 @@ +// Copyright 2022 The Goploy Authors. All rights reserved. +// Use of this source code is governed by a GPLv3-style +// license that can be found in the LICENSE file. + +package model + +import ( + sq "github.com/Masterminds/squirrel" +) + +const notificationTemplateTable = "`notification_template`" + +type NotificationTemplate struct { + ID int64 `json:"id"` + Type uint8 `json:"type"` + UseBy string `json:"useBy"` + Title string `json:"title"` + Template string `json:"template"` + InsertTime string `json:"insertTime,omitempty"` + UpdateTime string `json:"updateTime,omitempty"` +} + +type NotificationTemplates []NotificationTemplate + +func (nt NotificationTemplate) EditRow() error { + _, err := sq. + Update(notificationTemplateTable). + Set("template", nt.Template). + Set("title", nt.Title). + Where(sq.Eq{"id": nt.ID}). + RunWith(DB). + Exec() + return err +} + +func (nt NotificationTemplate) GetList() (NotificationTemplates, error) { + rows, err := sq. + Select("id, use_by, type, title, template, insert_time, update_time"). + From(notificationTemplateTable). + OrderBy("id DESC"). + RunWith(DB). + Query() + if err != nil { + return nil, err + } + + notificationTemplates := NotificationTemplates{} + for rows.Next() { + var notificationTemplate NotificationTemplate + + if err := rows.Scan( + ¬ificationTemplate.ID, + ¬ificationTemplate.UseBy, + ¬ificationTemplate.Type, + ¬ificationTemplate.Title, + ¬ificationTemplate.Template, + ¬ificationTemplate.InsertTime, + ¬ificationTemplate.UpdateTime); err != nil { + return nil, err + } + notificationTemplates = append(notificationTemplates, notificationTemplate) + } + + return notificationTemplates, nil +} + +func (nt NotificationTemplate) GetAll() (NotificationTemplates, error) { + rows, err := sq. + Select("id, use_by, type, template"). + From(notificationTemplateTable). + OrderBy("id DESC"). + RunWith(DB). + Query() + if err != nil { + return nil, err + } + notificationTemplates := NotificationTemplates{} + for rows.Next() { + var notificationTemplate NotificationTemplate + + if err := rows.Scan(¬ificationTemplate.ID, ¬ificationTemplate.UseBy, ¬ificationTemplate.Type, ¬ificationTemplate.Template); err != nil { + return notificationTemplates, err + } + notificationTemplates = append(notificationTemplates, notificationTemplate) + } + return notificationTemplates, nil +} + +func (nt NotificationTemplate) GetData() (NotificationTemplate, error) { + var notificationTemplate NotificationTemplate + err := sq. + Select("use_by, type, template"). + From(notificationTemplateTable). + Where(sq.Eq{"id": nt.ID}). + RunWith(DB). + QueryRow(). + Scan(¬ificationTemplate.UseBy, ¬ificationTemplate.Type, ¬ificationTemplate.Template) + return notificationTemplate, err +} + +func (nt NotificationTemplate) GetTemplate() (NotificationTemplate, error) { + var notificationTemplate NotificationTemplate + err := sq. + Select("title, template"). + From(notificationTemplateTable). + Where(sq.Eq{"use_by": nt.UseBy}). + Where(sq.Eq{"type": nt.Type}). + RunWith(DB). + QueryRow(). + Scan(¬ificationTemplate.Title, ¬ificationTemplate.Template) + return notificationTemplate, err +} diff --git a/internal/model/server_monitor.go b/internal/model/server_monitor.go index 943a22b..213aa0d 100644 --- a/internal/model/server_monitor.go +++ b/internal/model/server_monitor.go @@ -5,13 +5,7 @@ package model import ( - "bytes" - "encoding/json" - "fmt" sq "github.com/Masterminds/squirrel" - "io" - "net/http" - "time" ) const serverMonitorTable = "`server_monitor`" @@ -219,99 +213,3 @@ func (sm ServerMonitor) DeleteRow() error { Exec() return err } - -func (sm ServerMonitor) Notify(server Server, cycleValue string) (string, error) { - var err error - var resp *http.Response - if sm.NotifyType == NotifyWeiXin { - type markdown struct { - Content string `json:"content"` - } - type message struct { - Msgtype string `json:"msgtype"` - Markdown markdown `json:"markdown"` - } - content := fmt.Sprintf("Server: %s(%s)\n ", server.Name, server.Description) - content += "Item: " + sm.Item + " warning\n " - content += fmt.Sprintf("Event: %s value: %s, %s %s \n ", sm.Formula, cycleValue, sm.Operator, sm.Value) - - msg := message{ - Msgtype: "markdown", - Markdown: markdown{ - Content: content, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(sm.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if sm.NotifyType == NotifyDingTalk { - type markdown struct { - Title string `json:"title"` - Text string `json:"text"` - } - type message struct { - Msgtype string `json:"msgtype"` - Markdown markdown `json:"markdown"` - } - content := fmt.Sprintf("Server: %s(%s)\n ", server.Name, server.Description) - content += "Item: " + sm.Item + "\n " - content += fmt.Sprintf("Event: %s value: %s, %s %s \n ", sm.Formula, cycleValue, sm.Operator, sm.Value) - - msg := message{ - Msgtype: "markdown", - Markdown: markdown{ - Title: fmt.Sprintf("%s %s warning", server.Name, sm.Item), - Text: content, - }, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(sm.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if sm.NotifyType == NotifyFeiShu { - type message struct { - Title string `json:"title"` - Text string `json:"text"` - } - - content := fmt.Sprintf("Server: %s(%s)\n ", server.Name, server.Description) - content += "Item: " + sm.Item + "\n " - content += fmt.Sprintf("Event: %s value: %s, %s %s \n ", sm.Formula, cycleValue, sm.Operator, sm.Value) - - msg := message{ - Title: fmt.Sprintf("%s %s warning", server.Name, sm.Item), - Text: content, - } - b, _ := json.Marshal(msg) - resp, err = http.Post(sm.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } else if sm.NotifyType == NotifyCustom { - type message struct { - Code int `json:"code"` - Message string `json:"message"` - Data struct { - Server Server `json:"server"` - MonitorRule ServerMonitor `json:"monitorRule"` - Value string `json:"value"` - Time string `json:"time"` - } `json:"data"` - } - code := 0 - msg := message{ - Code: code, - Message: fmt.Sprintf("%s %s warning", server.Name, sm.Item), - } - msg.Data.Server = server - msg.Data.MonitorRule = sm - msg.Data.Value = cycleValue - msg.Data.Time = time.Now().Format("2006-01-02 15:04:05") - b, _ := json.Marshal(msg) - resp, err = http.Post(sm.NotifyTarget, "application/json", bytes.NewBuffer(b)) - } - if err != nil { - return "", err - } - defer resp.Body.Close() - responseData, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } else { - return string(responseData), err - } -} diff --git a/internal/notify/notify.go b/internal/notify/notify.go new file mode 100644 index 0000000..a2ea88d --- /dev/null +++ b/internal/notify/notify.go @@ -0,0 +1,167 @@ +package notify + +import ( + "bytes" + "encoding/json" + "fmt" + log "github.com/sirupsen/logrus" + "github.com/zhenorzz/goploy/internal/model" + "github.com/zhenorzz/goploy/internal/repo" + "io" + "net/http" + "text/template" +) + +type DeployData struct { + DeployState uint8 + Project model.Project + ProjectServers model.ProjectServers + CommitInfo repo.CommitInfo + DeployDetail string +} + +type MonitorData struct { + Monitor model.Monitor + ErrorMsg string +} + +type ServerMonitorData struct { + Server model.Server + ServerMonitor model.ServerMonitor + CycleValue string +} + +const ( + UseByDeploy = "deploy" + UseByMonitor = "monitor" + UseByServerMonitor = "server_monitor" +) + +func Send(name string, useBy string, data any, notifyType uint8, notifyTarget string) error { + if notifyType == model.NotifyCustom { + type message struct { + Data any `json:"data"` + } + msg := message{ + Data: data, + } + b, _ := json.Marshal(msg) + _, err := http.Post(notifyTarget, "application/json", bytes.NewBuffer(b)) + if err != nil { + log.Error(fmt.Sprintf("%s notify exec err: %s", name, err)) + return err + } + return nil + } + + notificationData, err := model.NotificationTemplate{UseBy: useBy, Type: notifyType}.GetTemplate() + if err != nil { + log.Error(fmt.Sprintf("%s could not find notification template: %s", name, err)) + return err + } + var buf bytes.Buffer + + tmpl, err := template.New(name + "title").Parse(notificationData.Title) + if err != nil { + log.Error(fmt.Sprintf("%s parse notification title error: %s", name, err)) + return err + } + + err = tmpl.Execute(&buf, data) + if err != nil { + log.Error(fmt.Sprintf("%s execute notification title error: %s", name, err)) + return err + } + + title := buf.String() + + tmpl, err = template.New(name + "template").Parse(notificationData.Template) + if err != nil { + log.Error(fmt.Sprintf("%s parse notification template error: %s", name, err)) + return err + } + + buf.Reset() + + err = tmpl.Execute(&buf, data) + if err != nil { + log.Error(fmt.Sprintf("%s execute notification template error: %s", name, err)) + return err + } + + text := buf.String() + + println(title) + + println(text) + + var resp *http.Response + if notifyType == model.NotifyWeiXin { + type markdown struct { + Content string `json:"content"` + } + type message struct { + Msgtype string `json:"msgtype"` + Markdown markdown `json:"markdown"` + } + + text = title + "\n" + text + msg := message{ + Msgtype: "markdown", + Markdown: markdown{ + Content: text, + }, + } + b, _ := json.Marshal(msg) + resp, err = http.Post(notifyTarget, "application/json", bytes.NewBuffer(b)) + } else if notifyType == model.NotifyDingTalk { + type markdown struct { + Title string `json:"title"` + Text string `json:"text"` + } + type message struct { + Msgtype string `json:"msgtype"` + Markdown markdown `json:"markdown"` + } + msg := message{ + Msgtype: "markdown", + Markdown: markdown{ + Title: title, + Text: text, + }, + } + b, _ := json.Marshal(msg) + resp, err = http.Post(notifyTarget, "application/json", bytes.NewBuffer(b)) + } else if notifyType == model.NotifyFeiShu { + type content struct { + Text string `json:"text"` + } + type message struct { + MsgType string `json:"msg_type"` + Content content `json:"content"` + } + text = title + "\n" + text + msg := message{ + MsgType: "text", + Content: content{ + Text: text, + }, + } + b, _ := json.Marshal(msg) + resp, err = http.Post(notifyTarget, "application/json", bytes.NewBuffer(b)) + } + + if err != nil { + log.Error(fmt.Sprintf("%s notify exec err: %s", name, err)) + return err + } else if resp != nil { + responseData, err := io.ReadAll(resp.Body) + if err != nil { + log.Error(fmt.Sprintf("%s notify read body err: %s", name, err)) + } else { + log.Trace(fmt.Sprintf("%s notify success: %s", name, string(responseData))) + } + _ = resp.Body.Close() + } + return nil +} diff --git a/web/package.json b/web/package.json index 791ae99..07bd99a 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "goploy", - "version": "1.17.1", + "version": "1.17.2", "license": "GPL-3.0-or-later", "scripts": { "dev": "vite", diff --git a/web/src/api/notification.ts b/web/src/api/notification.ts new file mode 100644 index 0000000..1369917 --- /dev/null +++ b/web/src/api/notification.ts @@ -0,0 +1,33 @@ +import { Request, ID } from './types' + +export interface NotificationData { + id: number + useBy: string + type: number + title: string + template: string + insertTime: string + updateTime: string +} + +export class NotificationList extends Request { + readonly url = '/notification/getList' + readonly method = 'get' + + public declare datagram: { + list: NotificationData[] + } +} + +export class NotificationEdit extends Request { + readonly url = '/notification/edit' + readonly method = 'put' + public param: { + id: number + template: string + } + constructor(param: NotificationEdit['param']) { + super() + this.param = param + } +} diff --git a/web/src/icons/svg/notifySetting.svg b/web/src/icons/svg/notifySetting.svg new file mode 100644 index 0000000..456c5e6 --- /dev/null +++ b/web/src/icons/svg/notifySetting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/lang/en.json b/web/src/lang/en.json index b3e6c82..ff4fa2f 100644 --- a/web/src/lang/en.json +++ b/web/src/lang/en.json @@ -1,4 +1,5 @@ { + "title": "Title", "default": "Default", "select": "Select", "name": "Name", @@ -171,9 +172,11 @@ "serverCrontab": "Crontab UI", "serverCron": "Cron", "serverNginx": "Nginx", - "namespace": "Namespace", + "setting": "Setting", "namespaceSetting": "NS setting", "roleSetting": "Role setting", + "memberSetting": "Member setting", + "notificationSetting": "Notify setting", "member": "Member", "log": "Log", "loginLog": "Login log", diff --git a/web/src/lang/zh-cn.json b/web/src/lang/zh-cn.json index 69c6fd2..c6d8734 100644 --- a/web/src/lang/zh-cn.json +++ b/web/src/lang/zh-cn.json @@ -1,4 +1,5 @@ { + "title": "标题", "default": "默认", "select": "选择", "name": "名称", @@ -153,9 +154,11 @@ "serverCrontab": "Crontab UI", "serverCron": "定时任务", "serverNginx": "Nginx管理", - "namespace": "空间", + "setting": "设置", "namespaceSetting": "空间设置", "roleSetting": "角色设置", + "memberSetting": "成员设置", + "notificationSetting": "推送设置", "member": "成员设置", "log": "日志", "loginLog": "登录日志", @@ -171,7 +174,7 @@ "ShowSFTPLogPage": "查看SFTP日志", "ShowTerminalLogPage": "查看终端日志", "ShowTerminalRecord": "查看终端录屏", - "Member": "成员管理", + "Setting": "设置", "ShowMemberPage": "查看成员设置", "AddMember": "新增成员", "EditMember": "编辑成员", @@ -186,6 +189,8 @@ "AddRole": "新增角色", "EditRole": "编辑角色", "DeleteRole": "删除角色", + "ShowNotificationPage": "查看推送设置", + "EditNotification": "编辑推送设置", "EditPermission": "编辑角色权限", "Server": "服务器管理", "ShowServerPage": "查看服务器设置", diff --git a/web/src/permission.ts b/web/src/permission.ts index 813c98b..9162a5c 100644 --- a/web/src/permission.ts +++ b/web/src/permission.ts @@ -5,12 +5,11 @@ export default Object.freeze({ ShowSFTPLogPage: 4, ShowTerminalLogPage: 5, ShowTerminalRecord: 6, - Member: 7, + Setting: 7, ShowMemberPage: 8, AddMember: 9, EditMember: 10, DeleteMember: 11, - Namespace: 12, ShowNamespacePage: 13, AddNamespace: 14, EditNamespace: 15, @@ -84,4 +83,6 @@ export default Object.freeze({ DeleteNginxConfig: 83, UnbindServerProject: 84, ManageRepository: 85, + ShowNotificationPage: 86, + EditNotification: 87, }) diff --git a/web/src/router/asyncRoutes.ts b/web/src/router/asyncRoutes.ts index 43e8dec..3688cfd 100644 --- a/web/src/router/asyncRoutes.ts +++ b/web/src/router/asyncRoutes.ts @@ -169,19 +169,19 @@ export default [ ], }, { - path: '/namespace', + path: '/setting', component: Layout, - redirect: '/namespace/index', - name: 'namespace', + redirect: '/setting/namespace', + name: 'setting', meta: { - title: 'namespace', - icon: 'namespace', + title: 'setting', + icon: 'setting', }, children: [ { - path: 'index', - name: 'NamespaceIndex', - component: () => import('@/views/namespace/index.vue'), + path: 'namespace', + name: 'NamespaceSetting', + component: () => import('@/views/setting/namespace.vue'), meta: { title: 'namespaceSetting', icon: 'namespaceSetting', @@ -190,36 +190,34 @@ export default [ }, { path: 'role', - name: 'NamespaceRole', - component: () => import('@/views/namespace/role.vue'), + name: 'RoleSetting', + component: () => import('@/views/setting/role.vue'), meta: { title: 'roleSetting', icon: 'roleSetting', permissions: [permission.ShowRolePage], }, }, - ], - }, - { - path: '/member', - component: Layout, - redirect: '/member/index', - name: 'member', - meta: { - title: 'member', - icon: 'user', - }, - children: [ { - path: 'index', - name: 'MemberIndex', - component: () => import('@/views/member/index.vue'), + path: 'member', + name: 'MemberSetting', + component: () => import('@/views/setting/member.vue'), meta: { - title: 'member', + title: 'memberSetting', icon: 'user', permissions: [permission.ShowMemberPage], }, }, + { + path: 'notification', + name: 'NotificationSetting', + component: () => import('@/views/setting/notification.vue'), + meta: { + title: 'notificationSetting', + icon: 'notifySetting', + permissions: [permission.ShowNotificationPage], + }, + }, ], }, { diff --git a/web/src/views/deploy/index.vue b/web/src/views/deploy/index.vue index c8c7dad..2da30b5 100644 --- a/web/src/views/deploy/index.vue +++ b/web/src/views/deploy/index.vue @@ -111,7 +111,7 @@ :href="row.name" target="_blank" :underline="false" - class="card-title__text" + class="card-title__link" style="color: inherit" > {{ row.name }} @@ -292,6 +292,9 @@ + + {{ $t('initial') }} + - + - + { + customVariables.map((item: any) => { if (item.type == 'list') { item.value = '' } @@ -880,6 +888,13 @@ function restorePublishForm() { font-size: 14px; font-weight: 600; white-space: nowrap; + flex: 1; + } + .card-title__link { + overflow: hidden; + text-overflow: ellipsis; + font-weight: 600; + white-space: nowrap; } } diff --git a/web/src/views/namespace/components/TheUserDialog.vue b/web/src/views/setting/components/TheUserDialog.vue similarity index 100% rename from web/src/views/namespace/components/TheUserDialog.vue rename to web/src/views/setting/components/TheUserDialog.vue diff --git a/web/src/views/member/index.vue b/web/src/views/setting/member.vue similarity index 99% rename from web/src/views/member/index.vue rename to web/src/views/setting/member.vue index 068634a..4e38caa 100644 --- a/web/src/views/member/index.vue +++ b/web/src/views/setting/member.vue @@ -165,7 +165,7 @@ + diff --git a/web/src/views/namespace/role.vue b/web/src/views/setting/role.vue similarity index 99% rename from web/src/views/namespace/role.vue rename to web/src/views/setting/role.vue index 1a60d12..7ef004d 100644 --- a/web/src/views/namespace/role.vue +++ b/web/src/views/setting/role.vue @@ -169,7 +169,7 @@