mirror of
https://github.com/nezhahq/nezha.git
synced 2025-02-10 05:28:12 -05:00
feat: batch set server config (#983)
* feat: batch set server config * run in parallel * fix route * fix * return some information * fix order
This commit is contained in:
parent
29d716b935
commit
a41f623dd2
@ -110,8 +110,8 @@ func routers(r *gin.Engine, frontendDist fs.FS) {
|
|||||||
|
|
||||||
auth.GET("/server", listHandler(listServer))
|
auth.GET("/server", listHandler(listServer))
|
||||||
auth.PATCH("/server/:id", commonHandler(updateServer))
|
auth.PATCH("/server/:id", commonHandler(updateServer))
|
||||||
auth.GET("/server/:id/config", commonHandler(getServerConfig))
|
auth.GET("/server/config/:id", commonHandler(getServerConfig))
|
||||||
auth.POST("/server/:id/config", commonHandler(setServerConfig))
|
auth.POST("/server/config", commonHandler(setServerConfig))
|
||||||
auth.POST("/batch-delete/server", commonHandler(batchDeleteServer))
|
auth.POST("/batch-delete/server", commonHandler(batchDeleteServer))
|
||||||
auth.POST("/force-update/server", commonHandler(forceUpdateServer))
|
auth.POST("/force-update/server", commonHandler(forceUpdateServer))
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -182,15 +183,15 @@ func batchDeleteServer(c *gin.Context) (any, error) {
|
|||||||
// @Accept json
|
// @Accept json
|
||||||
// @param request body []uint64 true "id list"
|
// @param request body []uint64 true "id list"
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} model.CommonResponse[model.ForceUpdateResponse]
|
// @Success 200 {object} model.CommonResponse[model.ServerTaskResponse]
|
||||||
// @Router /force-update/server [post]
|
// @Router /force-update/server [post]
|
||||||
func forceUpdateServer(c *gin.Context) (*model.ForceUpdateResponse, error) {
|
func forceUpdateServer(c *gin.Context) (*model.ServerTaskResponse, error) {
|
||||||
var forceUpdateServers []uint64
|
var forceUpdateServers []uint64
|
||||||
if err := c.ShouldBindJSON(&forceUpdateServers); err != nil {
|
if err := c.ShouldBindJSON(&forceUpdateServers); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
forceUpdateResp := new(model.ForceUpdateResponse)
|
forceUpdateResp := new(model.ServerTaskResponse)
|
||||||
|
|
||||||
for _, sid := range forceUpdateServers {
|
for _, sid := range forceUpdateServers {
|
||||||
singleton.ServerLock.RLock()
|
singleton.ServerLock.RLock()
|
||||||
@ -223,7 +224,7 @@ func forceUpdateServer(c *gin.Context) (*model.ForceUpdateResponse, error) {
|
|||||||
// @Tags auth required
|
// @Tags auth required
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} model.CommonResponse[string]
|
// @Success 200 {object} model.CommonResponse[string]
|
||||||
// @Router /server/{id}/config [get]
|
// @Router /server/config/{id} [get]
|
||||||
func getServerConfig(c *gin.Context) (string, error) {
|
func getServerConfig(c *gin.Context) (string, error) {
|
||||||
idStr := c.Param("id")
|
idStr := c.Param("id")
|
||||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
@ -273,40 +274,66 @@ func getServerConfig(c *gin.Context) (string, error) {
|
|||||||
// @Description Set server config
|
// @Description Set server config
|
||||||
// @Tags auth required
|
// @Tags auth required
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @param request body string true "config"
|
// @Param body body model.ServerConfigForm true "ServerConfigForm"
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} model.CommonResponse[any]
|
// @Success 200 {object} model.CommonResponse[model.ServerTaskResponse]
|
||||||
// @Router /server/{id}/config [post]
|
// @Router /server/config [post]
|
||||||
func setServerConfig(c *gin.Context) (any, error) {
|
func setServerConfig(c *gin.Context) (*model.ServerTaskResponse, error) {
|
||||||
idStr := c.Param("id")
|
var configForm model.ServerConfigForm
|
||||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
if err := c.ShouldBindJSON(&configForm); err != nil {
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var configRaw string
|
|
||||||
if err := c.ShouldBindJSON(&configRaw); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var resp model.ServerTaskResponse
|
||||||
singleton.ServerLock.RLock()
|
singleton.ServerLock.RLock()
|
||||||
s, ok := singleton.ServerList[id]
|
servers := make([]*model.Server, 0, len(configForm.Servers))
|
||||||
if !ok || s.TaskStream == nil {
|
for _, sid := range configForm.Servers {
|
||||||
singleton.ServerLock.RUnlock()
|
if s, ok := singleton.ServerList[sid]; ok {
|
||||||
return "", nil
|
if !s.HasPermission(c) {
|
||||||
|
singleton.ServerLock.RUnlock()
|
||||||
|
return nil, singleton.Localizer.ErrorT("permission denied")
|
||||||
|
}
|
||||||
|
if s.TaskStream == nil {
|
||||||
|
resp.Offline = append(resp.Offline, s.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
servers = append(servers, s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
singleton.ServerLock.RUnlock()
|
singleton.ServerLock.RUnlock()
|
||||||
|
|
||||||
if !s.HasPermission(c) {
|
var wg sync.WaitGroup
|
||||||
return "", singleton.Localizer.ErrorT("permission denied")
|
var respMu sync.Mutex
|
||||||
|
|
||||||
|
for i := 0; i < len(servers); i += 10 {
|
||||||
|
end := i + 10
|
||||||
|
if end > len(servers) {
|
||||||
|
end = len(servers)
|
||||||
|
}
|
||||||
|
group := servers[i:end]
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(srvGroup []*model.Server) {
|
||||||
|
defer wg.Done()
|
||||||
|
for _, s := range srvGroup {
|
||||||
|
// Create and send the task.
|
||||||
|
task := &pb.Task{
|
||||||
|
Type: model.TaskTypeApplyConfig,
|
||||||
|
Data: configForm.Config,
|
||||||
|
}
|
||||||
|
if err := s.TaskStream.Send(task); err != nil {
|
||||||
|
respMu.Lock()
|
||||||
|
resp.Failure = append(resp.Failure, s.ID)
|
||||||
|
respMu.Unlock()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
respMu.Lock()
|
||||||
|
resp.Success = append(resp.Success, s.ID)
|
||||||
|
respMu.Unlock()
|
||||||
|
}
|
||||||
|
}(group)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.TaskStream.Send(&pb.Task{
|
wg.Wait()
|
||||||
Type: model.TaskTypeApplyConfig,
|
return &resp, nil
|
||||||
Data: configRaw,
|
|
||||||
}); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,12 @@ type ServerForm struct {
|
|||||||
OverrideDDNSDomains map[uint64][]string `json:"override_ddns_domains,omitempty" validate:"optional"`
|
OverrideDDNSDomains map[uint64][]string `json:"override_ddns_domains,omitempty" validate:"optional"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForceUpdateResponse struct {
|
type ServerConfigForm struct {
|
||||||
|
Servers []uint64 `json:"servers,omitempty"`
|
||||||
|
Config string `json:"config,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerTaskResponse struct {
|
||||||
Success []uint64 `json:"success,omitempty" validate:"optional"`
|
Success []uint64 `json:"success,omitempty" validate:"optional"`
|
||||||
Failure []uint64 `json:"failure,omitempty" validate:"optional"`
|
Failure []uint64 `json:"failure,omitempty" validate:"optional"`
|
||||||
Offline []uint64 `json:"offline,omitempty" validate:"optional"`
|
Offline []uint64 `json:"offline,omitempty" validate:"optional"`
|
||||||
|
@ -54,7 +54,7 @@ func (provider *Provider) UpdateDomain(ctx context.Context, overrideDomains ...s
|
|||||||
if err := provider.updateDomain(domain); err != nil {
|
if err := provider.updateDomain(domain); err != nil {
|
||||||
log.Printf("NEZHA>> Failed to update DNS record of domain %s: %v", domain, err)
|
log.Printf("NEZHA>> Failed to update DNS record of domain %s: %v", domain, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("NEZHA>> Update DNS record of domain %s succeed", domain)
|
log.Printf("NEZHA>> Update DNS record of domain %s succeeded", domain)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,9 +44,9 @@ func (s *NezhaHandler) RequestTask(stream pb.NezhaService_RequestTaskServer) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
singleton.ServerLock.RLock()
|
singleton.ServerLock.Lock()
|
||||||
singleton.ServerList[clientID].TaskStream = stream
|
singleton.ServerList[clientID].TaskStream = stream
|
||||||
singleton.ServerLock.RUnlock()
|
singleton.ServerLock.Unlock()
|
||||||
|
|
||||||
var result *pb.TaskResult
|
var result *pb.TaskResult
|
||||||
for {
|
for {
|
||||||
|
@ -270,7 +270,7 @@ func SendNotification(notificationGroupID uint64, desc string, muteLabel *string
|
|||||||
if err := ns.Send(desc); err != nil {
|
if err := ns.Send(desc); err != nil {
|
||||||
log.Printf("NEZHA>> Sending notification to %s failed: %v", n.Name, err)
|
log.Printf("NEZHA>> Sending notification to %s failed: %v", n.Name, err)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("NEZHA>> Sending notification to %s succeed", n.Name)
|
log.Printf("NEZHA>> Sending notification to %s succeeded", n.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user