mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-02-04 04:30:05 +00:00
refactor: rename monitor -> service
This commit is contained in:
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/jinzhu/copier"
|
||||
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/pkg/utils"
|
||||
@@ -24,7 +23,6 @@ type commonPage struct {
|
||||
|
||||
func (cp *commonPage) serve() {
|
||||
cr := cp.r.Group("")
|
||||
cr.GET("/service", cp.service)
|
||||
// TODO: 界面直接跳转使用该接口
|
||||
cr.GET("/network/:id", cp.network)
|
||||
cr.GET("/network", cp.network)
|
||||
@@ -32,34 +30,9 @@ func (cp *commonPage) serve() {
|
||||
cr.GET("/file/:id", cp.fm)
|
||||
}
|
||||
|
||||
func (p *commonPage) service(c *gin.Context) {
|
||||
res, _, _ := requestGroup.Do("servicePage", func() (interface{}, error) {
|
||||
singleton.AlertsLock.RLock()
|
||||
defer singleton.AlertsLock.RUnlock()
|
||||
var stats map[uint64]model.ServiceItemResponse
|
||||
var statsStore map[uint64]model.CycleTransferStats
|
||||
copier.Copy(&stats, singleton.ServiceSentinelShared.LoadStats())
|
||||
copier.Copy(&statsStore, singleton.AlertsCycleTransferStatsStore)
|
||||
for k, service := range stats {
|
||||
if !service.Monitor.EnableShowInService {
|
||||
delete(stats, k)
|
||||
}
|
||||
}
|
||||
return []interface {
|
||||
}{
|
||||
stats, statsStore,
|
||||
}, nil
|
||||
})
|
||||
c.HTML(http.StatusOK, "", gin.H{
|
||||
// "Title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ServicesStatus"}),
|
||||
"Services": res.([]interface{})[0],
|
||||
"CycleTransferStats": res.([]interface{})[1],
|
||||
})
|
||||
}
|
||||
|
||||
func (cp *commonPage) network(c *gin.Context) {
|
||||
var (
|
||||
monitorHistory *model.MonitorHistory
|
||||
monitorHistory *model.ServiceHistory
|
||||
servers []model.Server
|
||||
serverIdsWithMonitor []uint64
|
||||
monitorInfos = []byte("{}")
|
||||
@@ -68,7 +41,7 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
if len(singleton.SortedServerList) > 0 {
|
||||
id = singleton.SortedServerList[0].ID
|
||||
}
|
||||
if err := singleton.DB.Model(&model.MonitorHistory{}).Select("monitor_id, server_id").
|
||||
if err := singleton.DB.Model(&model.ServiceHistory{}).Select("monitor_id, server_id").
|
||||
Where("monitor_id != 0 and server_id != 0").Limit(1).First(&monitorHistory).Error; err != nil {
|
||||
// mygin.ShowErrorPage(c, mygin.ErrInfo{
|
||||
// Code: http.StatusForbidden,
|
||||
@@ -114,12 +87,10 @@ func (cp *commonPage) network(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
}
|
||||
monitorHistories := singleton.MonitorAPI.GetMonitorHistories(map[string]any{"server_id": id})
|
||||
monitorInfos, _ = utils.Json.Marshal(monitorHistories)
|
||||
_, isMember := c.Get(model.CtxKeyAuthorizedUser)
|
||||
var isViewPasswordVerfied bool
|
||||
|
||||
if err := singleton.DB.Model(&model.MonitorHistory{}).
|
||||
if err := singleton.DB.Model(&model.ServiceHistory{}).
|
||||
Select("distinct(server_id)").
|
||||
Where("server_id != 0").
|
||||
Find(&serverIdsWithMonitor).
|
||||
|
||||
@@ -63,10 +63,10 @@ func routers(r *gin.Engine) {
|
||||
auth.POST("/user", commonHandler(createUser))
|
||||
auth.POST("/batch-delete/user", commonHandler(batchDeleteUser))
|
||||
|
||||
auth.GET("/monitor", commonHandler(listMonitor))
|
||||
auth.POST("/monitor", commonHandler(createMonitor))
|
||||
auth.PATCH("/monitor/:id", commonHandler(updateMonitor))
|
||||
auth.POST("/batch-delete/monitor", commonHandler(batchDeleteMonitor))
|
||||
auth.GET("/service", commonHandler(listService))
|
||||
auth.POST("/service", commonHandler(createService))
|
||||
auth.PATCH("/service/:id", commonHandler(updateService))
|
||||
auth.POST("/batch-delete/service", commonHandler(batchDeleteService))
|
||||
|
||||
auth.POST("/server-group", commonHandler(createServerGroup))
|
||||
auth.PATCH("/server-group/:id", commonHandler(updateServerGroup))
|
||||
|
||||
@@ -43,121 +43,6 @@ func (ma *memberAPI) serve() {
|
||||
mr.POST("/setting", ma.updateSetting)
|
||||
mr.DELETE("/:model/:id", ma.delete)
|
||||
mr.POST("/logout", ma.logout)
|
||||
mr.GET("/token", ma.getToken)
|
||||
mr.POST("/token", ma.issueNewToken)
|
||||
mr.DELETE("/token/:token", ma.deleteToken)
|
||||
}
|
||||
|
||||
type apiResult struct {
|
||||
Token string `json:"token"`
|
||||
Note string `json:"note"`
|
||||
}
|
||||
|
||||
// getToken 获取 Token
|
||||
func (ma *memberAPI) getToken(c *gin.Context) {
|
||||
u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
|
||||
singleton.ApiLock.RLock()
|
||||
defer singleton.ApiLock.RUnlock()
|
||||
|
||||
tokenList := singleton.UserIDToApiTokenList[u.ID]
|
||||
res := make([]*apiResult, len(tokenList))
|
||||
for i, token := range tokenList {
|
||||
res[i] = &apiResult{
|
||||
Token: token,
|
||||
Note: singleton.ApiTokenList[token].Note,
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"result": res,
|
||||
})
|
||||
}
|
||||
|
||||
type TokenForm struct {
|
||||
Note string
|
||||
}
|
||||
|
||||
// issueNewToken 生成新的 token
|
||||
func (ma *memberAPI) issueNewToken(c *gin.Context) {
|
||||
u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
|
||||
tf := &TokenForm{}
|
||||
err := c.ShouldBindJSON(tf)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: fmt.Sprintf("请求错误:%s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
secureToken, err := utils.GenerateRandomString(32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: fmt.Sprintf("请求错误:%s", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
token := &model.ApiToken{
|
||||
UserID: u.ID,
|
||||
Token: secureToken,
|
||||
Note: tf.Note,
|
||||
}
|
||||
singleton.DB.Create(token)
|
||||
|
||||
singleton.ApiLock.Lock()
|
||||
singleton.ApiTokenList[token.Token] = token
|
||||
singleton.UserIDToApiTokenList[u.ID] = append(singleton.UserIDToApiTokenList[u.ID], token.Token)
|
||||
singleton.ApiLock.Unlock()
|
||||
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusOK,
|
||||
Message: "success",
|
||||
Result: map[string]string{
|
||||
"token": token.Token,
|
||||
"note": token.Note,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// deleteToken 删除 token
|
||||
func (ma *memberAPI) deleteToken(c *gin.Context) {
|
||||
token := c.Param("token")
|
||||
if token == "" {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: "token 不能为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
singleton.ApiLock.Lock()
|
||||
defer singleton.ApiLock.Unlock()
|
||||
if _, ok := singleton.ApiTokenList[token]; !ok {
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: "token 不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
// 在数据库中删除该Token
|
||||
singleton.DB.Unscoped().Delete(&model.ApiToken{}, "token = ?", token)
|
||||
|
||||
// 在UserIDToApiTokenList中删除该Token
|
||||
for i, t := range singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID] {
|
||||
if t == token {
|
||||
singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID] = append(singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID][:i], singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID][i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID]) == 0 {
|
||||
delete(singleton.UserIDToApiTokenList, singleton.ApiTokenList[token].UserID)
|
||||
}
|
||||
// 在ApiTokenList中删除该Token
|
||||
delete(singleton.ApiTokenList, token)
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
Code: http.StatusOK,
|
||||
Message: "success",
|
||||
})
|
||||
}
|
||||
|
||||
func (ma *memberAPI) delete(c *gin.Context) {
|
||||
|
||||
@@ -26,16 +26,6 @@ func (mp *memberPage) serve() {
|
||||
mr.GET("/ddns", mp.ddns)
|
||||
mr.GET("/nat", mp.nat)
|
||||
mr.GET("/setting", mp.setting)
|
||||
mr.GET("/api", mp.api)
|
||||
}
|
||||
|
||||
func (mp *memberPage) api(c *gin.Context) {
|
||||
singleton.ApiLock.RLock()
|
||||
defer singleton.ApiLock.RUnlock()
|
||||
c.HTML(http.StatusOK, "dashboard-", gin.H{
|
||||
// "title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ApiManagement"}),
|
||||
"Tokens": singleton.ApiTokenList,
|
||||
})
|
||||
}
|
||||
|
||||
func (mp *memberPage) cron(c *gin.Context) {
|
||||
|
||||
@@ -6,42 +6,67 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/naiba/nezha/model"
|
||||
"github.com/naiba/nezha/service/singleton"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// List monitor
|
||||
// @Summary List monitor
|
||||
// List service
|
||||
// @Summary List service
|
||||
// @Security BearerAuth
|
||||
// @Schemes
|
||||
// @Description List monitor
|
||||
// @Description List service
|
||||
// @Tags auth required
|
||||
// @Produce json
|
||||
// @Success 200 {object} model.CommonResponse[[]model.Monitor]
|
||||
// @Router /monitor [get]
|
||||
func listMonitor(c *gin.Context) ([]*model.Monitor, error) {
|
||||
return singleton.ServiceSentinelShared.Monitors(), nil
|
||||
// @Success 200 {object} model.CommonResponse[model.ServiceResponse]
|
||||
// @Router /service [get]
|
||||
func listService(c *gin.Context) (*model.ServiceResponse, error) {
|
||||
res, err, _ := requestGroup.Do("list-service", func() (interface{}, error) {
|
||||
singleton.AlertsLock.RLock()
|
||||
defer singleton.AlertsLock.RUnlock()
|
||||
var stats map[uint64]model.ServiceResponseItem
|
||||
var statsStore map[uint64]model.CycleTransferStats
|
||||
copier.Copy(&stats, singleton.ServiceSentinelShared.LoadStats())
|
||||
copier.Copy(&statsStore, singleton.AlertsCycleTransferStatsStore)
|
||||
for k, service := range stats {
|
||||
if !service.Service.EnableShowInService {
|
||||
delete(stats, k)
|
||||
}
|
||||
}
|
||||
return []interface {
|
||||
}{
|
||||
stats, statsStore,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.ServiceResponse{
|
||||
Services: res.([]interface{})[0].(map[uint64]model.ServiceResponseItem),
|
||||
CycleTransferStats: res.([]interface{})[1].(map[uint64]model.CycleTransferStats),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create monitor
|
||||
// @Summary Create monitor
|
||||
// Create service
|
||||
// @Summary Create service
|
||||
// @Security BearerAuth
|
||||
// @Schemes
|
||||
// @Description Create monitor
|
||||
// @Description Create service
|
||||
// @Tags auth required
|
||||
// @Accept json
|
||||
// @param request body model.MonitorForm true "Monitor Request"
|
||||
// @param request body model.ServiceForm true "Service Request"
|
||||
// @Produce json
|
||||
// @Success 200 {object} model.CommonResponse[uint64]
|
||||
// @Router /monitor [post]
|
||||
func createMonitor(c *gin.Context) (uint64, error) {
|
||||
var mf model.MonitorForm
|
||||
// @Router /service [post]
|
||||
func createService(c *gin.Context) (uint64, error) {
|
||||
var mf model.ServiceForm
|
||||
if err := c.ShouldBindJSON(&mf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var m model.Monitor
|
||||
var m model.Service
|
||||
m.Name = mf.Name
|
||||
m.Target = strings.TrimSpace(mf.Target)
|
||||
m.Type = mf.Type
|
||||
@@ -69,42 +94,42 @@ func createMonitor(c *gin.Context) (uint64, error) {
|
||||
|
||||
var err error
|
||||
if m.Cover == 0 {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, skipServers).Error
|
||||
err = singleton.DB.Unscoped().Delete(&model.ServiceHistory{}, "service_id = ? and server_id in (?)", m.ID, skipServers).Error
|
||||
} else {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, skipServers).Error
|
||||
err = singleton.DB.Unscoped().Delete(&model.ServiceHistory{}, "service_id = ? and server_id not in (?)", m.ID, skipServers).Error
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return m.ID, singleton.ServiceSentinelShared.OnMonitorUpdate(m)
|
||||
return m.ID, singleton.ServiceSentinelShared.OnServiceUpdate(m)
|
||||
}
|
||||
|
||||
// Update monitor
|
||||
// @Summary Update monitor
|
||||
// Update service
|
||||
// @Summary Update service
|
||||
// @Security BearerAuth
|
||||
// @Schemes
|
||||
// @Description Update monitor
|
||||
// @Description Update service
|
||||
// @Tags auth required
|
||||
// @Accept json
|
||||
// @param id path uint true "Monitor ID"
|
||||
// @param request body model.MonitorForm true "Monitor Request"
|
||||
// @param id path uint true "Service ID"
|
||||
// @param request body model.ServiceForm true "Service Request"
|
||||
// @Produce json
|
||||
// @Success 200 {object} model.CommonResponse[any]
|
||||
// @Router /monitor/{id} [patch]
|
||||
func updateMonitor(c *gin.Context) (any, error) {
|
||||
// @Router /service/{id} [patch]
|
||||
func updateService(c *gin.Context) (any, error) {
|
||||
strID := c.Param("id")
|
||||
id, err := strconv.ParseUint(strID, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mf model.MonitorForm
|
||||
var mf model.ServiceForm
|
||||
if err := c.ShouldBindJSON(&mf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var m model.Monitor
|
||||
var m model.Service
|
||||
if err := singleton.DB.First(&m, id).Error; err != nil {
|
||||
return nil, fmt.Errorf("monitor id %d does not exist", id)
|
||||
return nil, fmt.Errorf("service id %d does not exist", id)
|
||||
}
|
||||
m.Name = mf.Name
|
||||
m.Target = strings.TrimSpace(mf.Target)
|
||||
@@ -132,42 +157,42 @@ func updateMonitor(c *gin.Context) (any, error) {
|
||||
}
|
||||
|
||||
if m.Cover == 0 {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id in (?)", m.ID, skipServers).Error
|
||||
err = singleton.DB.Unscoped().Delete(&model.ServiceHistory{}, "service_id = ? and server_id in (?)", m.ID, skipServers).Error
|
||||
} else {
|
||||
err = singleton.DB.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id = ? and server_id not in (?)", m.ID, skipServers).Error
|
||||
err = singleton.DB.Unscoped().Delete(&model.ServiceHistory{}, "service_id = ? and server_id not in (?)", m.ID, skipServers).Error
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, singleton.ServiceSentinelShared.OnMonitorUpdate(m)
|
||||
return nil, singleton.ServiceSentinelShared.OnServiceUpdate(m)
|
||||
}
|
||||
|
||||
// Batch delete monitor
|
||||
// @Summary Batch delete monitor
|
||||
// Batch delete service
|
||||
// @Summary Batch delete service
|
||||
// @Security BearerAuth
|
||||
// @Schemes
|
||||
// @Description Batch delete monitor
|
||||
// @Description Batch delete service
|
||||
// @Tags auth required
|
||||
// @Accept json
|
||||
// @param request body []uint true "id list"
|
||||
// @Produce json
|
||||
// @Success 200 {object} model.CommonResponse[any]
|
||||
// @Router /batch-delete/monitor [post]
|
||||
func batchDeleteMonitor(c *gin.Context) (any, error) {
|
||||
// @Router /batch-delete/service [post]
|
||||
func batchDeleteService(c *gin.Context) (any, error) {
|
||||
var ids []uint64
|
||||
if err := c.ShouldBindJSON(&ids); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err := singleton.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Unscoped().Delete(&model.Monitor{}, "id in (?)", ids).Error; err != nil {
|
||||
if err := tx.Unscoped().Delete(&model.Service{}, "id in (?)", ids).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Unscoped().Delete(&model.MonitorHistory{}, "monitor_id in (?)", ids).Error
|
||||
return tx.Unscoped().Delete(&model.ServiceHistory{}, "service_id in (?)", ids).Error
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
singleton.ServiceSentinelShared.OnMonitorDelete(ids)
|
||||
singleton.ServiceSentinelShared.OnServiceDelete(ids)
|
||||
return nil, nil
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func initSystem() {
|
||||
singleton.LoadSingleton()
|
||||
|
||||
// 每天的3:30 对 监控记录 和 流量记录 进行清理
|
||||
if _, err := singleton.Cron.AddFunc("0 30 3 * * *", singleton.CleanMonitorHistory); err != nil {
|
||||
if _, err := singleton.Cron.AddFunc("0 30 3 * * *", singleton.CleanServiceHistory); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -113,8 +113,8 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
singleton.CleanMonitorHistory()
|
||||
serviceSentinelDispatchBus := make(chan model.Monitor) // 用于传递服务监控任务信息的channel
|
||||
singleton.CleanServiceHistory()
|
||||
serviceSentinelDispatchBus := make(chan model.Service) // 用于传递服务监控任务信息的channel
|
||||
go rpc.DispatchTask(serviceSentinelDispatchBus)
|
||||
go rpc.DispatchKeepalive()
|
||||
go singleton.AlertSentinelStart()
|
||||
|
||||
@@ -22,7 +22,7 @@ func ServeRPC() *grpc.Server {
|
||||
return server
|
||||
}
|
||||
|
||||
func DispatchTask(serviceSentinelDispatchBus <-chan model.Monitor) {
|
||||
func DispatchTask(serviceSentinelDispatchBus <-chan model.Service) {
|
||||
workedServerIndex := 0
|
||||
for task := range serviceSentinelDispatchBus {
|
||||
round := 0
|
||||
@@ -42,17 +42,17 @@ func DispatchTask(serviceSentinelDispatchBus <-chan model.Monitor) {
|
||||
continue
|
||||
}
|
||||
// 如果此任务不可使用此服务器请求,跳过这个服务器(有些 IPv6 only 开了 NAT64 的机器请求 IPv4 总会出问题)
|
||||
if (task.Cover == model.MonitorCoverAll && task.SkipServers[singleton.SortedServerList[workedServerIndex].ID]) ||
|
||||
(task.Cover == model.MonitorCoverIgnoreAll && !task.SkipServers[singleton.SortedServerList[workedServerIndex].ID]) {
|
||||
if (task.Cover == model.ServiceCoverAll && task.SkipServers[singleton.SortedServerList[workedServerIndex].ID]) ||
|
||||
(task.Cover == model.ServiceCoverIgnoreAll && !task.SkipServers[singleton.SortedServerList[workedServerIndex].ID]) {
|
||||
workedServerIndex++
|
||||
continue
|
||||
}
|
||||
if task.Cover == model.MonitorCoverIgnoreAll && task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
if task.Cover == model.ServiceCoverIgnoreAll && task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
workedServerIndex++
|
||||
continue
|
||||
}
|
||||
if task.Cover == model.MonitorCoverAll && !task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
if task.Cover == model.ServiceCoverAll && !task.SkipServers[singleton.SortedServerList[workedServerIndex].ID] {
|
||||
singleton.SortedServerList[workedServerIndex].TaskStream.Send(task.PB())
|
||||
workedServerIndex++
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user