package xboard import ( "context" "fmt" "github.com/sagernet/sing-box/adapter" boxService "github.com/sagernet/sing-box/adapter/service" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" ) type multiNodeService struct { boxService.Adapter services []adapter.Service } func newMultiNodeService(ctx context.Context, logger log.ContextLogger, tag string, options option.XBoardServiceOptions) (adapter.Service, error) { expanded := expandNodeOptions(options) services := make([]adapter.Service, 0, len(expanded)) for i, node := range expanded { nodeTag := expandedNodeTag(tag, i, options.Nodes[i], node) service, err := newSingleService(ctx, logger, nodeTag, node) if err != nil { return nil, fmt.Errorf("create xboard node service %s: %w", nodeTag, err) } services = append(services, service) } return &multiNodeService{ Adapter: boxService.NewAdapter(C.TypeXBoard, tag), services: services, }, nil } func expandNodeOptions(base option.XBoardServiceOptions) []option.XBoardServiceOptions { if len(base.Nodes) == 0 { return []option.XBoardServiceOptions{base} } result := make([]option.XBoardServiceOptions, 0, len(base.Nodes)) for _, node := range base.Nodes { child := base child.Nodes = nil if node.PanelURL != "" { child.PanelURL = node.PanelURL } if node.ConfigPanelURL != "" { child.ConfigPanelURL = node.ConfigPanelURL } if node.UserPanelURL != "" { child.UserPanelURL = node.UserPanelURL } if node.Key != "" { child.Key = node.Key } if node.NodeID != 0 { child.NodeID = node.NodeID } if node.ConfigNodeID != 0 { child.ConfigNodeID = node.ConfigNodeID } if node.UserNodeID != 0 { child.UserNodeID = node.UserNodeID } if node.NodeType != "" { child.NodeType = node.NodeType } if node.SyncInterval != 0 { child.SyncInterval = node.SyncInterval } if node.ReportInterval != 0 { child.ReportInterval = node.ReportInterval } result = append(result, child) } return result } func expandedNodeTag(baseTag string, index int, entry option.XBoardNodeOptions, node option.XBoardServiceOptions) string { nodeTag := "" if entry.Tag != "" { nodeTag = entry.Tag } if nodeTag == "" && node.NodeID != 0 { nodeTag = fmt.Sprintf("%d", node.NodeID) } if nodeTag == "" { nodeTag = fmt.Sprintf("%d", index+1) } if baseTag == "" { return "xboard-" + nodeTag } return fmt.Sprintf("%s-%s", baseTag, nodeTag) } func (s *multiNodeService) Start(stage adapter.StartStage) error { for _, service := range s.services { if err := service.Start(stage); err != nil { return err } } return nil } func (s *multiNodeService) Close() error { for i := len(s.services) - 1; i >= 0; i-- { if err := s.services[i].Close(); err != nil { return err } } return nil }