mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-22 12:48:14 -05:00
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.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
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,
|
||||
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
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 (
|
||||
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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user