diff --git a/README.md b/README.md index d1b3145..b14e688 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # 哪吒面板 -系统状态监控报警;API(SSL证书变更、即将到期、到期)/TCP端口存活/PING 监控;计划任务可以定时在Agent上执行命令(备份、重启、What ever you want);极省资源,64M 服务器也能装 agent。 +![dashboard](https://img.shields.io/badge/管理面板-v0.3.2-brightgreen) ![agent](https://img.shields.io/badge/Agent-v0.3.1-brightgreen) + +系统状态监控报警、API(SSL证书变更、即将到期、到期)/TCP端口存活/PING 监控、计划任务(可以定时在Agent上执行命令,备份、重启、What ever you want)、极省资源,64M 服务器也能装 agent。 | 首页截图1 | 首页截图2 | | ---- | ---- | @@ -207,16 +209,3 @@ URL 里面也可放置占位符,请求时会进行简单的字符串替换。 - [哪吒面板,一个便携服务器状态监控面板搭建教程,不想拥有一个自己的探针吗?](https://haoduck.com/644.html) - [哪吒面板:小鸡们的最佳探针](https://www.zhujizixun.com/2843.html) *(已过时)* - [>>更多教程](https://www.google.com/search?q=%22%E5%93%AA%E5%90%92%E9%9D%A2%E6%9D%BF%22+%22%E6%95%99%E7%A8%8B%22) (Google) - -## 变更日志 - -只保留最后一次更新导致必须更新面板的说明。 - -- `agent 0.3.1` - - - 0.3.0 的更新的一个问题导致 Windows 用户的 Agent 无法自动更新,需要下载 0.3.1 或更新的 release 手动替换掉二进制文件。 - - 增加了对 mips(路由器) 的支持,在 release 处下载后手动配置 - -- `dashboard 0.3.0` `agent 0.3.0` **重大更新** - - 增加了定时任务功能,可以定时在 Agent 上执行脚本(应用于定期备份、重启服务等计划运维场景) diff --git a/cmd/dashboard/controller/common_page.go b/cmd/dashboard/controller/common_page.go index 751834e..c6396c9 100644 --- a/cmd/dashboard/controller/common_page.go +++ b/cmd/dashboard/controller/common_page.go @@ -1,6 +1,7 @@ package controller import ( + "log" "net/http" "time" @@ -36,51 +37,65 @@ type ServiceItem struct { } func (p *commonPage) service(c *gin.Context) { - var ms []model.Monitor - dao.DB.Find(&ms) - year, month, day := time.Now().Date() - today := time.Date(year, month, day, 0, 0, 0, 0, time.Local) - var mhs []model.MonitorHistory - dao.DB.Where("created_at >= ?", today.AddDate(0, 0, -29)).Find(&mhs) + var msm map[uint64]*ServiceItem - msm := make(map[uint64]*ServiceItem) - for i := 0; i < len(ms); i++ { - msm[ms[i].ID] = &ServiceItem{ - Monitor: ms[i], - Delay: &[30]float32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - Up: &[30]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - Down: &[30]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + var cached bool + if _, has := c.Get(model.CtxKeyAuthorizedUser); !has { + data, has := dao.Cache.Get(model.CacheKeyServicePage) + if has { + log.Println("use cache") + msm = data.(map[uint64]*ServiceItem) + cached = true } } - // 整合数据 - todayStatus := make(map[uint64][]bool) - for i := 0; i < len(mhs); i++ { - dayIndex := 29 - if mhs[i].CreatedAt.Before(today) { - dayIndex = 28 - (int(today.Sub(mhs[i].CreatedAt).Hours()) / 24) - } else { - todayStatus[mhs[i].MonitorID] = append(todayStatus[mhs[i].MonitorID], mhs[i].Successful) - } - if mhs[i].Successful { - msm[mhs[i].MonitorID].TotalUp++ - msm[mhs[i].MonitorID].Delay[dayIndex] = (msm[mhs[i].MonitorID].Delay[dayIndex]*float32(msm[mhs[i].MonitorID].Up[dayIndex]) + mhs[i].Delay) / float32(msm[mhs[i].MonitorID].Up[dayIndex]+1) - msm[mhs[i].MonitorID].Up[dayIndex]++ - } else { - msm[mhs[i].MonitorID].TotalDown++ - msm[mhs[i].MonitorID].Down[dayIndex]++ - } - } + if !cached { + msm = make(map[uint64]*ServiceItem) + var ms []model.Monitor + dao.DB.Find(&ms) + year, month, day := time.Now().Date() + today := time.Date(year, month, day, 0, 0, 0, 0, time.Local) + var mhs []model.MonitorHistory + dao.DB.Where("created_at >= ?", today.AddDate(0, 0, -29)).Find(&mhs) - // 当日最后 20 个采样作为当前状态 - for _, m := range msm { - for i := len(todayStatus[m.Monitor.ID]) - 1; i >= 0 && i >= (len(todayStatus[m.Monitor.ID])-1-20); i-- { - if todayStatus[m.Monitor.ID][i] { - m.CurrentUp++ - } else { - m.CurrentDown++ + for i := 0; i < len(ms); i++ { + msm[ms[i].ID] = &ServiceItem{ + Monitor: ms[i], + Delay: &[30]float32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Up: &[30]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Down: &[30]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, } } + // 整合数据 + todayStatus := make(map[uint64][]bool) + for i := 0; i < len(mhs); i++ { + dayIndex := 29 + if mhs[i].CreatedAt.Before(today) { + dayIndex = 28 - (int(today.Sub(mhs[i].CreatedAt).Hours()) / 24) + } else { + todayStatus[mhs[i].MonitorID] = append(todayStatus[mhs[i].MonitorID], mhs[i].Successful) + } + if mhs[i].Successful { + msm[mhs[i].MonitorID].TotalUp++ + msm[mhs[i].MonitorID].Delay[dayIndex] = (msm[mhs[i].MonitorID].Delay[dayIndex]*float32(msm[mhs[i].MonitorID].Up[dayIndex]) + mhs[i].Delay) / float32(msm[mhs[i].MonitorID].Up[dayIndex]+1) + msm[mhs[i].MonitorID].Up[dayIndex]++ + } else { + msm[mhs[i].MonitorID].TotalDown++ + msm[mhs[i].MonitorID].Down[dayIndex]++ + } + } + // 当日最后 20 个采样作为当前状态 + for _, m := range msm { + for i := len(todayStatus[m.Monitor.ID]) - 1; i >= 0 && i >= (len(todayStatus[m.Monitor.ID])-1-20); i-- { + if todayStatus[m.Monitor.ID][i] { + m.CurrentUp++ + } else { + m.CurrentDown++ + } + } + } + // 未登录人员缓存十分钟 + dao.Cache.Set(model.CacheKeyServicePage, msm, time.Minute*10) } c.HTML(http.StatusOK, "theme-"+dao.Conf.Site.Theme+"/service", mygin.CommonEnvironment(c, gin.H{ diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go index 11d934d..cfe334d 100644 --- a/cmd/dashboard/controller/member_api.go +++ b/cmd/dashboard/controller/member_api.go @@ -108,6 +108,7 @@ type serverForm struct { DisplayIndex int Secret string Tag string + Note string } func (ma *memberAPI) addOrEditServer(c *gin.Context) { @@ -122,6 +123,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) { s.DisplayIndex = sf.DisplayIndex s.ID = sf.ID s.Tag = sf.Tag + s.Note = sf.Note if sf.ID == 0 { s.Secret = utils.MD5(fmt.Sprintf("%s%s%d", time.Now(), sf.Name, admin.ID)) s.Secret = s.Secret[:10] diff --git a/cmd/dashboard/controller/oauth2.go b/cmd/dashboard/controller/oauth2.go index a97ec6f..e342920 100644 --- a/cmd/dashboard/controller/oauth2.go +++ b/cmd/dashboard/controller/oauth2.go @@ -28,14 +28,14 @@ func (oa *oauth2controller) serve() { func (oa *oauth2controller) login(c *gin.Context) { state := utils.RandStringBytesMaskImprSrcUnsafe(6) - dao.Cache.Set(fmt.Sprintf("%s%s", model.CtxKeyOauth2State, c.ClientIP()), state, 0) + dao.Cache.Set(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, c.ClientIP()), state, 0) url := oa.oauth2Config.AuthCodeURL(state, oauth2.AccessTypeOnline) c.Redirect(http.StatusFound, url) } func (oa *oauth2controller) callback(c *gin.Context) { // 验证登录跳转时的 State - state, ok := dao.Cache.Get(fmt.Sprintf("%s%s", model.CtxKeyOauth2State, c.ClientIP())) + state, ok := dao.Cache.Get(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, c.ClientIP())) if !ok || state.(string) != c.Query("state") { mygin.ShowErrorPage(c, mygin.ErrInfo{ Code: http.StatusBadRequest, diff --git a/model/common.go b/model/common.go index e2cb6fb..ef4736b 100644 --- a/model/common.go +++ b/model/common.go @@ -4,7 +4,8 @@ import "time" const CtxKeyAuthorizedUser = "ckau" -const CtxKeyOauth2State = "cko2s" +const CacheKeyOauth2State = "p:a:state" +const CacheKeyServicePage = "p:c:service" type Common struct { ID uint64 `gorm:"primary_key"` diff --git a/model/server.go b/model/server.go index e7d3de1..8d65296 100644 --- a/model/server.go +++ b/model/server.go @@ -11,8 +11,9 @@ import ( type Server struct { Common Name string - Tag string + Tag string // 分组名 Secret string `json:"-"` + Note string `json:"-"` // 管理员可见备注 DisplayIndex int // 展示权重,越大越靠前 Host *Host `gorm:"-"` @@ -24,5 +25,5 @@ type Server struct { } func (s Server) Marshal() template.JS { - return template.JS(fmt.Sprintf(`{"ID":%d,"Name":"%s","Secret":"%s"}`, s.ID, s.Name, s.Secret)) + return template.JS(fmt.Sprintf(`{"ID":%d,"Name":"%s","Secret":"%s","DisplayIndex":%d,"Tag":"%s","Note":"%s"}`, s.ID, s.Name, s.Secret, s.DisplayIndex, s.Tag, s.Note)) } diff --git a/resource/template/component/server.html b/resource/template/component/server.html index 9a73595..e333796 100644 --- a/resource/template/component/server.html +++ b/resource/template/component/server.html @@ -9,8 +9,8 @@