package service import ( _ "embed" "encoding/json" "errors" "fmt" "reflect" "strconv" "strings" "time" "x-ui/database" "x-ui/database/model" "x-ui/logger" "x-ui/util/common" "x-ui/util/random" "x-ui/util/reflect_util" "x-ui/web/entity" "x-ui/xray" ) //go:embed config.json var xrayTemplateConfig string var defaultValueMap = map[string]string{ "xrayTemplateConfig": xrayTemplateConfig, "webListen": "", "webDomain": "", "webPort": "2053", "webCertFile": "", "webKeyFile": "", "secret": random.Seq(32), "webBasePath": "/", "sessionMaxAge": "60", "pageSize": "50", "expireDiff": "0", "trafficDiff": "0", "remarkModel": "-ieo", "timeLocation": "Asia/Tehran", "tgBotEnable": "false", "tgBotToken": "", "tgBotProxy": "", "tgBotAPIServer": "", "tgBotChatId": "", "tgRunTime": "@daily", "tgBotBackup": "false", "tgBotLoginNotify": "true", "tgCpu": "80", "tgLang": "en-US", "secretEnable": "false", "subEnable": "false", "subListen": "", "subPort": "2096", "subPath": "/sub/", "subDomain": "", "subCertFile": "", "subKeyFile": "", "subUpdates": "12", "subEncrypt": "true", "subShowInfo": "true", "subURI": "", "subJsonPath": "/json/", "subJsonURI": "", "subJsonFragment": "", "subJsonNoises": "", "subJsonMux": "", "subJsonRules": "", "datepicker": "gregorian", "warp": "", } type SettingService struct{} func (s *SettingService) GetDefaultJsonConfig() (interface{}, error) { var jsonData interface{} err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData) if err != nil { return nil, err } return jsonData, nil } func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) { db := database.GetDB() settings := make([]*model.Setting, 0) err := db.Model(model.Setting{}).Not("key = ?", "xrayTemplateConfig").Find(&settings).Error if err != nil { return nil, err } allSetting := &entity.AllSetting{} t := reflect.TypeOf(allSetting).Elem() v := reflect.ValueOf(allSetting).Elem() fields := reflect_util.GetFields(t) setSetting := func(key, value string) (err error) { defer func() { panicErr := recover() if panicErr != nil { err = errors.New(fmt.Sprint(panicErr)) } }() var found bool var field reflect.StructField for _, f := range fields { if f.Tag.Get("json") == key { field = f found = true break } } if !found { // Some settings are automatically generated, no need to return to the front end to modify the user return nil } fieldV := v.FieldByName(field.Name) switch t := fieldV.Interface().(type) { case int: n, err := strconv.ParseInt(value, 10, 64) if err != nil { return err } fieldV.SetInt(n) case string: fieldV.SetString(value) case bool: fieldV.SetBool(value == "true") default: return common.NewErrorf("unknown field %v type %v", key, t) } return } keyMap := map[string]bool{} for _, setting := range settings { err := setSetting(setting.Key, setting.Value) if err != nil { return nil, err } keyMap[setting.Key] = true } for key, value := range defaultValueMap { if keyMap[key] { continue } err := setSetting(key, value) if err != nil { return nil, err } } return allSetting, nil } func (s *SettingService) ResetSettings() error { db := database.GetDB() err := db.Where("1 = 1").Delete(model.Setting{}).Error if err != nil { return err } return db.Model(model.User{}). Where("1 = 1"). Update("login_secret", "").Error } func (s *SettingService) getSetting(key string) (*model.Setting, error) { db := database.GetDB() setting := &model.Setting{} err := db.Model(model.Setting{}).Where("key = ?", key).First(setting).Error if err != nil { return nil, err } return setting, nil } func (s *SettingService) saveSetting(key string, value string) error { setting, err := s.getSetting(key) db := database.GetDB() if database.IsNotFound(err) { return db.Create(&model.Setting{ Key: key, Value: value, }).Error } else if err != nil { return err } setting.Key = key setting.Value = value return db.Save(setting).Error } func (s *SettingService) getString(key string) (string, error) { setting, err := s.getSetting(key) if database.IsNotFound(err) { value, ok := defaultValueMap[key] if !ok { return "", common.NewErrorf("key <%v> not in defaultValueMap", key) } return value, nil } else if err != nil { return "", err } return setting.Value, nil } func (s *SettingService) setString(key string, value string) error { return s.saveSetting(key, value) } func (s *SettingService) getBool(key string) (bool, error) { str, err := s.getString(key) if err != nil { return false, err } return strconv.ParseBool(str) } func (s *SettingService) setBool(key string, value bool) error { return s.setString(key, strconv.FormatBool(value)) } func (s *SettingService) getInt(key string) (int, error) { str, err := s.getString(key) if err != nil { return 0, err } return strconv.Atoi(str) } func (s *SettingService) setInt(key string, value int) error { return s.setString(key, strconv.Itoa(value)) } func (s *SettingService) GetXrayConfigTemplate() (string, error) { return s.getString("xrayTemplateConfig") } func (s *SettingService) GetListen() (string, error) { return s.getString("webListen") } func (s *SettingService) GetWebDomain() (string, error) { return s.getString("webDomain") } func (s *SettingService) GetTgBotToken() (string, error) { return s.getString("tgBotToken") } func (s *SettingService) SetTgBotToken(token string) error { return s.setString("tgBotToken", token) } func (s *SettingService) GetTgBotProxy() (string, error) { return s.getString("tgBotProxy") } func (s *SettingService) SetTgBotProxy(token string) error { return s.setString("tgBotProxy", token) } func (s *SettingService) GetTgBotAPIServer() (string, error) { return s.getString("tgBotAPIServer") } func (s *SettingService) SetTgBotAPIServer(token string) error { return s.setString("tgBotAPIServer", token) } func (s *SettingService) GetTgBotChatId() (string, error) { return s.getString("tgBotChatId") } func (s *SettingService) SetTgBotChatId(chatIds string) error { return s.setString("tgBotChatId", chatIds) } func (s *SettingService) GetTgbotEnabled() (bool, error) { return s.getBool("tgBotEnable") } func (s *SettingService) SetTgbotEnabled(value bool) error { return s.setBool("tgBotEnable", value) } func (s *SettingService) GetTgbotRuntime() (string, error) { return s.getString("tgRunTime") } func (s *SettingService) SetTgbotRuntime(time string) error { return s.setString("tgRunTime", time) } func (s *SettingService) GetTgBotBackup() (bool, error) { return s.getBool("tgBotBackup") } func (s *SettingService) GetTgBotLoginNotify() (bool, error) { return s.getBool("tgBotLoginNotify") } func (s *SettingService) GetTgCpu() (int, error) { return s.getInt("tgCpu") } func (s *SettingService) GetTgLang() (string, error) { return s.getString("tgLang") } func (s *SettingService) GetPort() (int, error) { return s.getInt("webPort") } func (s *SettingService) SetPort(port int) error { return s.setInt("webPort", port) } func (s *SettingService) SetCertFile(webCertFile string) error { return s.setString("webCertFile", webCertFile) } func (s *SettingService) GetCertFile() (string, error) { return s.getString("webCertFile") } func (s *SettingService) SetKeyFile(webKeyFile string) error { return s.setString("webKeyFile", webKeyFile) } func (s *SettingService) GetKeyFile() (string, error) { return s.getString("webKeyFile") } func (s *SettingService) GetExpireDiff() (int, error) { return s.getInt("expireDiff") } func (s *SettingService) GetTrafficDiff() (int, error) { return s.getInt("trafficDiff") } func (s *SettingService) GetSessionMaxAge() (int, error) { return s.getInt("sessionMaxAge") } func (s *SettingService) GetRemarkModel() (string, error) { return s.getString("remarkModel") } func (s *SettingService) GetSecretStatus() (bool, error) { return s.getBool("secretEnable") } func (s *SettingService) SetSecretStatus(value bool) error { return s.setBool("secretEnable", value) } func (s *SettingService) GetSecret() ([]byte, error) { secret, err := s.getString("secret") if secret == defaultValueMap["secret"] { err := s.saveSetting("secret", secret) if err != nil { logger.Warning("save secret failed:", err) } } return []byte(secret), err } func (s *SettingService) SetBasePath(basePath string) error { if !strings.HasPrefix(basePath, "/") { basePath = "/" + basePath } if !strings.HasSuffix(basePath, "/") { basePath += "/" } return s.setString("webBasePath", basePath) } func (s *SettingService) GetBasePath() (string, error) { basePath, err := s.getString("webBasePath") if err != nil { return "", err } if !strings.HasPrefix(basePath, "/") { basePath = "/" + basePath } if !strings.HasSuffix(basePath, "/") { basePath += "/" } return basePath, nil } func (s *SettingService) GetTimeLocation() (*time.Location, error) { l, err := s.getString("timeLocation") if err != nil { return nil, err } location, err := time.LoadLocation(l) if err != nil { defaultLocation := defaultValueMap["timeLocation"] logger.Errorf("location <%v> not exist, using default location: %v", l, defaultLocation) return time.LoadLocation(defaultLocation) } return location, nil } func (s *SettingService) GetSubEnable() (bool, error) { return s.getBool("subEnable") } func (s *SettingService) GetSubListen() (string, error) { return s.getString("subListen") } func (s *SettingService) GetSubPort() (int, error) { return s.getInt("subPort") } func (s *SettingService) GetSubPath() (string, error) { return s.getString("subPath") } func (s *SettingService) GetSubJsonPath() (string, error) { return s.getString("subJsonPath") } func (s *SettingService) GetSubDomain() (string, error) { return s.getString("subDomain") } func (s *SettingService) GetSubCertFile() (string, error) { return s.getString("subCertFile") } func (s *SettingService) GetSubKeyFile() (string, error) { return s.getString("subKeyFile") } func (s *SettingService) GetSubUpdates() (string, error) { return s.getString("subUpdates") } func (s *SettingService) GetSubEncrypt() (bool, error) { return s.getBool("subEncrypt") } func (s *SettingService) GetSubShowInfo() (bool, error) { return s.getBool("subShowInfo") } func (s *SettingService) GetPageSize() (int, error) { return s.getInt("pageSize") } func (s *SettingService) GetSubURI() (string, error) { return s.getString("subURI") } func (s *SettingService) GetSubJsonURI() (string, error) { return s.getString("subJsonURI") } func (s *SettingService) GetSubJsonFragment() (string, error) { return s.getString("subJsonFragment") } func (s *SettingService) GetSubJsonNoises() (string, error) { return s.getString("subJsonNoises") } func (s *SettingService) GetSubJsonMux() (string, error) { return s.getString("subJsonMux") } func (s *SettingService) GetSubJsonRules() (string, error) { return s.getString("subJsonRules") } func (s *SettingService) GetDatepicker() (string, error) { return s.getString("datepicker") } func (s *SettingService) GetWarp() (string, error) { return s.getString("warp") } func (s *SettingService) SetWarp(data string) error { return s.setString("warp", data) } func (s *SettingService) GetIpLimitEnable() (bool, error) { accessLogPath, err := xray.GetAccessLogPath() if err != nil { return false, err } return (accessLogPath != "none" && accessLogPath != ""), nil } func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { if err := allSetting.CheckValid(); err != nil { return err } v := reflect.ValueOf(allSetting).Elem() t := reflect.TypeOf(allSetting).Elem() fields := reflect_util.GetFields(t) errs := make([]error, 0) for _, field := range fields { key := field.Tag.Get("json") fieldV := v.FieldByName(field.Name) value := fmt.Sprint(fieldV.Interface()) err := s.saveSetting(key, value) if err != nil { errs = append(errs, err) } } return common.Combine(errs...) } func (s *SettingService) GetDefaultXrayConfig() (interface{}, error) { var jsonData interface{} err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData) if err != nil { return nil, err } return jsonData, nil } func (s *SettingService) GetDefaultSettings(host string) (interface{}, error) { type settingFunc func() (interface{}, error) settings := map[string]settingFunc{ "expireDiff": func() (interface{}, error) { return s.GetExpireDiff() }, "trafficDiff": func() (interface{}, error) { return s.GetTrafficDiff() }, "pageSize": func() (interface{}, error) { return s.GetPageSize() }, "defaultCert": func() (interface{}, error) { return s.GetCertFile() }, "defaultKey": func() (interface{}, error) { return s.GetKeyFile() }, "tgBotEnable": func() (interface{}, error) { return s.GetTgbotEnabled() }, "subEnable": func() (interface{}, error) { return s.GetSubEnable() }, "subURI": func() (interface{}, error) { return s.GetSubURI() }, "subJsonURI": func() (interface{}, error) { return s.GetSubJsonURI() }, "remarkModel": func() (interface{}, error) { return s.GetRemarkModel() }, "datepicker": func() (interface{}, error) { return s.GetDatepicker() }, "ipLimitEnable": func() (interface{}, error) { return s.GetIpLimitEnable() }, } result := make(map[string]interface{}) for key, fn := range settings { value, err := fn() if err != nil { return "", err } result[key] = value } if result["subEnable"].(bool) && (result["subURI"].(string) == "" || result["subJsonURI"].(string) == "") { subURI := "" subPort, _ := s.GetSubPort() subPath, _ := s.GetSubPath() subJsonPath, _ := s.GetSubJsonPath() subDomain, _ := s.GetSubDomain() subKeyFile, _ := s.GetSubKeyFile() subCertFile, _ := s.GetSubCertFile() subTLS := false if subKeyFile != "" && subCertFile != "" { subTLS = true } if subDomain == "" { subDomain = strings.Split(host, ":")[0] } if subTLS { subURI = "https://" } else { subURI = "http://" } if (subPort == 443 && subTLS) || (subPort == 80 && !subTLS) { subURI += subDomain } else { subURI += fmt.Sprintf("%s:%d", subDomain, subPort) } if result["subURI"].(string) == "" { result["subURI"] = subURI + subPath } if result["subJsonURI"].(string) == "" { result["subJsonURI"] = subURI + subJsonPath } } return result, nil }