mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-02-04 12:40:07 +00:00
refactor: remove pages, combine grpc http port
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
@@ -18,30 +17,30 @@ type apiV1 struct {
|
||||
func (v *apiV1) serve() {
|
||||
r := v.r.Group("")
|
||||
// 强制认证的 API
|
||||
r.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
MemberOnly: true,
|
||||
AllowAPI: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
// r.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
// MemberOnly: true,
|
||||
// AllowAPI: true,
|
||||
// IsPage: false,
|
||||
// Msg: "访问此接口需要认证",
|
||||
// Btn: "点此登录",
|
||||
// Redirect: "/login",
|
||||
// }))
|
||||
r.GET("/server/list", v.serverList)
|
||||
r.GET("/server/details", v.serverDetails)
|
||||
// 不强制认证的 API
|
||||
mr := v.r.Group("monitor")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
MemberOnly: false,
|
||||
IsPage: false,
|
||||
AllowAPI: true,
|
||||
Msg: "访问此接口需要认证",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
mr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
IsPage: false,
|
||||
AbortWhenFail: true,
|
||||
}))
|
||||
// mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
// MemberOnly: false,
|
||||
// IsPage: false,
|
||||
// AllowAPI: true,
|
||||
// Msg: "访问此接口需要认证",
|
||||
// Btn: "点此登录",
|
||||
// Redirect: "/login",
|
||||
// }))
|
||||
// mr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
// IsPage: false,
|
||||
// AbortWhenFail: true,
|
||||
// }))
|
||||
mr.GET("/:id", v.monitorHistoriesById)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,9 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/jinzhu/copier"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/naiba/nezha/pkg/websocketx"
|
||||
"github.com/naiba/nezha/proto"
|
||||
@@ -29,14 +27,11 @@ type commonPage struct {
|
||||
|
||||
func (cp *commonPage) serve() {
|
||||
cr := cp.r.Group("")
|
||||
cr.Use(mygin.Authorize(mygin.AuthorizeOption{}))
|
||||
cr.Use(mygin.PreferredTheme)
|
||||
cr.POST("/view-password", cp.issueViewPassword)
|
||||
cr.GET("/terminal/:id", cp.terminal)
|
||||
cr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
IsPage: true,
|
||||
AbortWhenFail: true,
|
||||
}))
|
||||
// cr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
|
||||
// IsPage: true,
|
||||
// AbortWhenFail: true,
|
||||
// }))
|
||||
cr.GET("/", cp.home)
|
||||
cr.GET("/service", cp.service)
|
||||
// TODO: 界面直接跳转使用该接口
|
||||
@@ -48,44 +43,6 @@ func (cp *commonPage) serve() {
|
||||
cr.GET("/file/:id", cp.fm)
|
||||
}
|
||||
|
||||
type viewPasswordForm struct {
|
||||
Password string
|
||||
}
|
||||
|
||||
// PingExample godoc
|
||||
// @Summary ping example
|
||||
// @Schemes
|
||||
// @Description do ping
|
||||
// @Tags example
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} Helloworld
|
||||
// @Router /example/helloworld [get]
|
||||
func (p *commonPage) issueViewPassword(c *gin.Context) {
|
||||
var vpf viewPasswordForm
|
||||
err := c.ShouldBind(&vpf)
|
||||
var hash []byte
|
||||
if err == nil && vpf.Password != singleton.Conf.Site.ViewPassword {
|
||||
// err = errors.New(singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "WrongAccessPassword"}))
|
||||
}
|
||||
if err == nil {
|
||||
hash, err = bcrypt.GenerateFromPassword([]byte(vpf.Password), bcrypt.DefaultCost)
|
||||
}
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusOK,
|
||||
// Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// MessageID: "AnErrorEccurred",
|
||||
// }),
|
||||
Msg: err.Error(),
|
||||
}, true)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.SetCookie(singleton.Conf.Site.CookieName+"-vp", string(hash), 60*60*24, "", "", false, false)
|
||||
c.Redirect(http.StatusFound, c.Request.Referer())
|
||||
}
|
||||
|
||||
func (p *commonPage) service(c *gin.Context) {
|
||||
res, _, _ := p.requestGroup.Do("servicePage", func() (interface{}, error) {
|
||||
singleton.AlertsLock.RLock()
|
||||
@@ -104,11 +61,11 @@ func (p *commonPage) service(c *gin.Context) {
|
||||
stats, statsStore,
|
||||
}, nil
|
||||
})
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/service"), mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesStatus"}),
|
||||
"Services": res.([]interface{})[0],
|
||||
"CycleTransferStats": res.([]interface{})[1],
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (cp *commonPage) network(c *gin.Context) {
|
||||
@@ -124,13 +81,13 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
}
|
||||
if err := singleton.DB.Model(&model.MonitorHistory{}).Select("monitor_id, server_id").
|
||||
Where("monitor_id != 0 and server_id != 0").Limit(1).First(&monitorHistory).Error; err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + "server monitor history not found",
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "请求参数有误:" + "server monitor history not found",
|
||||
// Link: "/",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
} else {
|
||||
if monitorHistory == nil || monitorHistory.ServerID == 0 {
|
||||
@@ -147,24 +104,24 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
var err error
|
||||
id, err = strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + err.Error(),
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "请求参数有误:" + err.Error(),
|
||||
// Link: "/",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
_, ok := singleton.ServerList[id]
|
||||
if !ok {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + "server id not found",
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "请求参数有误:" + "server id not found",
|
||||
// Link: "/",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -178,13 +135,13 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
Where("server_id != 0").
|
||||
Find(&serverIdsWithMonitor).
|
||||
Error; err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + "no server with monitor histories",
|
||||
Link: "/",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "请求参数有误:" + "no server with monitor histories",
|
||||
// Link: "/",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
if isMember || isViewPasswordVerfied {
|
||||
@@ -209,11 +166,10 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
Servers: servers,
|
||||
})
|
||||
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/network"), mygin.CommonEnvironment(c, gin.H{
|
||||
"Servers": string(serversBytes),
|
||||
"MonitorInfos": string(monitorInfos),
|
||||
"MaxTCPPingValue": singleton.Conf.MaxTCPPingValue,
|
||||
}))
|
||||
c.HTML(http.StatusOK, "", gin.H{
|
||||
"Servers": string(serversBytes),
|
||||
"MonitorInfos": string(monitorInfos),
|
||||
})
|
||||
}
|
||||
|
||||
func (cp *commonPage) getServerStat(c *gin.Context, withPublicNote bool) ([]byte, error) {
|
||||
@@ -251,20 +207,20 @@ func (cp *commonPage) getServerStat(c *gin.Context, withPublicNote bool) ([]byte
|
||||
func (cp *commonPage) home(c *gin.Context) {
|
||||
stat, err := cp.getServerStat(c, true)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
// Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// MessageID: "SystemError",
|
||||
// }),
|
||||
Msg: "服务器状态获取失败",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusInternalServerError,
|
||||
// // Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// // MessageID: "SystemError",
|
||||
// // }),
|
||||
// Msg: "服务器状态获取失败",
|
||||
// Link: "/",
|
||||
// Btn: "返回首页",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/home"), mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "", gin.H{
|
||||
"Servers": string(stat),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
@@ -280,15 +236,15 @@ type Data struct {
|
||||
func (cp *commonPage) ws(c *gin.Context) {
|
||||
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
// Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// MessageID: "NetworkError",
|
||||
// }),
|
||||
Msg: "Websocket协议切换失败",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusInternalServerError,
|
||||
// // Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// // MessageID: "NetworkError",
|
||||
// // }),
|
||||
// Msg: "Websocket协议切换失败",
|
||||
// Link: "/",
|
||||
// Btn: "返回首页",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
@@ -315,28 +271,28 @@ func (cp *commonPage) ws(c *gin.Context) {
|
||||
func (cp *commonPage) terminal(c *gin.Context) {
|
||||
streamId := c.Param("id")
|
||||
if _, err := rpc.NezhaHandlerSingleton.GetStream(streamId); err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "无权访问",
|
||||
Msg: "终端会话不存在",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "无权访问",
|
||||
// Msg: "终端会话不存在",
|
||||
// Link: "/",
|
||||
// Btn: "返回首页",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
defer rpc.NezhaHandlerSingleton.CloseStream(streamId)
|
||||
|
||||
wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
// Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// MessageID: "NetworkError",
|
||||
// }),
|
||||
Msg: "Websocket协议切换失败",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusInternalServerError,
|
||||
// // Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// // MessageID: "NetworkError",
|
||||
// // }),
|
||||
// Msg: "Websocket协议切换失败",
|
||||
// Link: "/",
|
||||
// Btn: "返回首页",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
@@ -367,38 +323,38 @@ type createTerminalRequest struct {
|
||||
|
||||
func (cp *commonPage) createTerminal(c *gin.Context) {
|
||||
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); !authorized {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "无权访问",
|
||||
Msg: "用户未登录",
|
||||
Link: "/login",
|
||||
Btn: "去登录",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "无权访问",
|
||||
// Msg: "用户未登录",
|
||||
// Link: "/login",
|
||||
// Btn: "去登录",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
var createTerminalReq createTerminalRequest
|
||||
if err := c.ShouldBind(&createTerminalReq); err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + err.Error(),
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "请求参数有误:" + err.Error(),
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
// Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// MessageID: "SystemError",
|
||||
// }),
|
||||
Msg: "生成会话ID失败",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusInternalServerError,
|
||||
// // Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// // MessageID: "SystemError",
|
||||
// // }),
|
||||
// Msg: "生成会话ID失败",
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -408,13 +364,13 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
||||
server := singleton.ServerList[createTerminalReq.ID]
|
||||
singleton.ServerLock.RUnlock()
|
||||
if server == nil || server.TaskStream == nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "服务器不存在或处于离线状态",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "服务器不存在或处于离线状态",
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -425,48 +381,48 @@ func (cp *commonPage) createTerminal(c *gin.Context) {
|
||||
Type: model.TaskTypeTerminalGRPC,
|
||||
Data: string(terminalData),
|
||||
}); err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "Agent信令下发失败",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "Agent信令下发失败",
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/terminal", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "", gin.H{
|
||||
"SessionID": streamId,
|
||||
"ServerName": server.Name,
|
||||
"ServerID": server.ID,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (cp *commonPage) fm(c *gin.Context) {
|
||||
streamId := c.Param("id")
|
||||
if _, err := rpc.NezhaHandlerSingleton.GetStream(streamId); err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "无权访问",
|
||||
Msg: "FM会话不存在",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "无权访问",
|
||||
// Msg: "FM会话不存在",
|
||||
// Link: "/",
|
||||
// Btn: "返回首页",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
defer rpc.NezhaHandlerSingleton.CloseStream(streamId)
|
||||
|
||||
wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
// Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// MessageID: "NetworkError",
|
||||
// }),
|
||||
Msg: "Websocket协议切换失败",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusInternalServerError,
|
||||
// // Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// // MessageID: "NetworkError",
|
||||
// // }),
|
||||
// Msg: "Websocket协议切换失败",
|
||||
// Link: "/",
|
||||
// Btn: "返回首页",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
defer wsConn.Close()
|
||||
@@ -492,27 +448,27 @@ func (cp *commonPage) fm(c *gin.Context) {
|
||||
func (cp *commonPage) createFM(c *gin.Context) {
|
||||
IdString := c.Query("id")
|
||||
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); !authorized {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "无权访问",
|
||||
Msg: "用户未登录",
|
||||
Link: "/login",
|
||||
Btn: "去登录",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "无权访问",
|
||||
// Msg: "用户未登录",
|
||||
// Link: "/login",
|
||||
// Btn: "去登录",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
streamId, err := uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusInternalServerError,
|
||||
// Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// MessageID: "SystemError",
|
||||
// }),
|
||||
Msg: "生成会话ID失败",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusInternalServerError,
|
||||
// // Title: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
||||
// // MessageID: "SystemError",
|
||||
// // }),
|
||||
// Msg: "生成会话ID失败",
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -520,13 +476,13 @@ func (cp *commonPage) createFM(c *gin.Context) {
|
||||
|
||||
serverId, err := strconv.Atoi(IdString)
|
||||
if err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "请求参数有误:" + err.Error(),
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "请求参数有误:" + err.Error(),
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -534,13 +490,13 @@ func (cp *commonPage) createFM(c *gin.Context) {
|
||||
server := singleton.ServerList[uint64(serverId)]
|
||||
singleton.ServerLock.RUnlock()
|
||||
if server == nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "服务器不存在或处于离线状态",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "服务器不存在或处于离线状态",
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -551,17 +507,17 @@ func (cp *commonPage) createFM(c *gin.Context) {
|
||||
Type: model.TaskTypeFM,
|
||||
Data: string(fmData),
|
||||
}); err != nil {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusForbidden,
|
||||
Title: "请求失败",
|
||||
Msg: "Agent信令下发失败",
|
||||
Link: "/server",
|
||||
Btn: "返回重试",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
// Title: "请求失败",
|
||||
// Msg: "Agent信令下发失败",
|
||||
// Link: "/server",
|
||||
// Btn: "返回重试",
|
||||
// }, true)
|
||||
return
|
||||
}
|
||||
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/file", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
"SessionID": streamId,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
@@ -15,7 +16,6 @@ import (
|
||||
|
||||
docs "github.com/naiba/nezha/cmd/dashboard/docs"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/naiba/nezha/proto"
|
||||
"github.com/naiba/nezha/service/rpc"
|
||||
@@ -41,7 +41,7 @@ import (
|
||||
|
||||
// @externalDocs.description OpenAPI
|
||||
// @externalDocs.url https://swagger.io/resources/open-api/
|
||||
func ServeWeb(port uint) *http.Server {
|
||||
func ServeWeb() *http.Server {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.Default()
|
||||
docs.SwaggerInfo.BasePath = "/api/v1"
|
||||
@@ -50,23 +50,24 @@ func ServeWeb(port uint) *http.Server {
|
||||
pprof.Register(r)
|
||||
}
|
||||
r.Use(natGateway)
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
r.Use(mygin.RecordPath)
|
||||
if singleton.Conf.Debug {
|
||||
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
|
||||
}
|
||||
r.Use(recordPath)
|
||||
routers(r)
|
||||
page404 := func(c *gin.Context) {
|
||||
mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
Code: http.StatusNotFound,
|
||||
Title: "该页面不存在",
|
||||
Msg: "该页面内容可能已着陆火星",
|
||||
Link: "/",
|
||||
Btn: "返回首页",
|
||||
}, true)
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusNotFound,
|
||||
// Title: "该页面不存在",
|
||||
// Msg: "该页面内容可能已着陆火星",
|
||||
// Link: "/",
|
||||
// Btn: "返回首页",
|
||||
// }, true)
|
||||
}
|
||||
r.NoRoute(page404)
|
||||
r.NoMethod(page404)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", port),
|
||||
ReadHeaderTimeout: time.Second * 5,
|
||||
Handler: r,
|
||||
}
|
||||
@@ -90,9 +91,6 @@ func routers(r *gin.Engine) {
|
||||
// 通用页面
|
||||
cp := commonPage{r: r}
|
||||
cp.serve()
|
||||
// 游客页面
|
||||
gp := guestPage{r}
|
||||
gp.serve()
|
||||
// 会员页面
|
||||
mp := &memberPage{r}
|
||||
mp.serve()
|
||||
@@ -164,3 +162,11 @@ func natGateway(c *gin.Context) {
|
||||
rpc.NezhaHandlerSingleton.StartStream(streamId, time.Second*10)
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func recordPath(c *gin.Context) {
|
||||
url := c.Request.URL.String()
|
||||
for _, p := range c.Params {
|
||||
url = strings.Replace(url, p.Value, ":"+p.Key, 1)
|
||||
}
|
||||
c.Set("MatchedPath", url)
|
||||
}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
type guestPage struct {
|
||||
r *gin.Engine
|
||||
}
|
||||
|
||||
func (gp *guestPage) serve() {
|
||||
gr := gp.r.Group("")
|
||||
gr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
GuestOnly: true,
|
||||
IsPage: true,
|
||||
Msg: "您已登录",
|
||||
Btn: "返回首页",
|
||||
Redirect: "/",
|
||||
}))
|
||||
|
||||
gr.GET("/login", gp.login)
|
||||
}
|
||||
|
||||
func (gp *guestPage) login(c *gin.Context) {
|
||||
if singleton.Conf.Oauth2.OidcAutoLogin {
|
||||
c.Redirect(http.StatusFound, "/oauth2/login")
|
||||
return
|
||||
}
|
||||
LoginType := "GitHub"
|
||||
RegistrationLink := "https://github.com/join"
|
||||
if singleton.Conf.Oauth2.Type == model.ConfigTypeGitee {
|
||||
LoginType = "Gitee"
|
||||
RegistrationLink = "https://gitee.com/signup"
|
||||
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeGitlab {
|
||||
LoginType = "Gitlab"
|
||||
RegistrationLink = "https://gitlab.com/users/sign_up"
|
||||
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeJihulab {
|
||||
LoginType = "Jihulab"
|
||||
RegistrationLink = "https://jihulab.com/users/sign_up"
|
||||
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeGitea {
|
||||
LoginType = "Gitea"
|
||||
RegistrationLink = fmt.Sprintf("%s/user/sign_up", singleton.Conf.Oauth2.Endpoint)
|
||||
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeCloudflare {
|
||||
LoginType = "Cloudflare"
|
||||
RegistrationLink = "https://dash.cloudflare.com/sign-up/teams"
|
||||
} else if singleton.Conf.Oauth2.Type == model.ConfigTypeOidc {
|
||||
LoginType = singleton.Conf.Oauth2.OidcDisplayName
|
||||
RegistrationLink = singleton.Conf.Oauth2.OidcRegisterURL
|
||||
}
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/login", mygin.CommonEnvironment(c, gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
"LoginType": LoginType,
|
||||
"RegistrationLink": RegistrationLink,
|
||||
}))
|
||||
}
|
||||
@@ -7,12 +7,14 @@ import (
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func initParams() *jwt.GinJWTMiddleware {
|
||||
return &jwt.GinJWTMiddleware{
|
||||
Realm: "test zone",
|
||||
Key: []byte("secret key"),
|
||||
Realm: singleton.Conf.SiteName,
|
||||
Key: []byte(singleton.Conf.SecretKey),
|
||||
Timeout: time.Hour,
|
||||
MaxRefresh: time.Hour,
|
||||
IdentityKey: model.CtxKeyAuthorizedUser,
|
||||
@@ -41,7 +43,7 @@ func payloadFunc() func(data interface{}) jwt.MapClaims {
|
||||
return func(data interface{}) jwt.MapClaims {
|
||||
if v, ok := data.(*model.User); ok {
|
||||
return jwt.MapClaims{
|
||||
model.CtxKeyAuthorizedUser: v.Username,
|
||||
model.CtxKeyAuthorizedUser: v.ID,
|
||||
}
|
||||
}
|
||||
return jwt.MapClaims{}
|
||||
@@ -51,36 +53,48 @@ func payloadFunc() func(data interface{}) jwt.MapClaims {
|
||||
func identityHandler() func(c *gin.Context) interface{} {
|
||||
return func(c *gin.Context) interface{} {
|
||||
claims := jwt.ExtractClaims(c)
|
||||
return &model.User{
|
||||
Username: claims[model.CtxKeyAuthorizedUser].(string),
|
||||
userId := claims[model.CtxKeyAuthorizedUser].(uint64)
|
||||
var user model.User
|
||||
if err := singleton.DB.First(&user, userId).Error; err != nil {
|
||||
return nil
|
||||
}
|
||||
return &user
|
||||
}
|
||||
}
|
||||
|
||||
// login test godoc
|
||||
// @Summary ping example
|
||||
// @Schemes
|
||||
// @Description do ping
|
||||
// @Tags example
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {string} Helloworld
|
||||
// @Router /example/login [get]
|
||||
func authenticator() func(c *gin.Context) (interface{}, error) {
|
||||
return func(c *gin.Context) (interface{}, error) {
|
||||
var loginVals model.LoginRequest
|
||||
if err := c.ShouldBind(&loginVals); err != nil {
|
||||
return "", jwt.ErrMissingLoginValues
|
||||
}
|
||||
userID := loginVals.Username
|
||||
password := loginVals.Password
|
||||
|
||||
if (userID == "admin" && password == "admin") || (userID == "test" && password == "test") {
|
||||
return &model.User{
|
||||
Username: userID,
|
||||
}, nil
|
||||
var user model.User
|
||||
if err := singleton.DB.Select("id").Where("username = ?", loginVals.Username).First(&user).Error; err != nil {
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
}
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginVals.Password)); err != nil {
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
}
|
||||
|
||||
return &user, nil
|
||||
}
|
||||
}
|
||||
|
||||
func authorizator() func(data interface{}, c *gin.Context) bool {
|
||||
return func(data interface{}, c *gin.Context) bool {
|
||||
if v, ok := data.(*model.User); ok && v.Username == "admin" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
_, ok := data.(*model.User)
|
||||
return ok
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
"github.com/naiba/nezha/proto"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
@@ -28,13 +27,13 @@ type memberAPI struct {
|
||||
|
||||
func (ma *memberAPI) serve() {
|
||||
mr := ma.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
MemberOnly: true,
|
||||
IsPage: false,
|
||||
Msg: "访问此接口需要登录",
|
||||
Btn: "点此登录",
|
||||
Redirect: "/login",
|
||||
}))
|
||||
// mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
// MemberOnly: true,
|
||||
// IsPage: false,
|
||||
// Msg: "访问此接口需要登录",
|
||||
// Btn: "点此登录",
|
||||
// Redirect: "/login",
|
||||
// }))
|
||||
|
||||
mr.GET("/search-server", ma.searchServer)
|
||||
mr.GET("/search-tasks", ma.searchTask)
|
||||
@@ -997,30 +996,23 @@ func (ma *memberAPI) logout(c *gin.Context) {
|
||||
Code: http.StatusOK,
|
||||
})
|
||||
|
||||
if oidcLogoutUrl := singleton.Conf.Oauth2.OidcLogoutURL; oidcLogoutUrl != "" {
|
||||
// 重定向到 OIDC 退出登录地址。不知道为什么,这里的重定向不生效
|
||||
c.Redirect(http.StatusOK, oidcLogoutUrl)
|
||||
}
|
||||
// if oidcLogoutUrl := singleton.Conf.Oauth2.OidcLogoutURL; oidcLogoutUrl != "" {
|
||||
// // 重定向到 OIDC 退出登录地址。不知道为什么,这里的重定向不生效
|
||||
// c.Redirect(http.StatusOK, oidcLogoutUrl)
|
||||
// }
|
||||
}
|
||||
|
||||
type settingForm struct {
|
||||
Title string
|
||||
Admin string
|
||||
SiteName string
|
||||
Language string
|
||||
Theme string
|
||||
DashboardTheme string
|
||||
CustomCode string
|
||||
CustomCodeDashboard string
|
||||
CustomNameservers string
|
||||
ViewPassword string
|
||||
IgnoredIPNotification string
|
||||
IPChangeNotificationTag string // IP变更提醒的通知组
|
||||
GRPCHost string
|
||||
InstallHost string
|
||||
Cover uint8
|
||||
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
DisableSwitchTemplateInFrontend string
|
||||
EnableIPChangeNotification string
|
||||
EnablePlainIPInNotification string
|
||||
}
|
||||
|
||||
func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
@@ -1033,38 +1025,31 @@ func (ma *memberAPI) updateSetting(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, yes := model.Themes[sf.Theme]; !yes {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: fmt.Sprintf("前台主题不存在:%s", sf.Theme),
|
||||
})
|
||||
return
|
||||
}
|
||||
// if _, yes := model.Themes[sf.Theme]; !yes {
|
||||
// c.JSON(http.StatusOK, model.Response{
|
||||
// Code: http.StatusBadRequest,
|
||||
// Message: fmt.Sprintf("前台主题不存在:%s", sf.Theme),
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
if _, yes := model.DashboardThemes[sf.DashboardTheme]; !yes {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: fmt.Sprintf("后台主题不存在:%s", sf.DashboardTheme),
|
||||
})
|
||||
return
|
||||
}
|
||||
// if _, yes := model.DashboardThemes[sf.DashboardTheme]; !yes {
|
||||
// c.JSON(http.StatusOK, model.Response{
|
||||
// Code: http.StatusBadRequest,
|
||||
// Message: fmt.Sprintf("后台主题不存在:%s", sf.DashboardTheme),
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
singleton.Conf.Language = sf.Language
|
||||
singleton.Conf.EnableIPChangeNotification = sf.EnableIPChangeNotification == "on"
|
||||
singleton.Conf.EnablePlainIPInNotification = sf.EnablePlainIPInNotification == "on"
|
||||
singleton.Conf.DisableSwitchTemplateInFrontend = sf.DisableSwitchTemplateInFrontend == "on"
|
||||
singleton.Conf.Cover = sf.Cover
|
||||
// singleton.Conf.GRPCHost = sf.GRPCHost
|
||||
singleton.Conf.InstallHost = sf.InstallHost
|
||||
singleton.Conf.IgnoredIPNotification = sf.IgnoredIPNotification
|
||||
singleton.Conf.IPChangeNotificationTag = sf.IPChangeNotificationTag
|
||||
singleton.Conf.Site.Brand = sf.Title
|
||||
singleton.Conf.Site.Theme = sf.Theme
|
||||
singleton.Conf.Site.DashboardTheme = sf.DashboardTheme
|
||||
singleton.Conf.Site.CustomCode = sf.CustomCode
|
||||
singleton.Conf.Site.CustomCodeDashboard = sf.CustomCodeDashboard
|
||||
singleton.Conf.SiteName = sf.SiteName
|
||||
singleton.Conf.DNSServers = sf.CustomNameservers
|
||||
singleton.Conf.Site.ViewPassword = sf.ViewPassword
|
||||
singleton.Conf.Oauth2.Admin = sf.Admin
|
||||
// 保证NotificationTag不为空
|
||||
if singleton.Conf.IPChangeNotificationTag == "" {
|
||||
singleton.Conf.IPChangeNotificationTag = "default"
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/mygin"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
@@ -15,13 +14,13 @@ type memberPage struct {
|
||||
|
||||
func (mp *memberPage) serve() {
|
||||
mr := mp.r.Group("")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
MemberOnly: true,
|
||||
IsPage: true,
|
||||
// Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
// Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
Redirect: "/login",
|
||||
}))
|
||||
// mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
// MemberOnly: true,
|
||||
// IsPage: true,
|
||||
// // Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
|
||||
// // Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
|
||||
// Redirect: "/login",
|
||||
// }))
|
||||
mr.GET("/server", mp.server)
|
||||
mr.GET("/monitor", mp.monitor)
|
||||
mr.GET("/cron", mp.cron)
|
||||
@@ -35,35 +34,35 @@ func (mp *memberPage) serve() {
|
||||
func (mp *memberPage) api(c *gin.Context) {
|
||||
singleton.ApiLock.RLock()
|
||||
defer singleton.ApiLock.RUnlock()
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/api", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ApiManagement"}),
|
||||
"Tokens": singleton.ApiTokenList,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) server(c *gin.Context) {
|
||||
singleton.SortedServerLock.RLock()
|
||||
defer singleton.SortedServerLock.RUnlock()
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/server", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServersManagement"}),
|
||||
"Servers": singleton.SortedServerList,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) monitor(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/monitor", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesManagement"}),
|
||||
"Monitors": singleton.ServiceSentinelShared.Monitors(),
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) cron(c *gin.Context) {
|
||||
var crons []model.Cron
|
||||
singleton.DB.Find(&crons)
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/cron", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ScheduledTasks"}),
|
||||
"Crons": crons,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) notification(c *gin.Context) {
|
||||
@@ -71,37 +70,37 @@ func (mp *memberPage) notification(c *gin.Context) {
|
||||
singleton.DB.Find(&nf)
|
||||
var ar []model.AlertRule
|
||||
singleton.DB.Find(&ar)
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/notification", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Notification"}),
|
||||
"Notifications": nf,
|
||||
"AlertRules": ar,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) ddns(c *gin.Context) {
|
||||
var data []model.DDNSProfile
|
||||
singleton.DB.Find(&data)
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/ddns", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "DDNS"}),
|
||||
"DDNS": data,
|
||||
"ProviderMap": model.ProviderMap,
|
||||
"ProviderList": model.ProviderList,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) nat(c *gin.Context) {
|
||||
var data []model.NAT
|
||||
singleton.DB.Find(&data)
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/nat", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "NAT"}),
|
||||
"NAT": data,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) setting(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/setting", mygin.CommonEnvironment(c, gin.H{
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}),
|
||||
"Languages": model.Languages,
|
||||
"DashboardThemes": model.DashboardThemes,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,18 +4,20 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
_ "time/tzdata"
|
||||
|
||||
"github.com/ory/graceful"
|
||||
"github.com/soheilhy/cmux"
|
||||
flag "github.com/spf13/pflag"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/naiba/nezha/cmd/dashboard/controller"
|
||||
"github.com/naiba/nezha/cmd/dashboard/rpc"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/proto"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
"github.com/ory/graceful"
|
||||
flag "github.com/spf13/pflag"
|
||||
// gin-swagger middleware
|
||||
// swagger embed files
|
||||
)
|
||||
|
||||
type DashboardCliParam struct {
|
||||
@@ -43,6 +45,25 @@ func init() {
|
||||
}
|
||||
|
||||
func initSystem() {
|
||||
// 初始化管理员账户
|
||||
var usersCount int64
|
||||
if err := singleton.DB.Model(&model.User{}).Count(&usersCount).Error; err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if usersCount == 0 {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte("admin"), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
admin := model.User{
|
||||
Username: "admin",
|
||||
Password: string(hash),
|
||||
}
|
||||
if err := singleton.DB.Create(&admin).Error; err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 启动 singleton 包下的所有服务
|
||||
singleton.LoadSingleton()
|
||||
|
||||
@@ -63,19 +84,28 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", singleton.Conf.ListenPort))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
m := cmux.New(l)
|
||||
grpcL := m.Match(cmux.HTTP2HeaderField("content-type", "application/grpc"))
|
||||
httpL := m.Match(cmux.HTTP1Fast())
|
||||
|
||||
// TODO 使用 cmux 在同一端口服务 HTTP 和 gRPC
|
||||
singleton.CleanMonitorHistory()
|
||||
go rpc.ServeRPC(singleton.Conf.ListenPort)
|
||||
go rpc.ServeRPC(grpcL)
|
||||
serviceSentinelDispatchBus := make(chan model.Monitor) // 用于传递服务监控任务信息的channel
|
||||
go rpc.DispatchTask(serviceSentinelDispatchBus)
|
||||
go rpc.DispatchKeepalive()
|
||||
go singleton.AlertSentinelStart()
|
||||
singleton.NewServiceSentinel(serviceSentinelDispatchBus)
|
||||
srv := controller.ServeWeb(singleton.Conf.ListenPort)
|
||||
srv := controller.ServeWeb()
|
||||
|
||||
go dispatchReportInfoTask()
|
||||
if err := graceful.Graceful(func() error {
|
||||
return srv.ListenAndServe()
|
||||
return srv.Serve(httpL)
|
||||
}, func(c context.Context) error {
|
||||
log.Println("NEZHA>> Graceful::START")
|
||||
singleton.RecordTransferHourlyUsage()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
@@ -12,15 +11,11 @@ import (
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
)
|
||||
|
||||
func ServeRPC(port uint) {
|
||||
func ServeRPC(l net.Listener) {
|
||||
server := grpc.NewServer()
|
||||
rpcService.NezhaHandlerSingleton = rpcService.NewNezhaHandler()
|
||||
pb.RegisterNezhaServiceServer(server, rpcService.NezhaHandlerSingleton)
|
||||
listen, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.Serve(listen)
|
||||
server.Serve(l)
|
||||
}
|
||||
|
||||
func DispatchTask(serviceSentinelDispatchBus <-chan model.Monitor) {
|
||||
|
||||
Reference in New Issue
Block a user