diff --git a/cmd/dashboard/controller/common_page.go b/cmd/dashboard/controller/common_page.go index c6396c9..57a4bae 100644 --- a/cmd/dashboard/controller/common_page.go +++ b/cmd/dashboard/controller/common_page.go @@ -1,12 +1,15 @@ package controller import ( + "errors" + "fmt" "log" "net/http" "time" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" + "golang.org/x/crypto/bcrypt" "github.com/naiba/nezha/model" "github.com/naiba/nezha/pkg/mygin" @@ -20,11 +23,63 @@ type commonPage struct { func (cp *commonPage) serve() { cr := cp.r.Group("") cr.Use(mygin.Authorize(mygin.AuthorizeOption{})) + cr.POST("/view-password", cp.issueViewPassword) + cr.Use(cp.checkViewPassword) // 前端查看密码鉴权 cr.GET("/", cp.home) cr.GET("/service", cp.service) cr.GET("/ws", cp.ws) } +type viewPasswordForm struct { + Password string +} + +func (p *commonPage) issueViewPassword(c *gin.Context) { + var vpf viewPasswordForm + err := c.ShouldBind(&vpf) + var hash []byte + if err == nil && vpf.Password != dao.Conf.Site.ViewPassword { + err = errors.New("查看密码错误") + } + if err == nil { + hash, err = bcrypt.GenerateFromPassword([]byte(vpf.Password), bcrypt.DefaultCost) + } + if err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Title: "出现错误", + Msg: fmt.Sprintf("请求错误:%s", err), + }, true) + c.Abort() + return + } + c.SetCookie(dao.Conf.Site.CookieName+"-vp", string(hash), 60*60*24, "", "", false, false) + c.Redirect(http.StatusFound, c.Request.Referer()) +} + +func (p *commonPage) checkViewPassword(c *gin.Context) { + if dao.Conf.Site.ViewPassword == "" { + c.Next() + return + } + if _, authorized := c.Get(model.CtxKeyAuthorizedUser); authorized { + c.Next() + return + } + + // 验证查看密码 + viewPassword, _ := c.Cookie(dao.Conf.Site.CookieName + "-vp") + if err := bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(dao.Conf.Site.ViewPassword)); err != nil { + c.HTML(http.StatusOK, "theme-"+dao.Conf.Site.Theme+"/viewpassword", mygin.CommonEnvironment(c, gin.H{ + "Title": "验证查看密码", + "CustomCode": dao.Conf.Site.CustomCode, + })) + c.Abort() + return + } + + c.Next() +} + type ServiceItem struct { Monitor model.Monitor TotalUp uint64 diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go index f19ebc2..01cd38b 100644 --- a/cmd/dashboard/controller/member_api.go +++ b/cmd/dashboard/controller/member_api.go @@ -445,6 +445,7 @@ type settingForm struct { Admin string Theme string CustomCode string + ViewPassword string EnableIPChangeNotification string } @@ -461,6 +462,7 @@ func (ma *memberAPI) updateSetting(c *gin.Context) { dao.Conf.Site.Brand = sf.Title dao.Conf.Site.Theme = sf.Theme dao.Conf.Site.CustomCode = sf.CustomCode + dao.Conf.Site.ViewPassword = sf.ViewPassword dao.Conf.GitHub.Admin = sf.Admin if err := dao.Conf.Save(); err != nil { c.JSON(http.StatusOK, model.Response{ diff --git a/cmd/dashboard/main.go b/cmd/dashboard/main.go index 39a6a51..c36b729 100644 --- a/cmd/dashboard/main.go +++ b/cmd/dashboard/main.go @@ -40,6 +40,9 @@ func init() { if dao.Conf.Debug { dao.DB = dao.DB.Debug() } + if dao.Conf.GRPCPort == 0 { + dao.Conf.GRPCPort = 5555 + } dao.Cache = cache.New(5*time.Minute, 10*time.Minute) initSystem() @@ -105,7 +108,7 @@ func loadCrons() { func main() { go controller.ServeWeb(dao.Conf.HTTPPort) - go rpc.ServeRPC(5555) + go rpc.ServeRPC(dao.Conf.GRPCPort) go rpc.DispatchTask(time.Minute * 3) dao.AlertSentinelStart() } diff --git a/go.mod b/go.mod index 27fb8f9..7c175f4 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/shirou/gopsutil/v3 v3.20.11 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 google.golang.org/grpc v1.33.1 google.golang.org/protobuf v1.25.0 diff --git a/model/config.go b/model/config.go index a3e12d1..5d5b7f4 100644 --- a/model/config.go +++ b/model/config.go @@ -13,10 +13,11 @@ import ( type Config struct { Debug bool Site struct { - Brand string // 站点名称 - CookieName string // 浏览器 Cookie 名称 - Theme string - CustomCode string + Brand string // 站点名称 + CookieName string // 浏览器 Cookie 名称 + Theme string + CustomCode string + ViewPassword string // 前台查看密码 } GitHub struct { Admin string // 管理员ID列表 @@ -24,6 +25,7 @@ type Config struct { ClientSecret string } HTTPPort uint + GRPCPort uint EnableIPChangeNotification bool v *viper.Viper diff --git a/pkg/mygin/auth.go b/pkg/mygin/auth.go index 8ee0103..83726d9 100644 --- a/pkg/mygin/auth.go +++ b/pkg/mygin/auth.go @@ -26,6 +26,7 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) { if opt.Guest { code = http.StatusBadRequest } + commonErr := ErrInfo{ Title: "访问受限", Code: code, @@ -36,6 +37,7 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) { var isLogin bool + // 用户鉴权 token, _ := c.Cookie(dao.Conf.Site.CookieName) token = strings.TrimSpace(token) if token != "" { diff --git a/resource/template/dashboard/setting.html b/resource/template/dashboard/setting.html index f628dcb..b597cc3 100644 --- a/resource/template/dashboard/setting.html +++ b/resource/template/dashboard/setting.html @@ -23,6 +23,10 @@ +