feat: 服务器状态API
This commit is contained in:
parent
5fca0a52ef
commit
76928b71d9
@ -43,6 +43,47 @@ func (ma *memberAPI) serve() {
|
|||||||
mr.POST("/setting", ma.updateSetting)
|
mr.POST("/setting", ma.updateSetting)
|
||||||
mr.DELETE("/:model/:id", ma.delete)
|
mr.DELETE("/:model/:id", ma.delete)
|
||||||
mr.POST("/logout", ma.logout)
|
mr.POST("/logout", ma.logout)
|
||||||
|
|
||||||
|
// API
|
||||||
|
mr.GET("/server/list", ma.serverList)
|
||||||
|
mr.GET("/server/details", ma.serverDetails)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverList 获取服务器列表
|
||||||
|
func (ma *memberAPI) serverList(c *gin.Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverDetails 获取服务器信息
|
||||||
|
// header: Authorization: Token
|
||||||
|
// query: idList (服务器ID,逗号分隔,优先级高于tag查询)
|
||||||
|
// query: tag (服务器分组)
|
||||||
|
func (ma *memberAPI) serverDetails(c *gin.Context) {
|
||||||
|
token, _ := c.Cookie("Authorization")
|
||||||
|
var idList []uint64
|
||||||
|
idListStr := strings.Split(c.Query("id"), ",")
|
||||||
|
if c.Query("id") != "" {
|
||||||
|
idList = make([]uint64, len(idListStr))
|
||||||
|
for i, v := range idListStr {
|
||||||
|
id, _ := strconv.ParseUint(v, 10, 64)
|
||||||
|
idList[i] = id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tag := c.Query("tag")
|
||||||
|
serverAPI := &singleton.ServerAPI{
|
||||||
|
Token: token,
|
||||||
|
IDList: idList,
|
||||||
|
Tag: tag,
|
||||||
|
}
|
||||||
|
if tag != "" {
|
||||||
|
c.JSON(200, serverAPI.GetStatusByTag())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(idList) != 0 {
|
||||||
|
c.JSON(200, serverAPI.GetStatusByIDList())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(200, serverAPI.GetAllStatus())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ma *memberAPI) delete(c *gin.Context) {
|
func (ma *memberAPI) delete(c *gin.Context) {
|
||||||
@ -194,6 +235,23 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
|||||||
// 设置新的 Secret-ID 绑定关系
|
// 设置新的 Secret-ID 绑定关系
|
||||||
delete(singleton.SecretToID, singleton.ServerList[s.ID].Secret)
|
delete(singleton.SecretToID, singleton.ServerList[s.ID].Secret)
|
||||||
}
|
}
|
||||||
|
// 如果修改了Tag
|
||||||
|
if s.Tag != singleton.ServerList[s.ID].Tag {
|
||||||
|
index := 0
|
||||||
|
for index < len(singleton.ServerTagToIDList[s.Tag]) {
|
||||||
|
if singleton.ServerTagToIDList[s.Tag][index] == s.ID {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
// 删除旧 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.ServerList[s.ID] = &s
|
||||||
singleton.ServerLock.Unlock()
|
singleton.ServerLock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
@ -202,6 +260,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
|
|||||||
singleton.ServerLock.Lock()
|
singleton.ServerLock.Lock()
|
||||||
singleton.SecretToID[s.Secret] = s.ID
|
singleton.SecretToID[s.Secret] = s.ID
|
||||||
singleton.ServerList[s.ID] = &s
|
singleton.ServerList[s.ID] = &s
|
||||||
|
singleton.ServerTagToIDList[s.Tag] = append(singleton.ServerTagToIDList[s.Tag], s.ID)
|
||||||
singleton.ServerLock.Unlock()
|
singleton.ServerLock.Unlock()
|
||||||
}
|
}
|
||||||
singleton.ReSortServer()
|
singleton.ReSortServer()
|
||||||
|
10
model/api_token.go
Normal file
10
model/api_token.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ApiToken struct {
|
||||||
|
Common
|
||||||
|
UserId uint64 `json:"user_id"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
TokenExpired time.Time `json:"token_expired"`
|
||||||
|
}
|
@ -34,7 +34,6 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
|||||||
Link: opt.Redirect,
|
Link: opt.Redirect,
|
||||||
Btn: opt.Btn,
|
Btn: opt.Btn,
|
||||||
}
|
}
|
||||||
|
|
||||||
var isLogin bool
|
var isLogin bool
|
||||||
|
|
||||||
// 用户鉴权
|
// 用户鉴权
|
||||||
@ -50,6 +49,19 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API鉴权
|
||||||
|
apiToken := c.GetHeader("Authorization")
|
||||||
|
if apiToken != "" {
|
||||||
|
var t model.ApiToken
|
||||||
|
// TODO: 需要有缓存机制 减少数据库查询次数
|
||||||
|
if err := singleton.DB.Where("token = ?", apiToken).First(&t).Error; err == nil {
|
||||||
|
isLogin = t.TokenExpired.After(time.Now())
|
||||||
|
}
|
||||||
|
if isLogin {
|
||||||
|
c.Set(model.CtxKeyAuthorizedUser, &t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 已登录且只能游客访问
|
// 已登录且只能游客访问
|
||||||
if isLogin && opt.Guest {
|
if isLogin && opt.Guest {
|
||||||
ShowErrorPage(c, commonErr, opt.IsPage)
|
ShowErrorPage(c, commonErr, opt.IsPage)
|
||||||
|
83
service/singleton/api.go
Normal file
83
service/singleton/api.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package singleton
|
||||||
|
|
||||||
|
import "github.com/naiba/nezha/model"
|
||||||
|
|
||||||
|
type ServerAPI struct {
|
||||||
|
Token string // 传入Token 后期可能会需要用于scope判定
|
||||||
|
IDList []uint64
|
||||||
|
Tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommonResponse struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatusResponse struct {
|
||||||
|
Host *model.Host `json:"host"`
|
||||||
|
Status *model.HostState `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerStatusResponse struct {
|
||||||
|
CommonResponse
|
||||||
|
Result []*StatusResponse `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatusByIDList 获取传入IDList的服务器状态信息
|
||||||
|
func (s *ServerAPI) GetStatusByIDList() *ServerStatusResponse {
|
||||||
|
var res []*StatusResponse
|
||||||
|
|
||||||
|
ServerLock.RLock()
|
||||||
|
defer ServerLock.RUnlock()
|
||||||
|
|
||||||
|
for _, v := range s.IDList {
|
||||||
|
server := ServerList[v]
|
||||||
|
if server == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res = append(res, &StatusResponse{
|
||||||
|
Host: server.Host,
|
||||||
|
Status: server.State,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ServerStatusResponse{
|
||||||
|
CommonResponse: CommonResponse{
|
||||||
|
Code: 0,
|
||||||
|
Message: "success",
|
||||||
|
},
|
||||||
|
Result: res,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatusByTag 获取传入分组的所有服务器状态信息
|
||||||
|
func (s *ServerAPI) GetStatusByTag() *ServerStatusResponse {
|
||||||
|
s.IDList = ServerTagToIDList[s.Tag]
|
||||||
|
return s.GetStatusByIDList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllStatus 获取所有服务器状态信息
|
||||||
|
func (s *ServerAPI) GetAllStatus() *ServerStatusResponse {
|
||||||
|
ServerLock.RLock()
|
||||||
|
defer ServerLock.RUnlock()
|
||||||
|
var res []*StatusResponse
|
||||||
|
for _, v := range ServerList {
|
||||||
|
host := v.Host
|
||||||
|
state := v.State
|
||||||
|
if host == nil || state == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res = append(res, &StatusResponse{
|
||||||
|
Host: v.Host,
|
||||||
|
Status: v.State,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ServerStatusResponse{
|
||||||
|
CommonResponse: CommonResponse{
|
||||||
|
Code: 0,
|
||||||
|
Message: "success",
|
||||||
|
},
|
||||||
|
Result: res,
|
||||||
|
}
|
||||||
|
}
|
@ -8,9 +8,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ServerList map[uint64]*model.Server // [ServerID] -> model.Server
|
ServerList map[uint64]*model.Server // [ServerID] -> model.Server
|
||||||
SecretToID map[string]uint64 // [ServerSecret] -> ServerID
|
SecretToID map[string]uint64 // [ServerSecret] -> ServerID
|
||||||
ServerLock sync.RWMutex
|
ServerTagToIDList map[string][]uint64 // [ServerTag] -> ServerID
|
||||||
|
ServerLock sync.RWMutex
|
||||||
|
|
||||||
SortedServerList []*model.Server // 用于存储服务器列表的 slice,按照服务器 ID 排序
|
SortedServerList []*model.Server // 用于存储服务器列表的 slice,按照服务器 ID 排序
|
||||||
SortedServerLock sync.RWMutex
|
SortedServerLock sync.RWMutex
|
||||||
@ -20,6 +21,7 @@ var (
|
|||||||
func InitServer() {
|
func InitServer() {
|
||||||
ServerList = make(map[uint64]*model.Server)
|
ServerList = make(map[uint64]*model.Server)
|
||||||
SecretToID = make(map[string]uint64)
|
SecretToID = make(map[string]uint64)
|
||||||
|
ServerTagToIDList = make(map[string][]uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
//LoadServers 加载服务器列表并根据ID排序
|
//LoadServers 加载服务器列表并根据ID排序
|
||||||
@ -33,6 +35,7 @@ func LoadServers() {
|
|||||||
innerS.State = &model.HostState{}
|
innerS.State = &model.HostState{}
|
||||||
ServerList[innerS.ID] = &innerS
|
ServerList[innerS.ID] = &innerS
|
||||||
SecretToID[innerS.Secret] = innerS.ID
|
SecretToID[innerS.Secret] = innerS.ID
|
||||||
|
ServerTagToIDList[innerS.Tag] = append(ServerTagToIDList[innerS.Tag], innerS.ID)
|
||||||
}
|
}
|
||||||
ReSortServer()
|
ReSortServer()
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ func InitDBFromPath(path string) {
|
|||||||
}
|
}
|
||||||
err = DB.AutoMigrate(model.Server{}, model.User{},
|
err = DB.AutoMigrate(model.Server{}, model.User{},
|
||||||
model.Notification{}, model.AlertRule{}, model.Monitor{},
|
model.Notification{}, model.AlertRule{}, model.Monitor{},
|
||||||
model.MonitorHistory{}, model.Cron{}, model.Transfer{})
|
model.MonitorHistory{}, model.Cron{}, model.Transfer{}, model.ApiToken{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user