From 9352d3d0e04993eead918574732659f9311786f2 Mon Sep 17 00:00:00 2001 From: naiba Date: Sat, 17 Sep 2022 00:08:27 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20batch=20update=20server=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/dashboard/controller/common_page.go | 4 +- cmd/dashboard/controller/member_api.go | 63 +++++++++++++++++++ cmd/dashboard/controller/oauth2.go | 4 +- resource/l10n/zh-CN.toml | 6 ++ .../template/dashboard-default/server.html | 54 ++++++++++++++++ 5 files changed, 128 insertions(+), 3 deletions(-) diff --git a/cmd/dashboard/controller/common_page.go b/cmd/dashboard/controller/common_page.go index 4da4208..06ee331 100644 --- a/cmd/dashboard/controller/common_page.go +++ b/cmd/dashboard/controller/common_page.go @@ -167,6 +167,8 @@ type Data struct { Servers []*model.Server `json:"servers,omitempty"` } +var cloudflareCookiesValidator = regexp.MustCompile("^[A-Za-z0-9-_]+$") + func (cp *commonPage) ws(c *gin.Context) { conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) if err != nil { @@ -285,7 +287,7 @@ func (cp *commonPage) terminal(c *gin.Context) { encodedCookies := strings.Split(cloudflareCookies, ".") if len(encodedCookies) == 3 { for i := 0; i < 3; i++ { - if valid, _ := regexp.MatchString("^[A-Za-z0-9-_]+$", encodedCookies[i]); !valid { + if !cloudflareCookiesValidator.MatchString(encodedCookies[i]) { cloudflareCookies = "" break } diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go index cde4cb3..e707262 100644 --- a/cmd/dashboard/controller/member_api.go +++ b/cmd/dashboard/controller/member_api.go @@ -10,6 +10,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/jinzhu/copier" "github.com/naiba/nezha/model" "github.com/naiba/nezha/pkg/mygin" @@ -39,6 +40,7 @@ func (ma *memberAPI) serve() { mr.POST("/cron", ma.addOrEditCron) mr.GET("/cron/:id/manual", ma.manualTrigger) mr.POST("/force-update", ma.forceUpdate) + mr.POST("/batch-update-server-group", ma.batchUpdateServerGroup) mr.POST("/notification", ma.addOrEditNotification) mr.POST("/alert-rule", ma.addOrEditAlertRule) mr.POST("/setting", ma.updateSetting) @@ -537,6 +539,67 @@ func (ma *memberAPI) manualTrigger(c *gin.Context) { }) } +type BatchUpdateServerGroupRequest struct { + Servers []uint64 + Group string +} + +func (ma *memberAPI) batchUpdateServerGroup(c *gin.Context) { + var req BatchUpdateServerGroupRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusBadRequest, + Message: err.Error(), + }) + return + } + + if err := singleton.DB.Model(&model.Server{}).Where("id in (?)", req.Servers).Update("tag", req.Group).Error; err != nil { + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusBadRequest, + Message: err.Error(), + }) + return + } + + singleton.ServerLock.Lock() + + for i := 0; i < len(req.Servers); i++ { + serverId := req.Servers[i] + var s model.Server + copier.Copy(&s, singleton.ServerList[serverId]) + s.Tag = req.Group + // 如果修改了Tag + if s.Tag != singleton.ServerList[s.ID].Tag { + index := -1 + for i := 0; i < len(singleton.ServerTagToIDList[s.Tag]); i++ { + if singleton.ServerTagToIDList[s.Tag][i] == s.ID { + index = i + break + } + } + if index > -1 { + // 删除旧 Tag-ID 绑定关系 + singleton.ServerTagToIDList[singleton.ServerList[s.ID].Tag] = append(singleton.ServerTagToIDList[singleton.ServerList[s.ID].Tag][:index], singleton.ServerTagToIDList[singleton.ServerList[s.ID].Tag][index+1:]...) + } + // 设置新的 Tag-ID 绑定关系 + singleton.ServerTagToIDList[s.Tag] = append(singleton.ServerTagToIDList[s.Tag], s.ID) + if len(singleton.ServerTagToIDList[s.Tag]) == 0 { + delete(singleton.ServerTagToIDList, s.Tag) + } + } + singleton.ServerList[s.ID] = &s + } + + singleton.ServerLock.Unlock() + + singleton.ReSortServer() + + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusOK, + }) +} + func (ma *memberAPI) forceUpdate(c *gin.Context) { var forceUpdateServers []uint64 if err := c.ShouldBindJSON(&forceUpdateServers); err != nil { diff --git a/cmd/dashboard/controller/oauth2.go b/cmd/dashboard/controller/oauth2.go index f53d70c..56d2634 100644 --- a/cmd/dashboard/controller/oauth2.go +++ b/cmd/dashboard/controller/oauth2.go @@ -13,7 +13,7 @@ import ( "github.com/xanzy/go-gitlab" "golang.org/x/oauth2" GitHubOauth2 "golang.org/x/oauth2/github" - GitlabOauth2 "golang.org/x/oauth2/github" + GitlabOauth2 "golang.org/x/oauth2/gitlab" "github.com/naiba/nezha/model" "github.com/naiba/nezha/pkg/mygin" @@ -156,7 +156,7 @@ func (oa *oauth2controller) callback(c *gin.Context) { } var isAdmin bool for _, admin := range strings.Split(singleton.Conf.Oauth2.Admin, ",") { - if admin != "" && strings.ToLower(user.Login) == strings.ToLower(admin) { + if admin != "" && strings.EqualFold(user.Login, admin) { isAdmin = true break } diff --git a/resource/l10n/zh-CN.toml b/resource/l10n/zh-CN.toml index 4da8ff1..3018106 100644 --- a/resource/l10n/zh-CN.toml +++ b/resource/l10n/zh-CN.toml @@ -181,6 +181,12 @@ other = "启用" [AddServer] other = "添加服务器" +[BatchEditServerGroup] +other = "批量修改分组" + +[InputServerGroupName] +other = "输入分组名称" + [ServerGroup] other = "服务器分组" diff --git a/resource/template/dashboard-default/server.html b/resource/template/dashboard-default/server.html index 1bbd3ad..4f55c64 100644 --- a/resource/template/dashboard-default/server.html +++ b/resource/template/dashboard-default/server.html @@ -5,6 +5,9 @@
+ @@ -128,5 +131,56 @@ }); }) } + function batchEditServerGroup() { + let groupName = prompt('{{tr "InputServerGroupName"}}') + if (!groupName) { + return; + } + const servers = [] + checkBoxList.forEach(cb => { + if (cb.checked) { + servers.push(parseInt(cb.value)) + } + }) + if (servers.length == 0) { + $.suiAlert({ + title: '{{tr "NoServerSelected"}}', + description: '', + type: 'warning', + time: '2', + position: 'top-center', + }); + return + } + $.post('/api/batch-update-server-group', JSON.stringify({ servers: servers, group: groupName })) + .then((resp) => { + if (resp.code == 200) { + $.suiAlert({ + title: '{{tr "ExecutionResults"}}', + description: resp.message, + type: 'success', + time: '3', + position: 'top-center', + }); + window.location.reload() + } else { + $.suiAlert({ + title: '', + description: resp.message, + type: 'error', + time: '3', + position: 'top-center', + }); + } + }).catch(err => { + $.suiAlert({ + title: '', + description: err, + type: 'error', + time: '3', + position: 'top-center', + }); + }) + } {{end}}