mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-02-04 04:30:05 +00:00
@@ -40,7 +40,7 @@ var (
|
|||||||
frontendDist embed.FS
|
frontendDist embed.FS
|
||||||
)
|
)
|
||||||
|
|
||||||
func initSystem() error {
|
func initSystem(bus chan<- *model.Service) error {
|
||||||
// 初始化管理员账户
|
// 初始化管理员账户
|
||||||
var usersCount int64
|
var usersCount int64
|
||||||
if err := singleton.DB.Model(&model.User{}).Count(&usersCount).Error; err != nil {
|
if err := singleton.DB.Model(&model.User{}).Count(&usersCount).Error; err != nil {
|
||||||
@@ -61,7 +61,9 @@ func initSystem() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 启动 singleton 包下的所有服务
|
// 启动 singleton 包下的所有服务
|
||||||
singleton.LoadSingleton()
|
if err := singleton.LoadSingleton(bus); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// 每天的3:30 对 监控记录 和 流量记录 进行清理
|
// 每天的3:30 对 监控记录 和 流量记录 进行清理
|
||||||
if _, err := singleton.CronShared.AddFunc("0 30 3 * * *", singleton.CleanServiceHistory); err != nil {
|
if _, err := singleton.CronShared.AddFunc("0 30 3 * * *", singleton.CleanServiceHistory); err != nil {
|
||||||
@@ -107,12 +109,13 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serviceSentinelDispatchBus := make(chan *model.Service) // 用于传递服务监控任务信息的channel
|
||||||
// 初始化 dao 包
|
// 初始化 dao 包
|
||||||
if err := utils.FirstError(singleton.InitFrontendTemplates,
|
if err := utils.FirstError(singleton.InitFrontendTemplates,
|
||||||
func() error { return singleton.InitConfigFromPath(dashboardCliParam.ConfigFile) },
|
func() error { return singleton.InitConfigFromPath(dashboardCliParam.ConfigFile) },
|
||||||
singleton.InitTimezoneAndCache,
|
singleton.InitTimezoneAndCache,
|
||||||
func() error { return singleton.InitDBFromPath(dashboardCliParam.DatabaseLocation) },
|
func() error { return singleton.InitDBFromPath(dashboardCliParam.DatabaseLocation) },
|
||||||
initSystem); err != nil {
|
func() error { return initSystem(serviceSentinelDispatchBus) }); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,15 +125,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
singleton.CleanServiceHistory()
|
singleton.CleanServiceHistory()
|
||||||
serviceSentinelDispatchBus := make(chan *model.Service) // 用于传递服务监控任务信息的channel
|
|
||||||
rpc.DispatchKeepalive()
|
rpc.DispatchKeepalive()
|
||||||
go rpc.DispatchTask(serviceSentinelDispatchBus)
|
go rpc.DispatchTask(serviceSentinelDispatchBus)
|
||||||
go singleton.AlertSentinelStart()
|
go singleton.AlertSentinelStart()
|
||||||
singleton.ServiceSentinelShared, err = singleton.NewServiceSentinel(
|
|
||||||
serviceSentinelDispatchBus, singleton.ServerShared, singleton.NotificationShared, singleton.CronShared)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
grpcHandler := rpc.ServeRPC()
|
grpcHandler := rpc.ServeRPC()
|
||||||
httpHandler := controller.ServeWeb(frontendDist)
|
httpHandler := controller.ServeWeb(frontendDist)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"iter"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -71,35 +72,63 @@ func FindByUserID[S ~[]E, E CommonInterface](s S, uid uint64) []uint64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SearchByIDCtx[S ~[]E, E CommonInterface](c *gin.Context, x S) S {
|
func SearchByIDCtx[S ~[]E, E CommonInterface](c *gin.Context, x S) S {
|
||||||
switch any(x).(type) {
|
return SearchByID(strings.SplitSeq(c.Query("id"), ","), x)
|
||||||
case []*Server:
|
|
||||||
l := searchByIDCtxServer(c, any(x).([]*Server))
|
|
||||||
return any(l).(S)
|
|
||||||
default:
|
|
||||||
var s S
|
|
||||||
for idStr := range strings.SplitSeq(c.Query("id"), ",") {
|
|
||||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
s = appendBinarySearch(s, x, id)
|
|
||||||
}
|
|
||||||
return utils.IfOr(len(s) > 0, s, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func searchByIDCtxServer(c *gin.Context, x []*Server) []*Server {
|
func SearchByID[S ~[]E, E CommonInterface](seq iter.Seq[string], x S) S {
|
||||||
list1, list2 := SplitList(x)
|
if hasPriorityList[E]() {
|
||||||
|
return searchByIDPri(seq, x)
|
||||||
|
}
|
||||||
|
|
||||||
var clist1, clist2 []*Server
|
var s S
|
||||||
for idStr := range strings.SplitSeq(c.Query("id"), ",") {
|
for idStr := range seq {
|
||||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
clist1 = appendBinarySearch(clist1, list1, id)
|
s = appendBinarySearch(s, x, id)
|
||||||
|
}
|
||||||
|
return utils.IfOr(len(s) > 0, s, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasPriorityList[T CommonInterface]() bool {
|
||||||
|
var class T
|
||||||
|
|
||||||
|
switch any(class).(type) {
|
||||||
|
case *Server:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type splitter[S ~[]E, E CommonInterface] interface {
|
||||||
|
// SplitList should split a sorted list into two separate lists:
|
||||||
|
// The first list contains elements with a priority set (DisplayIndex != 0).
|
||||||
|
// The second list contains elements without a priority set (DisplayIndex == 0).
|
||||||
|
// The original slice is not modified. If no element without a priority is found, it returns nil.
|
||||||
|
// Should be safe to use with a nil pointer.
|
||||||
|
SplitList(x S) (S, S)
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchByIDPri[S ~[]E, E CommonInterface](seq iter.Seq[string], x S) S {
|
||||||
|
var class E
|
||||||
|
split, ok := any(class).(splitter[S, E])
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
plist, list2 := split.SplitList(x)
|
||||||
|
|
||||||
|
var clist1, clist2 S
|
||||||
|
for idStr := range seq {
|
||||||
|
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
clist1 = appendSearch(clist1, plist, id)
|
||||||
clist2 = appendBinarySearch(clist2, list2, id)
|
clist2 = appendBinarySearch(clist2, list2, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,3 +144,13 @@ func appendBinarySearch[S ~[]E, E CommonInterface](x, y S, target uint64) S {
|
|||||||
}
|
}
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func appendSearch[S ~[]E, E CommonInterface](x, y S, target uint64) S {
|
||||||
|
if i := slices.IndexFunc(y, func(e E) bool {
|
||||||
|
return e.GetID() == target
|
||||||
|
}); i != -1 {
|
||||||
|
x = append(x, y[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|||||||
49
model/common_test.go
Normal file
49
model/common_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSearchByID(t *testing.T) {
|
||||||
|
t.Run("WithoutPriorityList", func(t *testing.T) {
|
||||||
|
list, exp := []*DDNSProfile{
|
||||||
|
{Common: Common{ID: 1}},
|
||||||
|
{Common: Common{ID: 2}},
|
||||||
|
{Common: Common{ID: 3}},
|
||||||
|
{Common: Common{ID: 4}},
|
||||||
|
{Common: Common{ID: 5}},
|
||||||
|
}, []*DDNSProfile{
|
||||||
|
{Common: Common{ID: 4}},
|
||||||
|
{Common: Common{ID: 1}},
|
||||||
|
{Common: Common{ID: 3}},
|
||||||
|
}
|
||||||
|
|
||||||
|
searchList := slices.Values([]string{"4", "1", "3"})
|
||||||
|
filtered := SearchByID(searchList, list)
|
||||||
|
if !reflect.DeepEqual(filtered, exp) {
|
||||||
|
t.Fatalf("expected %v, but got %v", exp, filtered)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("WithPriorityTest", func(t *testing.T) {
|
||||||
|
list, exp := []*Server{
|
||||||
|
{Common: Common{ID: 5}, DisplayIndex: 2},
|
||||||
|
{Common: Common{ID: 4}, DisplayIndex: 1},
|
||||||
|
{Common: Common{ID: 1}},
|
||||||
|
{Common: Common{ID: 2}},
|
||||||
|
{Common: Common{ID: 3}},
|
||||||
|
}, []*Server{
|
||||||
|
{Common: Common{ID: 4}, DisplayIndex: 1},
|
||||||
|
{Common: Common{ID: 5}, DisplayIndex: 2},
|
||||||
|
{Common: Common{ID: 3}},
|
||||||
|
}
|
||||||
|
|
||||||
|
searchList := slices.Values([]string{"3", "4", "5"})
|
||||||
|
filtered := SearchByID(searchList, list)
|
||||||
|
if !reflect.DeepEqual(filtered, exp) {
|
||||||
|
t.Fatalf("expected %v, but got %v", exp, filtered)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -73,11 +73,7 @@ func (s *Server) AfterFind(tx *gorm.DB) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split a sorted server list into two separate lists:
|
func (s *Server) SplitList(x []*Server) ([]*Server, []*Server) {
|
||||||
// The first list contains servers with a priority set (DisplayIndex != 0).
|
|
||||||
// The second list contains servers without a priority set (DisplayIndex == 0).
|
|
||||||
// The original slice is not modified. If no server without a priority is found, it returns nil.
|
|
||||||
func SplitList(x []*Server) ([]*Server, []*Server) {
|
|
||||||
pri := func(s *Server) bool {
|
pri := func(s *Server) bool {
|
||||||
return s.DisplayIndex == 0
|
return s.DisplayIndex == 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func InitTimezoneAndCache() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadSingleton 加载子服务并执行
|
// LoadSingleton 加载子服务并执行
|
||||||
func LoadSingleton() {
|
func LoadSingleton(bus chan<- *model.Service) (err error) {
|
||||||
initUser() // 加载用户ID绑定表
|
initUser() // 加载用户ID绑定表
|
||||||
initI18n() // 加载本地化服务
|
initI18n() // 加载本地化服务
|
||||||
NotificationShared = NewNotificationClass() // 加载通知服务
|
NotificationShared = NewNotificationClass() // 加载通知服务
|
||||||
@@ -59,6 +59,8 @@ func LoadSingleton() {
|
|||||||
CronShared = NewCronClass() // 加载定时任务
|
CronShared = NewCronClass() // 加载定时任务
|
||||||
NATShared = NewNATClass()
|
NATShared = NewNATClass()
|
||||||
DDNSShared = NewDDNSClass()
|
DDNSShared = NewDDNSClass()
|
||||||
|
ServiceSentinelShared, err = NewServiceSentinel(bus, ServerShared, NotificationShared, CronShared)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitFrontendTemplates 从内置文件中加载FrontendTemplates
|
// InitFrontendTemplates 从内置文件中加载FrontendTemplates
|
||||||
|
|||||||
Reference in New Issue
Block a user