package panel import ( "encoding/json" "github.com/Yuzuki616/V2bX/app/mydispatcher" io "io/ioutil" "log" "sync" "github.com/Yuzuki616/V2bX/api" _ "github.com/Yuzuki616/V2bX/main/distro/all" "github.com/Yuzuki616/V2bX/service" "github.com/Yuzuki616/V2bX/service/controller" "github.com/imdario/mergo" "github.com/r3labs/diff/v2" "github.com/xtls/xray-core/app/proxyman" "github.com/xtls/xray-core/app/stats" "github.com/xtls/xray-core/common/serial" "github.com/xtls/xray-core/core" "github.com/xtls/xray-core/infra/conf" ) // Panel Structure type Panel struct { access sync.Mutex panelConfig *Config Server *core.Instance Service []service.Service Running bool } func New(panelConfig *Config) *Panel { p := &Panel{panelConfig: panelConfig} return p } func (p *Panel) loadCore(panelConfig *Config) *core.Instance { // Log Config coreLogConfig := &conf.LogConfig{} logConfig := getDefaultLogConfig() if panelConfig.LogConfig != nil { if _, err := diff.Merge(logConfig, panelConfig.LogConfig, logConfig); err != nil { log.Panicf("Read Log config failed: %s", err) } } coreLogConfig.LogLevel = logConfig.Level coreLogConfig.AccessLog = logConfig.AccessPath coreLogConfig.ErrorLog = logConfig.ErrorPath // DNS config coreDnsConfig := &conf.DNSConfig{} if panelConfig.DnsConfigPath != "" { if data, err := io.ReadFile(panelConfig.DnsConfigPath); err != nil { log.Panicf("Failed to read DNS config file at: %s", panelConfig.DnsConfigPath) } else { if err = json.Unmarshal(data, coreDnsConfig); err != nil { log.Panicf("Failed to unmarshal DNS config: %s", panelConfig.DnsConfigPath) } } } dnsConfig, err := coreDnsConfig.Build() if err != nil { log.Panicf("Failed to understand DNS config, Please check: https://xtls.github.io/config/dns.html for help: %s", err) } // Routing config coreRouterConfig := &conf.RouterConfig{} if panelConfig.RouteConfigPath != "" { if data, err := io.ReadFile(panelConfig.RouteConfigPath); err != nil { log.Panicf("Failed to read Routing config file at: %s", panelConfig.RouteConfigPath) } else { if err = json.Unmarshal(data, coreRouterConfig); err != nil { log.Panicf("Failed to unmarshal Routing config: %s", panelConfig.RouteConfigPath) } } } routeConfig, err := coreRouterConfig.Build() if err != nil { log.Panicf("Failed to understand Routing config Please check: https://xtls.github.io/config/routing.html for help: %s", err) } // Custom Inbound config var coreCustomInboundConfig []conf.InboundDetourConfig if panelConfig.InboundConfigPath != "" { if data, err := io.ReadFile(panelConfig.InboundConfigPath); err != nil { log.Panicf("Failed to read Custom Inbound config file at: %s", panelConfig.OutboundConfigPath) } else { if err = json.Unmarshal(data, &coreCustomInboundConfig); err != nil { log.Panicf("Failed to unmarshal Custom Inbound config: %s", panelConfig.OutboundConfigPath) } } } var inBoundConfig []*core.InboundHandlerConfig for _, config := range coreCustomInboundConfig { oc, err := config.Build() if err != nil { log.Panicf("Failed to understand Inbound config, Please check: https://xtls.github.io/config/inbound.html for help: %s", err) } inBoundConfig = append(inBoundConfig, oc) } // Custom Outbound config var coreCustomOutboundConfig []conf.OutboundDetourConfig if panelConfig.OutboundConfigPath != "" { if data, err := io.ReadFile(panelConfig.OutboundConfigPath); err != nil { log.Panicf("Failed to read Custom Outbound config file at: %s", panelConfig.OutboundConfigPath) } else { if err = json.Unmarshal(data, &coreCustomOutboundConfig); err != nil { log.Panicf("Failed to unmarshal Custom Outbound config: %s", panelConfig.OutboundConfigPath) } } } var outBoundConfig []*core.OutboundHandlerConfig for _, config := range coreCustomOutboundConfig { oc, err := config.Build() if err != nil { log.Panicf("Failed to understand Outbound config, Please check: https://xtls.github.io/config/outbound.html for help: %s", err) } outBoundConfig = append(outBoundConfig, oc) } // Policy config levelPolicyConfig := parseConnectionConfig(panelConfig.ConnetionConfig) corePolicyConfig := &conf.PolicyConfig{} corePolicyConfig.Levels = map[uint32]*conf.Policy{0: levelPolicyConfig} policyConfig, _ := corePolicyConfig.Build() // Build Core Config config := &core.Config{ App: []*serial.TypedMessage{ serial.ToTypedMessage(coreLogConfig.Build()), serial.ToTypedMessage(&mydispatcher.Config{}), serial.ToTypedMessage(&stats.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(policyConfig), serial.ToTypedMessage(dnsConfig), serial.ToTypedMessage(routeConfig), }, Inbound: inBoundConfig, Outbound: outBoundConfig, } server, err := core.New(config) if err != nil { log.Panicf("failed to create instance: %s", err) } log.Printf("Xray Core Version: %s", core.Version()) return server } // Start Start the panel func (p *Panel) Start() { p.access.Lock() defer p.access.Unlock() log.Print("Start the panel..") // Load Core server := p.loadCore(p.panelConfig) if err := server.Start(); err != nil { log.Panicf("Failed to start instance: %s", err) } p.Server = server // Load Nodes config for _, nodeConfig := range p.panelConfig.NodesConfig { var apiClient = api.New(nodeConfig.ApiConfig) var controllerService service.Service // Register controller service controllerConfig := getDefaultControllerConfig() if nodeConfig.ControllerConfig != nil { if err := mergo.Merge(controllerConfig, nodeConfig.ControllerConfig, mergo.WithOverride); err != nil { log.Panicf("Read Controller Config Failed") } } controllerService = controller.New(server, apiClient, controllerConfig) p.Service = append(p.Service, controllerService) } // Start all the service for _, s := range p.Service { err := s.Start() if err != nil { log.Panicf("Panel Start fialed: %s", err) } } p.Running = true return } // Close Close the panel func (p *Panel) Close() { p.access.Lock() defer p.access.Unlock() for _, s := range p.Service { err := s.Close() if err != nil { log.Panicf("Panel Close fialed: %s", err) } } p.Service = nil p.Server.Close() p.Running = false return } func parseConnectionConfig(c *ConnetionConfig) (policy *conf.Policy) { connetionConfig := getDefaultConnetionConfig() if c != nil { if _, err := diff.Merge(connetionConfig, c, connetionConfig); err != nil { log.Panicf("Read ConnetionConfig failed: %s", err) } } policy = &conf.Policy{ StatsUserUplink: true, StatsUserDownlink: true, Handshake: &connetionConfig.Handshake, ConnectionIdle: &connetionConfig.ConnIdle, UplinkOnly: &connetionConfig.UplinkOnly, DownlinkOnly: &connetionConfig.DownlinkOnly, BufferSize: &connetionConfig.BufferSize, } return }