feat: 服务器状态API

This commit is contained in:
Akkia 2022-05-17 11:21:27 +08:00
parent 5fca0a52ef
commit 76928b71d9
No known key found for this signature in database
GPG Key ID: DABE9A4AB2DD7EF3
6 changed files with 172 additions and 5 deletions

View File

@ -43,6 +43,47 @@ func (ma *memberAPI) serve() {
mr.POST("/setting", ma.updateSetting)
mr.DELETE("/:model/:id", ma.delete)
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) {
@ -194,6 +235,23 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
// 设置新的 Secret-ID 绑定关系
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.ServerLock.Unlock()
} else {
@ -202,6 +260,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
singleton.ServerLock.Lock()
singleton.SecretToID[s.Secret] = s.ID
singleton.ServerList[s.ID] = &s
singleton.ServerTagToIDList[s.Tag] = append(singleton.ServerTagToIDList[s.Tag], s.ID)
singleton.ServerLock.Unlock()
}
singleton.ReSortServer()

10
model/api_token.go Normal file
View 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"`
}

View File

@ -34,7 +34,6 @@ func Authorize(opt AuthorizeOption) func(*gin.Context) {
Link: opt.Redirect,
Btn: opt.Btn,
}
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 {
ShowErrorPage(c, commonErr, opt.IsPage)

83
service/singleton/api.go Normal file
View 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,
}
}

View File

@ -8,9 +8,10 @@ import (
)
var (
ServerList map[uint64]*model.Server // [ServerID] -> model.Server
SecretToID map[string]uint64 // [ServerSecret] -> ServerID
ServerLock sync.RWMutex
ServerList map[uint64]*model.Server // [ServerID] -> model.Server
SecretToID map[string]uint64 // [ServerSecret] -> ServerID
ServerTagToIDList map[string][]uint64 // [ServerTag] -> ServerID
ServerLock sync.RWMutex
SortedServerList []*model.Server // 用于存储服务器列表的 slice按照服务器 ID 排序
SortedServerLock sync.RWMutex
@ -20,6 +21,7 @@ var (
func InitServer() {
ServerList = make(map[uint64]*model.Server)
SecretToID = make(map[string]uint64)
ServerTagToIDList = make(map[string][]uint64)
}
//LoadServers 加载服务器列表并根据ID排序
@ -33,6 +35,7 @@ func LoadServers() {
innerS.State = &model.HostState{}
ServerList[innerS.ID] = &innerS
SecretToID[innerS.Secret] = innerS.ID
ServerTagToIDList[innerS.Tag] = append(ServerTagToIDList[innerS.Tag], innerS.ID)
}
ReSortServer()
}

View File

@ -63,7 +63,7 @@ func InitDBFromPath(path string) {
}
err = DB.AutoMigrate(model.Server{}, model.User{},
model.Notification{}, model.AlertRule{}, model.Monitor{},
model.MonitorHistory{}, model.Cron{}, model.Transfer{})
model.MonitorHistory{}, model.Cron{}, model.Transfer{}, model.ApiToken{})
if err != nil {
panic(err)
}