package service import ( "encoding/json" "errors" "sync" "x-ui/logger" "x-ui/xray" "go.uber.org/atomic" ) var p *xray.Process var lock sync.Mutex var isNeedXrayRestart atomic.Bool var result string type XrayService struct { inboundService InboundService settingService SettingService } func (s *XrayService) IsXrayRunning() bool { return p != nil && p.IsRunning() } func (s *XrayService) GetXrayErr() error { if p == nil { return nil } return p.GetErr() } func (s *XrayService) GetXrayResult() string { if result != "" { return result } if s.IsXrayRunning() { return "" } if p == nil { return "" } result = p.GetResult() return result } func (s *XrayService) GetXrayVersion() string { if p == nil { return "Unknown" } return p.GetVersion() } func RemoveIndex(s []interface{}, index int) []interface{} { return append(s[:index], s[index+1:]...) } func (s *XrayService) GetXrayConfig() (*xray.Config, error) { templateConfig, err := s.settingService.GetXrayConfigTemplate() if err != nil { return nil, err } xrayConfig := &xray.Config{} err = json.Unmarshal([]byte(templateConfig), xrayConfig) if err != nil { return nil, err } s.inboundService.DisableInvalidClients() inbounds, err := s.inboundService.GetAllInbounds() if err != nil { return nil, err } for _, inbound := range inbounds { if !inbound.Enable { continue } // get settings clients settings := map[string]interface{}{} json.Unmarshal([]byte(inbound.Settings), &settings) clients, ok := settings["clients"].([]interface{}) if ok { // check users active or not clientStats := inbound.ClientStats for _, clientTraffic := range clientStats { for index, client := range clients { c := client.(map[string]interface{}) if c["email"] == clientTraffic.Email { if !clientTraffic.Enable { clients = RemoveIndex(clients, index) logger.Info("Remove Inbound User", c["email"], "due the expire or traffic limit") } } } } settings["clients"] = clients modifiedSettings, err := json.Marshal(settings) if err != nil { return nil, err } inbound.Settings = string(modifiedSettings) } inboundConfig := inbound.GenXrayInboundConfig() xrayConfig.InboundConfigs = append(xrayConfig.InboundConfigs, *inboundConfig) } return xrayConfig, nil } func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.ClientTraffic, error) { if !s.IsXrayRunning() { return nil, nil, errors.New("xray is not running") } return p.GetTraffic(true) } func (s *XrayService) RestartXray(isForce bool) error { lock.Lock() defer lock.Unlock() logger.Debug("restart xray, force:", isForce) xrayConfig, err := s.GetXrayConfig() if err != nil { return err } if p != nil && p.IsRunning() { if !isForce && p.GetConfig().Equals(xrayConfig) { logger.Debug("not need to restart xray") return nil } p.Stop() } p = xray.NewProcess(xrayConfig) result = "" return p.Start() } func (s *XrayService) StopXray() error { lock.Lock() defer lock.Unlock() logger.Debug("stop xray") if s.IsXrayRunning() { return p.Stop() } return errors.New("xray is not running") } func (s *XrayService) SetToNeedRestart() { isNeedXrayRestart.Store(true) } func (s *XrayService) IsNeedRestartAndSetFalse() bool { return isNeedXrayRestart.CAS(true, false) }