Skip to content

Commit

Permalink
Update.
Browse files Browse the repository at this point in the history
  • Loading branch information
xOS committed Feb 27, 2024
1 parent d338008 commit d2f492f
Show file tree
Hide file tree
Showing 33 changed files with 1,100 additions and 167 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 探针

> 本项目为原项目[哪吒探针](https://github.com/naiba/nezha)的精简修改自用版
> 本项目为原项目[哪吒探针](https://github.com/naiba/nezha)的修改自用版
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/xos/serverstatus/dashboard.yml?label=管理面板%20v0.1.41&logo=github&style=for-the-badge) ![Agent release](https://img.shields.io/github/v/release/xOS/ServerStatus?color=brightgreen&label=探针&style=for-the-badge&logo=github) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/xos/serverstatus/agent.yml?label=探针%20CI&logo=github&style=for-the-badge) ![shell](https://img.shields.io/badge/安装脚本-v0.1.13-brightgreen?style=for-the-badge&logo=linux)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/xos/serverstatus/dashboard.yml?label=管理面板%20v0.2.0&logo=github&style=for-the-badge) ![Agent release](https://img.shields.io/github/v/release/xOS/ServerStatus?color=brightgreen&label=探针&style=for-the-badge&logo=github) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/xos/serverstatus/agent.yml?label=探针%20CI&logo=github&style=for-the-badge) ![shell](https://img.shields.io/badge/安装脚本-v0.1.14-brightgreen?style=for-the-badge&logo=linux)


## 注意:
Expand Down
38 changes: 31 additions & 7 deletions cmd/dashboard/controller/api_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"strings"

"github.com/gin-gonic/gin"
"github.com/xos/serverstatus/model"
"github.com/xos/serverstatus/pkg/mygin"
"github.com/xos/serverstatus/service/singleton"
)
Expand All @@ -15,18 +16,31 @@ type apiV1 struct {

func (v *apiV1) serve() {
r := v.r.Group("")
// API
// 强制认证的 API
r.Use(mygin.Authorize(mygin.AuthorizeOption{
Member: true,
IsPage: false,
AllowAPI: true,
Msg: "访问此接口需要认证",
Btn: "点此登录",
Redirect: "/login",
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.GET("/:id", v.monitorHistoriesById)
}

Expand Down Expand Up @@ -83,5 +97,15 @@ func (v *apiV1) monitorHistoriesById(c *gin.Context) {
})
return
}

_, isMember := c.Get(model.CtxKeyAuthorizedUser)
_, isViewPasswordVerfied := c.Get(model.CtxKeyViewPasswordVerified)
authorized := isMember || isViewPasswordVerfied

if server.HideForGuest && !authorized {
c.AbortWithStatusJSON(403, gin.H{"code": 403, "message": "需要认证"})
return
}

c.JSON(200, singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": server.ID}))
}
40 changes: 10 additions & 30 deletions cmd/dashboard/controller/common_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ type commonPage struct {
func (cp *commonPage) serve() {
cr := cp.r.Group("")
cr.Use(mygin.Authorize(mygin.AuthorizeOption{}))
cr.GET("/terminal/:id", cp.terminal)
cr.Use(mygin.PreferredTheme)
cr.POST("/view-password", cp.issueViewPassword)
cr.Use(cp.checkViewPassword) // 前端查看密码鉴权
cr.GET("/terminal/:id", cp.terminal)
cr.Use(mygin.ValidateViewPassword(mygin.ValidateViewPasswordOption{
IsPage: true,
AbortWhenFail: true,
}))
cr.GET("/", cp.home)
cr.GET("/service", cp.service)
// TODO: 界面直接跳转使用该接口
Expand All @@ -63,6 +67,7 @@ type viewPasswordForm struct {
func (p *commonPage) issueViewPassword(c *gin.Context) {
var vpf viewPasswordForm
err := c.ShouldBind(&vpf)
log.Println("bingo", vpf)
var hash []byte
if err == nil && vpf.Password != singleton.Conf.Site.ViewPassword {
err = errors.New(singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "WrongAccessPassword"}))
Expand All @@ -85,31 +90,6 @@ func (p *commonPage) issueViewPassword(c *gin.Context) {
c.Redirect(http.StatusFound, c.Request.Referer())
}

func (p *commonPage) checkViewPassword(c *gin.Context) {
if singleton.Conf.Site.ViewPassword == "" {
c.Next()
return
}
if _, authorized := c.Get(model.CtxKeyAuthorizedUser); authorized {
c.Next()
return
}

// 验证查看密码
viewPassword, _ := c.Cookie(singleton.Conf.Site.CookieName + "-vp")
if err := bcrypt.CompareHashAndPassword([]byte(viewPassword), []byte(singleton.Conf.Site.ViewPassword)); err != nil {
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/viewpassword", mygin.CommonEnvironment(c, gin.H{
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "VerifyPassword"}),
"CustomCode": singleton.Conf.Site.CustomCode,
}))
c.Abort()
return
}

c.Set(model.CtxKeyViewPasswordVerified, true)
c.Next()
}

func (p *commonPage) service(c *gin.Context) {
res, _, _ := p.requestGroup.Do("servicePage", func() (interface{}, error) {
singleton.AlertsLock.RLock()
Expand All @@ -128,7 +108,7 @@ func (p *commonPage) service(c *gin.Context) {
stats, statsStore,
}, nil
})
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/service", mygin.CommonEnvironment(c, gin.H{
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/service"), mygin.CommonEnvironment(c, gin.H{
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesStatus"}),
"Services": res.([]interface{})[0],
"CycleTransferStats": res.([]interface{})[1],
Expand Down Expand Up @@ -234,7 +214,7 @@ func (cp *commonPage) network(c *gin.Context) {
Servers: servers,
})

c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/network", mygin.CommonEnvironment(c, gin.H{
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/network"), mygin.CommonEnvironment(c, gin.H{
"Servers": string(serversBytes),
"MonitorInfos": string(monitorInfos),
"CustomCode": singleton.Conf.Site.CustomCode,
Expand Down Expand Up @@ -284,7 +264,7 @@ func (cp *commonPage) home(c *gin.Context) {
}, true)
return
}
c.HTML(http.StatusOK, "theme-"+singleton.Conf.Site.Theme+"/home", mygin.CommonEnvironment(c, gin.H{
c.HTML(http.StatusOK, mygin.GetPreferredTheme(c, "/home"), mygin.CommonEnvironment(c, gin.H{
"Servers": string(stat),
"CycleTransferStats": statsStore,
"CustomCode": singleton.Conf.Site.CustomCode,
Expand Down
10 changes: 5 additions & 5 deletions cmd/dashboard/controller/guest_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ type guestPage struct {
func (gp *guestPage) serve() {
gr := gp.r.Group("")
gr.Use(mygin.Authorize(mygin.AuthorizeOption{
Guest: true,
IsPage: true,
Msg: "您已登录",
Btn: "返回首页",
Redirect: "/",
GuestOnly: true,
IsPage: true,
Msg: "您已登录",
Btn: "返回首页",
Redirect: "/",
}))

gr.GET("/login", gp.login)
Expand Down
24 changes: 15 additions & 9 deletions cmd/dashboard/controller/member_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ type memberAPI struct {
func (ma *memberAPI) serve() {
mr := ma.r.Group("")
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
Member: true,
IsPage: false,
Msg: "访问此接口需要登录",
Btn: "点此登录",
Redirect: "/login",
MemberOnly: true,
IsPage: false,
Msg: "访问此接口需要登录",
Btn: "点此登录",
Redirect: "/login",
}))

mr.GET("/search-server", ma.searchServer)
Expand Down Expand Up @@ -301,6 +301,8 @@ type serverForm struct {
Tag string
Note string
HideForGuest string
EnableDDNS string
DDNSDomain string
}

func (ma *memberAPI) addOrEditServer(c *gin.Context) {
Expand All @@ -316,6 +318,8 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
s.Tag = sf.Tag
s.Note = sf.Note
s.HideForGuest = sf.HideForGuest == "on"
s.EnableDDNS = sf.EnableDDNS == "on"
s.DDNSDomain = sf.DDNSDomain
if s.ID == 0 {
s.Secret, err = utils.GenerateRandomString(18)
if err == nil {
Expand Down Expand Up @@ -441,10 +445,12 @@ func (ma *memberAPI) addOrEditMonitor(c *gin.Context) {
err = singleton.DB.Save(&m).Error
}
}
if m.Cover == 0 {
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
} else {
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
if err == nil {
if m.Cover == 0 {
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
} else {
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, strings.Split(m.SkipServersRaw[1:len(m.SkipServersRaw)-1], ",")).Error
}
}
}
if err == nil {
Expand Down
11 changes: 5 additions & 6 deletions cmd/dashboard/controller/member_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ type memberPage struct {
func (mp *memberPage) serve() {
mr := mp.r.Group("")
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
Member: true,
IsPage: true,
Msg: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "YouAreNotAuthorized"}),
Btn: singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Login"}),
Redirect: "/login",
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)
Expand Down Expand Up @@ -81,7 +81,6 @@ func (mp *memberPage) setting(c *gin.Context) {
c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/setting", mygin.CommonEnvironment(c, gin.H{
"Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "Settings"}),
"Languages": model.Languages,
"Themes": model.Themes,
"DashboardThemes": model.DashboardThemes,
}))
}
1 change: 1 addition & 0 deletions model/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

const CtxKeyAuthorizedUser = "ckau"
const CtxKeyViewPasswordVerified = "ckvpv"
const CtxKeyPreferredTheme = "ckpt"
const CacheKeyOauth2State = "p:a:state"

type Common struct {
Expand Down
26 changes: 24 additions & 2 deletions model/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ var Languages = map[string]string{
}

var Themes = map[string]string{
"default": "Default",
"custom": "Custom(local)",
"default": "Default",
"custom": "Custom(local)",
}

var DashboardThemes = map[string]string{
Expand Down Expand Up @@ -106,6 +106,19 @@ type Config struct {
IgnoredIPNotificationServerIDs map[uint64]bool // [ServerID] -> bool(值为true代表当前ServerID在特定服务器列表内)
MaxTCPPingValue int32
AvgPingCount int

// 动态域名解析更新
DDNS struct {
Enable bool
Provider string
AccessID string
AccessSecret string
WebhookURL string
WebhookMethod string
WebhookRequestBody string
WebhookHeaders string
MaxRetries uint32
}
}

// Read 读取配置文件并应用
Expand Down Expand Up @@ -146,6 +159,15 @@ func (c *Config) Read(path string) error {
if c.AvgPingCount == 0 {
c.AvgPingCount = 2
}
if c.DDNS.Provider == "" {
c.DDNS.Provider = "webhook"
}
if c.DDNS.WebhookMethod == "" {
c.DDNS.WebhookMethod = "POST"
}
if c.DDNS.MaxRetries == 0 {
c.DDNS.MaxRetries = 3
}

c.updateIgnoredIPNotificationID()
return nil
Expand Down
5 changes: 4 additions & 1 deletion model/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Server struct {
Note string `json:"-"` // 管理员可见备注
DisplayIndex int // 展示排序,越大越靠前
HideForGuest bool // 对游客隐藏
EnableDDNS bool // 是否启用DDNS 未在配置文件中启用DDNS 或 DDNS检查时间为0时此项无效
DDNSDomain string // DDNS中的前缀 如基础域名为abc.oracle DDNSName为mjj 就会把mjj.abc.oracle解析服务器IP 为空则停用

Host *Host `gorm:"-"`
State *HostState `gorm:"-"`
Expand Down Expand Up @@ -50,5 +52,6 @@ func (s Server) Marshal() template.JS {
tag, _ := utils.Json.Marshal(s.Tag)
note, _ := utils.Json.Marshal(s.Note)
secret, _ := utils.Json.Marshal(s.Secret)
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest))) // #nosec
ddnsDomain, _ := utils.Json.Marshal(s.DDNSDomain)
return template.JS(fmt.Sprintf(`{"ID":%d,"Name":%s,"Secret":%s,"DisplayIndex":%d,"Tag":%s,"Note":%s,"HideForGuest": %s,"EnableDDNS": %s,"DDNSDomain": %s}`, s.ID, name, secret, s.DisplayIndex, tag, note, boolToString(s.HideForGuest), boolToString(s.EnableDDNS), ddnsDomain)) // #nosec
}
Loading

0 comments on commit d2f492f

Please sign in to comment.