setting.go 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816
  1. package service
  2. import (
  3. _ "embed"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net"
  8. "net/http"
  9. "os"
  10. "reflect"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "time"
  15. "github.com/google/uuid"
  16. "github.com/xlzd/gotp"
  17. "gorm.io/gorm"
  18. "github.com/mhsanaei/3x-ui/v3/internal/config"
  19. "github.com/mhsanaei/3x-ui/v3/internal/database"
  20. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  21. "github.com/mhsanaei/3x-ui/v3/internal/logger"
  22. "github.com/mhsanaei/3x-ui/v3/internal/util/common"
  23. "github.com/mhsanaei/3x-ui/v3/internal/util/netproxy"
  24. "github.com/mhsanaei/3x-ui/v3/internal/util/random"
  25. "github.com/mhsanaei/3x-ui/v3/internal/util/reflect_util"
  26. "github.com/mhsanaei/3x-ui/v3/internal/web/entity"
  27. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  28. "github.com/mhsanaei/3x-ui/v3/internal/xray/dnsconf"
  29. )
  30. //go:embed config.json
  31. var xrayTemplateConfig string
  32. const (
  33. DefaultSubClashUserAgentRegex = `(?i)(clash|mihomo)`
  34. DefaultSubJsonUserAgentRegex = ``
  35. DefaultRemarkTemplate = "{{INBOUND}}-{{EMAIL}}|📊{{TRAFFIC_LEFT}}|⏳{{DAYS_LEFT}}D"
  36. DefaultSubExpiredTemplate = "⛔ {{EMAIL}} | Expired: {{EXPIRE_DATE}}"
  37. DefaultSubTrafficDepletedTemplate = "🚫 {{EMAIL}} | Traffic Depleted | {{TRAFFIC_USED}}/{{TRAFFIC_TOTAL}}"
  38. DefaultTrustedProxyCIDRs = "127.0.0.1/32,::1/128"
  39. maxRegexLength = 2048
  40. )
  41. var defaultValueMap = map[string]string{
  42. "xrayTemplateConfig": xrayTemplateConfig,
  43. "webListen": "",
  44. "webDomain": "",
  45. "webPort": "2053",
  46. "webCertFile": "",
  47. "webKeyFile": "",
  48. "secret": random.Seq(32),
  49. "panelGuid": uuid.NewString(),
  50. "apiToken": "",
  51. // Node mTLS material (opt-in). All default empty: the CA + master client
  52. // cert are minted lazily on first use, and the node-side trust CA is pasted
  53. // in by the operator. Kept out of entity.AllSetting so private keys never
  54. // reach the settings UI/export.
  55. "nodeMtlsCaCertPem": "",
  56. "nodeMtlsCaKeyPem": "",
  57. "nodeMtlsClientCertPem": "",
  58. "nodeMtlsClientKeyPem": "",
  59. "nodeMtlsClientCertSha256": "",
  60. "nodeMtlsClientCAPem": "",
  61. "webBasePath": normalizeBasePath(getEnv("XUI_INIT_WEB_BASE_PATH", "/")),
  62. "sessionMaxAge": "360",
  63. "trustedProxyCIDRs": DefaultTrustedProxyCIDRs,
  64. "realityScanCandidates": DefaultRealityScanCandidatesCSV,
  65. "ipLimitAllowlist": "",
  66. "pageSize": "25",
  67. "expireDiff": "0",
  68. "trafficDiff": "0",
  69. "remarkTemplate": DefaultRemarkTemplate,
  70. "subShowIdentityOnAllLinks": "false",
  71. "subInfoNodeEnable": "false",
  72. "subCalendarExpireInclusive": "false",
  73. "subExpiredTemplate": DefaultSubExpiredTemplate,
  74. "subTrafficDepletedTemplate": DefaultSubTrafficDepletedTemplate,
  75. "timeLocation": "Local",
  76. "tgBotEnable": "false",
  77. "tgBotToken": "",
  78. "tgBotProxy": "",
  79. "tgBotAPIServer": "",
  80. "tgBotChatId": "",
  81. "tgRunTime": "@daily",
  82. "tgBotBackup": "false",
  83. "tgCpu": "80",
  84. "tgMemory": "80",
  85. "tgLang": "en-US",
  86. "twoFactorEnable": "false",
  87. "twoFactorToken": "",
  88. "happLinkEnable": "false",
  89. "subEnable": "true",
  90. "subJsonEnable": "false",
  91. "subJsonAutoDetect": "false",
  92. "subJsonAlwaysArray": "false",
  93. "subJsonUserAgentRegex": "",
  94. "subClashAutoDetect": "false",
  95. "subClashUserAgentRegex": "",
  96. "subTitle": "",
  97. "subSupportUrl": "",
  98. "subProfileUrl": "",
  99. "subAnnounce": "",
  100. "subEnableRouting": "false",
  101. "subRoutingRules": "",
  102. "subHideSettings": "false",
  103. "subHappAutoDetect": "false",
  104. "subHappProviderId": "",
  105. "subHappNewUrl": "",
  106. "subHappFallbackUrl": "",
  107. "subHappSubInfoColor": "blue",
  108. "subHappSubInfoText": "",
  109. "subHappSubInfoButtonText": "",
  110. "subHappSubInfoButtonLink": "",
  111. "subHappSubExpire": "false",
  112. "subHappSubExpireButtonLink": "",
  113. "subHappNotificationExpire": "false",
  114. "subHappNoLimit": "false",
  115. "subHappAlwaysHwid": "false",
  116. "subHappTunMode": "",
  117. "subHappTunType": "",
  118. "subHappExcludeRoutes": "",
  119. "subHappExcludeApns": "false",
  120. "subHappColorProfile": "",
  121. "subHappPingType": "",
  122. "subHappAutoConnect": "false",
  123. "subHappAutoConnectType": "lowestdelay",
  124. "subHappPerAppMode": "off",
  125. "subHappPerAppList": "",
  126. "subIncyEnableRouting": "false",
  127. "subIncyRoutingRules": "",
  128. "subListen": "",
  129. "subPort": "2096",
  130. "subPath": "/sub/",
  131. "subDomain": "",
  132. "subCertFile": "",
  133. "subKeyFile": "",
  134. "subUpdates": "12",
  135. "subEncrypt": "true",
  136. "subURI": "",
  137. "subJsonPath": "/json/",
  138. "subJsonURI": "",
  139. "subClashEnable": "false",
  140. "subClashPath": "/clash/",
  141. "subClashURI": "",
  142. "subClashEnableRouting": "false",
  143. "subClashRules": "",
  144. "subJsonMux": "",
  145. "subJsonRules": "",
  146. "subJsonRoutingRules": "",
  147. "subJsonDns": "",
  148. "subJsonFinalMask": "",
  149. "subJsonObservatory": "",
  150. "subThemeDir": "",
  151. "datepicker": "gregorian",
  152. "warp": "",
  153. "warpUpdateInterval": "0",
  154. "nord": "",
  155. "pia": "",
  156. "externalTrafficInformEnable": "false",
  157. "externalTrafficInformURI": "",
  158. "restartXrayOnClientDisable": "true",
  159. "xrayOutboundTestUrl": "https://www.google.com/generate_204",
  160. "panelOutbound": "",
  161. "devChannelEnable": "false",
  162. // LDAP defaults
  163. "ldapEnable": "false",
  164. "ldapHost": "",
  165. "ldapPort": "389",
  166. "ldapUseTLS": "false",
  167. "ldapInsecureSkipVerify": "false",
  168. "ldapBindDN": "",
  169. "ldapPassword": "",
  170. "ldapBaseDN": "",
  171. "ldapUserFilter": "(objectClass=person)",
  172. "ldapUserAttr": "mail",
  173. "ldapVlessField": "vless_enabled",
  174. "ldapSyncCron": "@every 1m",
  175. "ldapFlagField": "",
  176. "ldapTruthyValues": "true,1,yes,on",
  177. "ldapInvertFlag": "false",
  178. "ldapInboundTags": "",
  179. "ldapAutoCreate": "false",
  180. "ldapAutoDelete": "false",
  181. "ldapDefaultTotalGB": "0",
  182. "ldapDefaultExpiryDays": "0",
  183. "ldapDefaultLimitIP": "0",
  184. // Event bus — per-subscriber event filtering (empty = all disabled)
  185. "tgEnabledEvents": "login.attempt,cpu.high",
  186. "smtpEnabledEvents": "login.attempt,cpu.high",
  187. "smtpCpu": "80",
  188. "smtpMemory": "80",
  189. // Consecutive failed observatory probes before an outbound.down event fires
  190. "outboundDownThreshold": "3",
  191. // Email (SMTP) notifications
  192. "smtpEnable": "false",
  193. "smtpHost": "",
  194. "smtpPort": "587",
  195. "smtpUsername": "",
  196. "smtpPassword": "",
  197. "smtpFrom": "",
  198. "smtpFromName": "",
  199. "smtpTo": "",
  200. "smtpEncryptionType": "starttls", // no, starttls, tls
  201. // Discord bot notifications
  202. "discordBotEnable": "false",
  203. "discordBotToken": "",
  204. "discordChannelId": "",
  205. "discordAdminIds": "",
  206. "discordRunTime": "@daily",
  207. "discordBotBackup": "false",
  208. "discordCpu": "80",
  209. "discordMemory": "80",
  210. "discordLang": "en-US",
  211. "discordEnabledEvents": "login.attempt,cpu.high",
  212. }
  213. // SettingService provides business logic for application settings management.
  214. // It handles configuration storage, retrieval, and validation for all system settings.
  215. type SettingService struct{}
  216. func (s *SettingService) GetAllSetting() (*entity.AllSetting, error) {
  217. db := database.GetDB()
  218. settings := make([]*model.Setting, 0)
  219. err := db.Model(model.Setting{}).Not("key = ?", "xrayTemplateConfig").Find(&settings).Error
  220. if err != nil {
  221. return nil, err
  222. }
  223. allSetting := &entity.AllSetting{}
  224. t := reflect.TypeFor[entity.AllSetting]()
  225. v := reflect.ValueOf(allSetting).Elem()
  226. fields := reflect_util.GetFields(t)
  227. setSetting := func(key, value string) (err error) {
  228. defer func() {
  229. panicErr := recover()
  230. if panicErr != nil {
  231. err = errors.New(fmt.Sprint(panicErr))
  232. }
  233. }()
  234. var found bool
  235. var field reflect.StructField
  236. for _, f := range fields {
  237. if f.Tag.Get("json") == key {
  238. field = f
  239. found = true
  240. break
  241. }
  242. }
  243. if !found {
  244. // Some settings are automatically generated, no need to return to the front end to modify the user
  245. return nil
  246. }
  247. fieldV := v.FieldByName(field.Name)
  248. switch t := fieldV.Interface().(type) {
  249. case int:
  250. n, err := strconv.ParseInt(effectiveSettingValue(key, value), 10, 64)
  251. if err != nil {
  252. return err
  253. }
  254. fieldV.SetInt(n)
  255. case string:
  256. fieldV.SetString(value)
  257. case bool:
  258. fieldV.SetBool(effectiveSettingValue(key, value) == "true")
  259. default:
  260. return common.NewErrorf("unknown field %v type %v", key, t)
  261. }
  262. return
  263. }
  264. keyMap := map[string]bool{}
  265. for _, setting := range settings {
  266. err := setSetting(setting.Key, setting.Value)
  267. if err != nil {
  268. return nil, err
  269. }
  270. keyMap[setting.Key] = true
  271. }
  272. for key, value := range defaultValueMap {
  273. if keyMap[key] {
  274. continue
  275. }
  276. err := setSetting(key, value)
  277. if err != nil {
  278. return nil, err
  279. }
  280. }
  281. return allSetting, nil
  282. }
  283. func (s *SettingService) GetAllSettingView() (*entity.AllSettingView, error) {
  284. allSetting, err := s.GetAllSetting()
  285. if err != nil {
  286. return nil, err
  287. }
  288. view := &entity.AllSettingView{AllSetting: *allSetting}
  289. view.HasTgBotToken = secretConfigured(allSetting.TgBotToken)
  290. view.HasTwoFactorToken = secretConfigured(allSetting.TwoFactorToken)
  291. view.HasLdapPassword = secretConfigured(allSetting.LdapPassword)
  292. view.HasWarpSecret = secretConfigured(mustString(s.GetWarp()))
  293. view.HasNordSecret = secretConfigured(mustString(s.GetNord()))
  294. view.HasSmtpPassword = secretConfigured(allSetting.SmtpPassword)
  295. view.HasDiscordBotToken = secretConfigured(allSetting.DiscordBotToken)
  296. var apiTokenCount int64
  297. if err := database.GetDB().Model(model.ApiToken{}).Where("enabled = ?", true).Count(&apiTokenCount).Error; err == nil {
  298. view.HasApiToken = apiTokenCount > 0
  299. }
  300. view.TgBotToken = ""
  301. view.TwoFactorToken = ""
  302. view.LdapPassword = ""
  303. view.SmtpPassword = ""
  304. view.DiscordBotToken = ""
  305. return view, nil
  306. }
  307. func secretConfigured(value string) bool {
  308. return strings.TrimSpace(value) != ""
  309. }
  310. func mustString(value string, _ error) string {
  311. return value
  312. }
  313. func getEnv(key, fallback string) string {
  314. val, ok := os.LookupEnv(key)
  315. if !ok {
  316. return fallback
  317. }
  318. val = strings.TrimSpace(val)
  319. if val == "" {
  320. return fallback
  321. }
  322. return val
  323. }
  324. func (s *SettingService) ResetSettings() error {
  325. db := database.GetDB()
  326. return db.Transaction(func(tx *gorm.DB) error {
  327. if err := tx.Where("1 = 1").Delete(model.Setting{}).Error; err != nil {
  328. return err
  329. }
  330. paths := []model.Setting{
  331. {Key: "subPath", Value: "/" + random.NumLower(16) + "/"},
  332. {Key: "subJsonPath", Value: "/" + random.NumLower(16) + "/"},
  333. {Key: "subClashPath", Value: "/" + random.NumLower(16) + "/"},
  334. }
  335. return tx.Create(&paths).Error
  336. })
  337. }
  338. func (s *SettingService) getSetting(key string) (*model.Setting, error) {
  339. db := database.GetDB()
  340. setting := &model.Setting{}
  341. err := db.Model(model.Setting{}).Where("key = ?", key).First(setting).Error
  342. if err != nil {
  343. return nil, err
  344. }
  345. return setting, nil
  346. }
  347. func (s *SettingService) saveSetting(key string, value string) error {
  348. setting, err := s.getSetting(key)
  349. db := database.GetDB()
  350. if database.IsNotFound(err) {
  351. return db.Create(&model.Setting{
  352. Key: key,
  353. Value: value,
  354. }).Error
  355. } else if err != nil {
  356. return err
  357. }
  358. setting.Key = key
  359. setting.Value = value
  360. return db.Save(setting).Error
  361. }
  362. func (s *SettingService) getString(key string) (string, error) {
  363. setting, err := s.getSetting(key)
  364. if database.IsNotFound(err) {
  365. value, ok := defaultValueMap[key]
  366. if !ok {
  367. return "", common.NewErrorf("key <%v> not in defaultValueMap", key)
  368. }
  369. return value, nil
  370. } else if err != nil {
  371. return "", err
  372. }
  373. return setting.Value, nil
  374. }
  375. func (s *SettingService) setString(key string, value string) error {
  376. return s.saveSetting(key, value)
  377. }
  378. func effectiveSettingValue(key, stored string) string {
  379. if stored == "" {
  380. if def, ok := defaultValueMap[key]; ok {
  381. return def
  382. }
  383. }
  384. return stored
  385. }
  386. func (s *SettingService) getBool(key string) (bool, error) {
  387. str, err := s.getString(key)
  388. if err != nil {
  389. return false, err
  390. }
  391. return strconv.ParseBool(effectiveSettingValue(key, str))
  392. }
  393. func (s *SettingService) setBool(key string, value bool) error {
  394. return s.setString(key, strconv.FormatBool(value))
  395. }
  396. func (s *SettingService) getInt(key string) (int, error) {
  397. str, err := s.getString(key)
  398. if err != nil {
  399. return 0, err
  400. }
  401. return strconv.Atoi(effectiveSettingValue(key, str))
  402. }
  403. func (s *SettingService) setInt(key string, value int) error {
  404. return s.setString(key, strconv.Itoa(value))
  405. }
  406. func (s *SettingService) GetWarpLastUpdate() (int64, error) {
  407. setting, err := s.getSetting("warpLastUpdate")
  408. if database.IsNotFound(err) {
  409. return 0, nil
  410. }
  411. if err != nil {
  412. return 0, err
  413. }
  414. if setting.Value == "" {
  415. return 0, nil
  416. }
  417. return strconv.ParseInt(setting.Value, 10, 64)
  418. }
  419. func (s *SettingService) SetWarpLastUpdate(val int64) error {
  420. return s.saveSetting("warpLastUpdate", strconv.FormatInt(val, 10))
  421. }
  422. func (s *SettingService) SetWarpUpdateInterval(val int) error {
  423. return s.setInt("warpUpdateInterval", val)
  424. }
  425. func (s *SettingService) GetXrayConfigTemplate() (string, error) {
  426. return s.getString("xrayTemplateConfig")
  427. }
  428. func (s *SettingService) GetXrayOutboundTestUrl() (string, error) {
  429. return s.getString("xrayOutboundTestUrl")
  430. }
  431. func (s *SettingService) SetXrayOutboundTestUrl(url string) error {
  432. clean, err := SanitizeHTTPURL(url)
  433. if err != nil {
  434. return err
  435. }
  436. return s.setString("xrayOutboundTestUrl", clean)
  437. }
  438. func (s *SettingService) GetListen() (string, error) {
  439. return s.getString("webListen")
  440. }
  441. func (s *SettingService) SetListen(ip string) error {
  442. return s.setString("webListen", ip)
  443. }
  444. func (s *SettingService) GetWebDomain() (string, error) {
  445. return s.getString("webDomain")
  446. }
  447. func (s *SettingService) GetTgBotToken() (string, error) {
  448. return s.getString("tgBotToken")
  449. }
  450. func (s *SettingService) SetTgBotToken(token string) error {
  451. return s.setString("tgBotToken", token)
  452. }
  453. func (s *SettingService) GetTgBotProxy() (string, error) {
  454. return s.getString("tgBotProxy")
  455. }
  456. func (s *SettingService) SetTgBotProxy(token string) error {
  457. return s.setString("tgBotProxy", token)
  458. }
  459. // GetPanelOutbound returns the Xray outbound tag the panel's own outbound
  460. // requests (version checks, Telegram, subscription fetches) are routed through.
  461. func (s *SettingService) GetPanelOutbound() (string, error) {
  462. return s.getString("panelOutbound")
  463. }
  464. func (s *SettingService) SetPanelOutbound(tag string) error {
  465. return s.setString("panelOutbound", tag)
  466. }
  467. // PanelEgressProxyURL resolves the loopback SOCKS bridge that the generated
  468. // config exposes when a panel outbound is configured (see injectPanelEgress).
  469. // It returns "" — meaning a direct connection — when the feature is off or
  470. // the bridge is not present in the running core yet.
  471. func (s *SettingService) PanelEgressProxyURL() string {
  472. tag, err := s.GetPanelOutbound()
  473. if err != nil || tag == "" {
  474. return ""
  475. }
  476. proc := XrayProcess()
  477. if proc == nil || !proc.IsRunning() {
  478. logger.Warning("panel outbound [", tag, "] is set but Xray is not running, using a direct connection")
  479. return ""
  480. }
  481. cfg := proc.GetConfig()
  482. if cfg == nil {
  483. return ""
  484. }
  485. for i := range cfg.InboundConfigs {
  486. if cfg.InboundConfigs[i].Tag == PanelEgressInboundTag {
  487. return fmt.Sprintf("socks5://127.0.0.1:%d", cfg.InboundConfigs[i].Port)
  488. }
  489. }
  490. logger.Warning("panel outbound [", tag, "] is set but the egress bridge is not in the running config, using a direct connection")
  491. return ""
  492. }
  493. func (s *SettingService) NodeEgressProxyURL(nodeID int) string {
  494. tag := NodeEgressInboundTag(nodeID)
  495. proc := XrayProcess()
  496. if proc == nil || !proc.IsRunning() {
  497. logger.Warning("node outbound [", tag, "] is set but Xray is not running, using a direct connection")
  498. return ""
  499. }
  500. cfg := proc.GetConfig()
  501. if cfg == nil {
  502. return ""
  503. }
  504. for i := range cfg.InboundConfigs {
  505. if cfg.InboundConfigs[i].Tag == tag {
  506. return fmt.Sprintf("socks5://127.0.0.1:%d", cfg.InboundConfigs[i].Port)
  507. }
  508. }
  509. logger.Warning("node outbound [", tag, "] is set but the egress bridge is not in the running config, using a direct connection")
  510. return ""
  511. }
  512. // NewProxiedHTTPClient returns an HTTP client that routes the panel's own
  513. // outbound requests through the configured panel outbound (via the loopback
  514. // SOCKS bridge in the running Xray). When the feature is off or the bridge
  515. // is unavailable it falls back to a direct client.
  516. func (s *SettingService) NewProxiedHTTPClient(timeout time.Duration) *http.Client {
  517. proxyUrl := s.PanelEgressProxyURL()
  518. client, err := netproxy.NewHTTPClient(proxyUrl, timeout)
  519. if err != nil {
  520. logger.Warningf("Invalid panel egress proxy %q, using direct connection: %v", proxyUrl, err)
  521. return &http.Client{Timeout: timeout}
  522. }
  523. return client
  524. }
  525. func (s *SettingService) GetTgBotAPIServer() (string, error) {
  526. return s.getString("tgBotAPIServer")
  527. }
  528. func (s *SettingService) SetTgBotAPIServer(token string) error {
  529. return s.setString("tgBotAPIServer", token)
  530. }
  531. func (s *SettingService) GetTgBotChatId() (string, error) {
  532. return s.getString("tgBotChatId")
  533. }
  534. func (s *SettingService) SetTgBotChatId(chatIds string) error {
  535. return s.setString("tgBotChatId", chatIds)
  536. }
  537. func (s *SettingService) GetTgbotEnabled() (bool, error) {
  538. return s.getBool("tgBotEnable")
  539. }
  540. func (s *SettingService) SetTgbotEnabled(value bool) error {
  541. return s.setBool("tgBotEnable", value)
  542. }
  543. func (s *SettingService) GetTgbotRuntime() (string, error) {
  544. return s.getString("tgRunTime")
  545. }
  546. func (s *SettingService) SetTgbotRuntime(time string) error {
  547. return s.setString("tgRunTime", time)
  548. }
  549. func (s *SettingService) GetTgBotBackup() (bool, error) {
  550. return s.getBool("tgBotBackup")
  551. }
  552. func (s *SettingService) GetTgCpu() (int, error) {
  553. return s.getInt("tgCpu")
  554. }
  555. func (s *SettingService) GetTgMemory() (int, error) {
  556. return s.getInt("tgMemory")
  557. }
  558. func (s *SettingService) SetTgMemory(value int) error {
  559. return s.setInt("tgMemory", value)
  560. }
  561. func (s *SettingService) GetTgLang() (string, error) {
  562. return s.getString("tgLang")
  563. }
  564. func (s *SettingService) GetTwoFactorEnable() (bool, error) {
  565. return s.getBool("twoFactorEnable")
  566. }
  567. func (s *SettingService) SetTwoFactorEnable(value bool) error {
  568. return s.setBool("twoFactorEnable", value)
  569. }
  570. func (s *SettingService) GetTwoFactorToken() (string, error) {
  571. return s.getString("twoFactorToken")
  572. }
  573. func (s *SettingService) SetTwoFactorToken(value string) error {
  574. return s.setString("twoFactorToken", value)
  575. }
  576. func (s *SettingService) VerifyTwoFactorCode(code string) error {
  577. enabled, err := s.GetTwoFactorEnable()
  578. if err != nil {
  579. return err
  580. }
  581. if !enabled {
  582. return nil
  583. }
  584. token, err := s.GetTwoFactorToken()
  585. if err != nil {
  586. return err
  587. }
  588. if strings.TrimSpace(token) == "" || !gotp.NewDefaultTOTP(token).Verify(strings.TrimSpace(code), time.Now().Unix()) {
  589. return common.NewError("invalid two factor code")
  590. }
  591. return nil
  592. }
  593. func (s *SettingService) GetPort() (int, error) {
  594. return s.getInt("webPort")
  595. }
  596. func (s *SettingService) SetPort(port int) error {
  597. return s.setInt("webPort", port)
  598. }
  599. func (s *SettingService) SetCertFile(webCertFile string) error {
  600. return s.setString("webCertFile", webCertFile)
  601. }
  602. func (s *SettingService) GetCertFile() (string, error) {
  603. return s.getString("webCertFile")
  604. }
  605. func (s *SettingService) SetKeyFile(webKeyFile string) error {
  606. return s.setString("webKeyFile", webKeyFile)
  607. }
  608. func (s *SettingService) GetKeyFile() (string, error) {
  609. return s.getString("webKeyFile")
  610. }
  611. func (s *SettingService) GetExpireDiff() (int, error) {
  612. return s.getInt("expireDiff")
  613. }
  614. func (s *SettingService) GetTrafficDiff() (int, error) {
  615. return s.getInt("trafficDiff")
  616. }
  617. func (s *SettingService) GetSessionMaxAge() (int, error) {
  618. return s.getInt("sessionMaxAge")
  619. }
  620. // GetIpLimitAllowlist returns the operator's trusted addresses and networks,
  621. // which the IP limit neither counts nor bans.
  622. func (s *SettingService) GetIpLimitAllowlist() (string, error) {
  623. return s.getString("ipLimitAllowlist")
  624. }
  625. func (s *SettingService) GetTrustedProxyCIDRs() (string, error) {
  626. return s.getString("trustedProxyCIDRs")
  627. }
  628. func (s *SettingService) GetRealityScanCandidates() (string, error) {
  629. return s.getString("realityScanCandidates")
  630. }
  631. func (s *SettingService) GetRemarkTemplate() (string, error) {
  632. return s.getString("remarkTemplate")
  633. }
  634. func (s *SettingService) GetSubShowIdentityOnAllLinks() (bool, error) {
  635. return s.getBool("subShowIdentityOnAllLinks")
  636. }
  637. func (s *SettingService) GetSubInfoNodeEnable() (bool, error) {
  638. return s.getBool("subInfoNodeEnable")
  639. }
  640. func (s *SettingService) GetSubCalendarExpireInclusive() (bool, error) {
  641. return s.getBool("subCalendarExpireInclusive")
  642. }
  643. func (s *SettingService) GetSubExpiredTemplate() (string, error) {
  644. return s.getString("subExpiredTemplate")
  645. }
  646. func (s *SettingService) GetSubTrafficDepletedTemplate() (string, error) {
  647. return s.getString("subTrafficDepletedTemplate")
  648. }
  649. func (s *SettingService) GetSecret() ([]byte, error) {
  650. secret, err := s.getString("secret")
  651. if secret == "" || secret == defaultValueMap["secret"] {
  652. if secret == "" {
  653. secret = defaultValueMap["secret"]
  654. }
  655. saveErr := s.saveSetting("secret", secret)
  656. if saveErr != nil {
  657. logger.Warning("save secret failed:", saveErr)
  658. }
  659. }
  660. return []byte(secret), err
  661. }
  662. // GetPanelGuid returns this panel's stable self-identifier, persisting a
  663. // freshly generated UUID on first read. It is the globally stable node
  664. // identity used to attribute online clients and inbounds to the physical
  665. // node that hosts them across a chain of nodes (#4983), where per-panel
  666. // autoincrement node ids are meaningless one hop away.
  667. func (s *SettingService) GetPanelGuid() (string, error) {
  668. guid, err := s.getString("panelGuid")
  669. if err != nil {
  670. return "", err
  671. }
  672. if guid == defaultValueMap["panelGuid"] {
  673. if saveErr := s.saveSetting("panelGuid", guid); saveErr != nil {
  674. logger.Warning("save panelGuid failed:", saveErr)
  675. }
  676. }
  677. return guid, nil
  678. }
  679. func (s *SettingService) SetBasePath(basePath string) error {
  680. if !strings.HasPrefix(basePath, "/") {
  681. basePath = "/" + basePath
  682. }
  683. if !strings.HasSuffix(basePath, "/") {
  684. basePath += "/"
  685. }
  686. return s.setString("webBasePath", basePath)
  687. }
  688. func (s *SettingService) GetBasePath() (string, error) {
  689. basePath, err := s.getString("webBasePath")
  690. if err != nil {
  691. return "", err
  692. }
  693. return normalizeBasePath(basePath), nil
  694. }
  695. func (s *SettingService) GetTimeLocation() (*time.Location, error) {
  696. l, err := s.getString("timeLocation")
  697. if err != nil {
  698. return nil, err
  699. }
  700. location, err := time.LoadLocation(l)
  701. if err != nil {
  702. defaultLocation := defaultValueMap["timeLocation"]
  703. logger.Errorf("location <%v> not exist, using default location: %v", l, defaultLocation)
  704. location, err = time.LoadLocation(defaultLocation)
  705. if err != nil {
  706. logger.Errorf("failed to load default location, using UTC: %v", err)
  707. return time.UTC, nil
  708. }
  709. return location, nil
  710. }
  711. return location, nil
  712. }
  713. func (s *SettingService) GetSubEnable() (bool, error) {
  714. return s.getBool("subEnable")
  715. }
  716. func (s *SettingService) GetHappLinkEnable() (bool, error) {
  717. return s.getBool("happLinkEnable")
  718. }
  719. func (s *SettingService) GetSubJsonEnable() (bool, error) {
  720. return s.getBool("subJsonEnable")
  721. }
  722. func (s *SettingService) GetSubJsonAutoDetect() (bool, error) {
  723. return s.getBool("subJsonAutoDetect")
  724. }
  725. func (s *SettingService) GetSubJsonAlwaysArray() (bool, error) {
  726. return s.getBool("subJsonAlwaysArray")
  727. }
  728. func (s *SettingService) GetSubJsonUserAgentRegex() (string, error) {
  729. return s.getString("subJsonUserAgentRegex")
  730. }
  731. func (s *SettingService) GetSubClashAutoDetect() (bool, error) {
  732. return s.getBool("subClashAutoDetect")
  733. }
  734. func (s *SettingService) GetSubClashUserAgentRegex() (string, error) {
  735. return s.getString("subClashUserAgentRegex")
  736. }
  737. func (s *SettingService) GetSubTitle() (string, error) {
  738. return s.getString("subTitle")
  739. }
  740. func (s *SettingService) GetSubSupportUrl() (string, error) {
  741. value, err := s.getString("subSupportUrl")
  742. return common.EnsureURLScheme(value), err
  743. }
  744. func (s *SettingService) GetSubProfileUrl() (string, error) {
  745. value, err := s.getString("subProfileUrl")
  746. return common.EnsureURLScheme(value), err
  747. }
  748. func (s *SettingService) GetSubAnnounce() (string, error) {
  749. return s.getString("subAnnounce")
  750. }
  751. func (s *SettingService) GetSubEnableRouting() (bool, error) {
  752. return s.getBool("subEnableRouting")
  753. }
  754. func (s *SettingService) GetSubRoutingRules() (string, error) {
  755. return s.getString("subRoutingRules")
  756. }
  757. func (s *SettingService) GetSubHideSettings() (bool, error) {
  758. return s.getBool("subHideSettings")
  759. }
  760. func (s *SettingService) GetSubHappAutoDetect() (bool, error) {
  761. return s.getBool("subHappAutoDetect")
  762. }
  763. func (s *SettingService) GetSubHappProviderId() (string, error) {
  764. return s.getString("subHappProviderId")
  765. }
  766. func (s *SettingService) GetSubHappNewUrl() (string, error) {
  767. return s.getString("subHappNewUrl")
  768. }
  769. func (s *SettingService) GetSubHappFallbackUrl() (string, error) {
  770. return s.getString("subHappFallbackUrl")
  771. }
  772. func (s *SettingService) GetSubHappSubInfoColor() (string, error) {
  773. return s.getString("subHappSubInfoColor")
  774. }
  775. func (s *SettingService) GetSubHappSubInfoText() (string, error) {
  776. return s.getString("subHappSubInfoText")
  777. }
  778. func (s *SettingService) GetSubHappSubInfoButtonText() (string, error) {
  779. return s.getString("subHappSubInfoButtonText")
  780. }
  781. func (s *SettingService) GetSubHappSubInfoButtonLink() (string, error) {
  782. return s.getString("subHappSubInfoButtonLink")
  783. }
  784. func (s *SettingService) GetSubHappSubExpire() (bool, error) {
  785. return s.getBool("subHappSubExpire")
  786. }
  787. func (s *SettingService) GetSubHappSubExpireButtonLink() (string, error) {
  788. return s.getString("subHappSubExpireButtonLink")
  789. }
  790. func (s *SettingService) GetSubHappNotificationExpire() (bool, error) {
  791. return s.getBool("subHappNotificationExpire")
  792. }
  793. func (s *SettingService) GetSubHappNoLimit() (bool, error) {
  794. return s.getBool("subHappNoLimit")
  795. }
  796. func (s *SettingService) GetSubHappAlwaysHwid() (bool, error) {
  797. return s.getBool("subHappAlwaysHwid")
  798. }
  799. func (s *SettingService) GetSubHappTunMode() (string, error) {
  800. return s.getString("subHappTunMode")
  801. }
  802. func (s *SettingService) GetSubHappTunType() (string, error) {
  803. return s.getString("subHappTunType")
  804. }
  805. func (s *SettingService) GetSubHappExcludeRoutes() (string, error) {
  806. return s.getString("subHappExcludeRoutes")
  807. }
  808. func (s *SettingService) GetSubHappExcludeApns() (bool, error) {
  809. return s.getBool("subHappExcludeApns")
  810. }
  811. func (s *SettingService) GetSubHappColorProfile() (string, error) {
  812. return s.getString("subHappColorProfile")
  813. }
  814. func (s *SettingService) GetSubHappPingType() (string, error) {
  815. return s.getString("subHappPingType")
  816. }
  817. func (s *SettingService) GetSubHappAutoConnect() (bool, error) {
  818. return s.getBool("subHappAutoConnect")
  819. }
  820. func (s *SettingService) GetSubHappAutoConnectType() (string, error) {
  821. return s.getString("subHappAutoConnectType")
  822. }
  823. func (s *SettingService) GetSubHappPerAppMode() (string, error) {
  824. return s.getString("subHappPerAppMode")
  825. }
  826. func (s *SettingService) GetSubHappPerAppList() (string, error) {
  827. return s.getString("subHappPerAppList")
  828. }
  829. func (s *SettingService) GetSubIncyEnableRouting() (bool, error) {
  830. return s.getBool("subIncyEnableRouting")
  831. }
  832. func (s *SettingService) GetSubIncyRoutingRules() (string, error) {
  833. return s.getString("subIncyRoutingRules")
  834. }
  835. func (s *SettingService) GetSubListen() (string, error) {
  836. return s.getString("subListen")
  837. }
  838. func (s *SettingService) GetSubPort() (int, error) {
  839. return s.getInt("subPort")
  840. }
  841. func (s *SettingService) GetSubPath() (string, error) {
  842. return s.getString("subPath")
  843. }
  844. func (s *SettingService) GetSubJsonPath() (string, error) {
  845. return s.getString("subJsonPath")
  846. }
  847. func (s *SettingService) GetSubDomain() (string, error) {
  848. return s.getString("subDomain")
  849. }
  850. func (s *SettingService) SetSubCertFile(subCertFile string) error {
  851. return s.setString("subCertFile", subCertFile)
  852. }
  853. func (s *SettingService) GetSubCertFile() (string, error) {
  854. return s.getString("subCertFile")
  855. }
  856. func (s *SettingService) SetSubKeyFile(subKeyFile string) error {
  857. return s.setString("subKeyFile", subKeyFile)
  858. }
  859. func (s *SettingService) GetSubKeyFile() (string, error) {
  860. return s.getString("subKeyFile")
  861. }
  862. func (s *SettingService) GetSubUpdates() (string, error) {
  863. return s.getString("subUpdates")
  864. }
  865. func (s *SettingService) GetSubEncrypt() (bool, error) {
  866. return s.getBool("subEncrypt")
  867. }
  868. func (s *SettingService) GetPageSize() (int, error) {
  869. return s.getInt("pageSize")
  870. }
  871. func (s *SettingService) GetSubURI() (string, error) {
  872. return s.getString("subURI")
  873. }
  874. func (s *SettingService) GetSubJsonURI() (string, error) {
  875. return s.getString("subJsonURI")
  876. }
  877. func (s *SettingService) GetSubClashEnable() (bool, error) {
  878. return s.getBool("subClashEnable")
  879. }
  880. func (s *SettingService) GetSubClashPath() (string, error) {
  881. return s.getString("subClashPath")
  882. }
  883. func (s *SettingService) GetSubClashURI() (string, error) {
  884. return s.getString("subClashURI")
  885. }
  886. func (s *SettingService) GetSubClashEnableRouting() (bool, error) {
  887. return s.getBool("subClashEnableRouting")
  888. }
  889. func (s *SettingService) GetSubClashRules() (string, error) {
  890. return s.getString("subClashRules")
  891. }
  892. func (s *SettingService) GetSubJsonMux() (string, error) {
  893. return s.getString("subJsonMux")
  894. }
  895. func (s *SettingService) GetSubJsonRules() (string, error) {
  896. return s.getString("subJsonRules")
  897. }
  898. func (s *SettingService) GetSubJsonRoutingRules() (string, error) {
  899. return s.getString("subJsonRoutingRules")
  900. }
  901. func (s *SettingService) GetSubJsonDns() (string, error) {
  902. return s.getString("subJsonDns")
  903. }
  904. func (s *SettingService) GetSubJsonFinalMask() (string, error) {
  905. return s.getString("subJsonFinalMask")
  906. }
  907. func (s *SettingService) GetSubJsonObservatory() (string, error) {
  908. return s.getString("subJsonObservatory")
  909. }
  910. func (s *SettingService) GetSubThemeDir() (string, error) {
  911. return s.getString("subThemeDir")
  912. }
  913. func (s *SettingService) GetDatepicker() (string, error) {
  914. return s.getString("datepicker")
  915. }
  916. func (s *SettingService) GetWarp() (string, error) {
  917. return s.getString("warp")
  918. }
  919. func (s *SettingService) SetWarp(data string) error {
  920. return s.setString("warp", data)
  921. }
  922. func (s *SettingService) GetNord() (string, error) {
  923. return s.getString("nord")
  924. }
  925. func (s *SettingService) SetNord(data string) error {
  926. return s.setString("nord", data)
  927. }
  928. func (s *SettingService) GetPia() (string, error) {
  929. return s.getString("pia")
  930. }
  931. func (s *SettingService) SetPia(data string) error {
  932. return s.setString("pia", data)
  933. }
  934. func (s *SettingService) GetExternalTrafficInformEnable() (bool, error) {
  935. return s.getBool("externalTrafficInformEnable")
  936. }
  937. func (s *SettingService) SetExternalTrafficInformEnable(value bool) error {
  938. return s.setBool("externalTrafficInformEnable", value)
  939. }
  940. func (s *SettingService) GetExternalTrafficInformURI() (string, error) {
  941. return s.getString("externalTrafficInformURI")
  942. }
  943. func (s *SettingService) SetExternalTrafficInformURI(InformURI string) error {
  944. return s.setString("externalTrafficInformURI", InformURI)
  945. }
  946. func (s *SettingService) GetRestartXrayOnClientDisable() (bool, error) {
  947. return s.getBool("restartXrayOnClientDisable")
  948. }
  949. func (s *SettingService) SetRestartXrayOnClientDisable(value bool) error {
  950. return s.setBool("restartXrayOnClientDisable", value)
  951. }
  952. // GetDevChannelEnable reports whether the panel self-update tracks the rolling
  953. // per-commit dev release instead of the latest stable tag.
  954. func (s *SettingService) GetDevChannelEnable() (bool, error) {
  955. return s.getBool("devChannelEnable")
  956. }
  957. func (s *SettingService) SetDevChannelEnable(value bool) error {
  958. return s.setBool("devChannelEnable", value)
  959. }
  960. // GetIpLimitEnable reports whether the IP-limit feature is available. Always
  961. // true since the panel enforces limits via the core's online-stats API; on an
  962. // older core the job falls back to access-log parsing and warns there when the
  963. // log is missing, so the UI no longer hides the field behind that condition.
  964. func (s *SettingService) GetIpLimitEnable() (bool, error) {
  965. return true, nil
  966. }
  967. // GetAccessLogEnable reports whether an Xray access log is configured. Used by
  968. // the UI for features that genuinely read the log file (the xray log viewer) —
  969. // distinct from IP limiting, which works without it.
  970. func (s *SettingService) GetAccessLogEnable() (bool, error) {
  971. accessLogPath, err := xray.GetAccessLogPath()
  972. if err != nil {
  973. return false, err
  974. }
  975. return (accessLogPath != "none" && accessLogPath != ""), nil
  976. }
  977. // GetLdapEnable returns whether LDAP is enabled.
  978. func (s *SettingService) GetLdapEnable() (bool, error) {
  979. return s.getBool("ldapEnable")
  980. }
  981. func (s *SettingService) GetLdapHost() (string, error) {
  982. return s.getString("ldapHost")
  983. }
  984. func (s *SettingService) GetLdapPort() (int, error) {
  985. return s.getInt("ldapPort")
  986. }
  987. func (s *SettingService) GetLdapUseTLS() (bool, error) {
  988. return s.getBool("ldapUseTLS")
  989. }
  990. func (s *SettingService) GetLdapInsecureSkipVerify() (bool, error) {
  991. return s.getBool("ldapInsecureSkipVerify")
  992. }
  993. func (s *SettingService) GetLdapBindDN() (string, error) {
  994. return s.getString("ldapBindDN")
  995. }
  996. func (s *SettingService) GetLdapPassword() (string, error) {
  997. return s.getString("ldapPassword")
  998. }
  999. func (s *SettingService) GetLdapBaseDN() (string, error) {
  1000. return s.getString("ldapBaseDN")
  1001. }
  1002. func (s *SettingService) GetLdapUserFilter() (string, error) {
  1003. return s.getString("ldapUserFilter")
  1004. }
  1005. func (s *SettingService) GetLdapUserAttr() (string, error) {
  1006. return s.getString("ldapUserAttr")
  1007. }
  1008. func (s *SettingService) GetLdapVlessField() (string, error) {
  1009. return s.getString("ldapVlessField")
  1010. }
  1011. func (s *SettingService) GetLdapSyncCron() (string, error) {
  1012. return s.getString("ldapSyncCron")
  1013. }
  1014. func (s *SettingService) GetLdapFlagField() (string, error) {
  1015. return s.getString("ldapFlagField")
  1016. }
  1017. func (s *SettingService) GetLdapTruthyValues() (string, error) {
  1018. return s.getString("ldapTruthyValues")
  1019. }
  1020. func (s *SettingService) GetLdapInvertFlag() (bool, error) {
  1021. return s.getBool("ldapInvertFlag")
  1022. }
  1023. func (s *SettingService) GetLdapInboundTags() (string, error) {
  1024. return s.getString("ldapInboundTags")
  1025. }
  1026. func (s *SettingService) GetLdapAutoCreate() (bool, error) {
  1027. return s.getBool("ldapAutoCreate")
  1028. }
  1029. func (s *SettingService) GetLdapAutoDelete() (bool, error) {
  1030. return s.getBool("ldapAutoDelete")
  1031. }
  1032. func (s *SettingService) GetLdapDefaultTotalGB() (int, error) {
  1033. return s.getInt("ldapDefaultTotalGB")
  1034. }
  1035. func (s *SettingService) GetLdapDefaultExpiryDays() (int, error) {
  1036. return s.getInt("ldapDefaultExpiryDays")
  1037. }
  1038. func (s *SettingService) GetLdapDefaultLimitIP() (int, error) {
  1039. return s.getInt("ldapDefaultLimitIP")
  1040. }
  1041. // Event bus — per-subscriber event filtering
  1042. func (s *SettingService) GetTgEnabledEvents() (string, error) {
  1043. return s.getString("tgEnabledEvents")
  1044. }
  1045. func (s *SettingService) SetTgEnabledEvents(events string) error {
  1046. return s.setString("tgEnabledEvents", events)
  1047. }
  1048. func (s *SettingService) GetSmtpEnabledEvents() (string, error) {
  1049. return s.getString("smtpEnabledEvents")
  1050. }
  1051. func (s *SettingService) SetSmtpEnabledEvents(events string) error {
  1052. return s.setString("smtpEnabledEvents", events)
  1053. }
  1054. // Email (SMTP) settings
  1055. func (s *SettingService) GetSmtpEnable() (bool, error) {
  1056. return s.getBool("smtpEnable")
  1057. }
  1058. func (s *SettingService) SetSmtpEnable(value bool) error {
  1059. return s.setBool("smtpEnable", value)
  1060. }
  1061. func (s *SettingService) GetSmtpHost() (string, error) {
  1062. return s.getString("smtpHost")
  1063. }
  1064. func (s *SettingService) SetSmtpHost(value string) error {
  1065. return s.setString("smtpHost", value)
  1066. }
  1067. func (s *SettingService) GetSmtpPort() (int, error) {
  1068. return s.getInt("smtpPort")
  1069. }
  1070. func (s *SettingService) SetSmtpPort(value int) error {
  1071. return s.setInt("smtpPort", value)
  1072. }
  1073. func (s *SettingService) GetSmtpUsername() (string, error) {
  1074. return s.getString("smtpUsername")
  1075. }
  1076. func (s *SettingService) SetSmtpUsername(value string) error {
  1077. return s.setString("smtpUsername", value)
  1078. }
  1079. func (s *SettingService) GetSmtpFrom() (string, error) {
  1080. return s.getString("smtpFrom")
  1081. }
  1082. func (s *SettingService) SetSmtpFrom(value string) error {
  1083. return s.setString("smtpFrom", value)
  1084. }
  1085. func (s *SettingService) GetSmtpFromName() (string, error) {
  1086. return s.getString("smtpFromName")
  1087. }
  1088. func (s *SettingService) SetSmtpFromName(value string) error {
  1089. return s.setString("smtpFromName", value)
  1090. }
  1091. func (s *SettingService) GetSmtpPassword() (string, error) {
  1092. return s.getString("smtpPassword")
  1093. }
  1094. func (s *SettingService) SetSmtpPassword(value string) error {
  1095. return s.setString("smtpPassword", value)
  1096. }
  1097. func (s *SettingService) GetSmtpTo() (string, error) {
  1098. return s.getString("smtpTo")
  1099. }
  1100. func (s *SettingService) SetSmtpTo(value string) error {
  1101. return s.setString("smtpTo", value)
  1102. }
  1103. func (s *SettingService) GetSmtpEncryptionType() (string, error) {
  1104. return s.getString("smtpEncryptionType")
  1105. }
  1106. func (s *SettingService) SetSmtpEncryptionType(value string) error {
  1107. return s.setString("smtpEncryptionType", value)
  1108. }
  1109. func (s *SettingService) GetSmtpCpu() (int, error) {
  1110. return s.getInt("smtpCpu")
  1111. }
  1112. func (s *SettingService) SetSmtpCpu(value int) error {
  1113. return s.setInt("smtpCpu", value)
  1114. }
  1115. func (s *SettingService) GetSmtpMemory() (int, error) {
  1116. return s.getInt("smtpMemory")
  1117. }
  1118. func (s *SettingService) SetSmtpMemory(value int) error {
  1119. return s.setInt("smtpMemory", value)
  1120. }
  1121. // Discord bot settings
  1122. func (s *SettingService) GetDiscordBotEnable() (bool, error) {
  1123. return s.getBool("discordBotEnable")
  1124. }
  1125. func (s *SettingService) SetDiscordBotEnable(value bool) error {
  1126. return s.setBool("discordBotEnable", value)
  1127. }
  1128. func (s *SettingService) GetDiscordBotToken() (string, error) {
  1129. return s.getString("discordBotToken")
  1130. }
  1131. func (s *SettingService) SetDiscordBotToken(value string) error {
  1132. return s.setString("discordBotToken", value)
  1133. }
  1134. func (s *SettingService) GetDiscordChannelId() (string, error) {
  1135. return s.getString("discordChannelId")
  1136. }
  1137. func (s *SettingService) SetDiscordChannelId(value string) error {
  1138. return s.setString("discordChannelId", value)
  1139. }
  1140. func (s *SettingService) GetDiscordAdminIds() (string, error) {
  1141. return s.getString("discordAdminIds")
  1142. }
  1143. func (s *SettingService) SetDiscordAdminIds(value string) error {
  1144. return s.setString("discordAdminIds", value)
  1145. }
  1146. func (s *SettingService) GetDiscordEnabledEvents() (string, error) {
  1147. return s.getString("discordEnabledEvents")
  1148. }
  1149. func (s *SettingService) SetDiscordEnabledEvents(events string) error {
  1150. return s.setString("discordEnabledEvents", events)
  1151. }
  1152. func (s *SettingService) GetDiscordCpu() (int, error) {
  1153. return s.getInt("discordCpu")
  1154. }
  1155. func (s *SettingService) SetDiscordCpu(value int) error {
  1156. return s.setInt("discordCpu", value)
  1157. }
  1158. func (s *SettingService) GetDiscordMemory() (int, error) {
  1159. return s.getInt("discordMemory")
  1160. }
  1161. func (s *SettingService) SetDiscordMemory(value int) error {
  1162. return s.setInt("discordMemory", value)
  1163. }
  1164. func (s *SettingService) GetDiscordRunTime() (string, error) {
  1165. return s.getString("discordRunTime")
  1166. }
  1167. func (s *SettingService) SetDiscordRunTime(value string) error {
  1168. return s.setString("discordRunTime", value)
  1169. }
  1170. func (s *SettingService) GetDiscordBotBackup() (bool, error) {
  1171. return s.getBool("discordBotBackup")
  1172. }
  1173. func (s *SettingService) SetDiscordBotBackup(value bool) error {
  1174. return s.setBool("discordBotBackup", value)
  1175. }
  1176. func (s *SettingService) GetDiscordLang() (string, error) {
  1177. return s.getString("discordLang")
  1178. }
  1179. func (s *SettingService) SetDiscordLang(value string) error {
  1180. return s.setString("discordLang", value)
  1181. }
  1182. // GetOutboundDownThreshold returns how many consecutive failed observatory
  1183. // probes an outbound must accumulate before an outbound.down notification is
  1184. // emitted. 1 preserves the legacy "notify on the first failed probe" behaviour.
  1185. func (s *SettingService) GetOutboundDownThreshold() (int, error) {
  1186. return s.getInt("outboundDownThreshold")
  1187. }
  1188. func (s *SettingService) SetOutboundDownThreshold(value int) error {
  1189. return s.setInt("outboundDownThreshold", value)
  1190. }
  1191. // SecretClears marks redacted secrets the user explicitly emptied. Without a
  1192. // flag, a blank submitted secret means "unchanged" (the field is always served
  1193. // blank to the browser) and the stored value is preserved.
  1194. type SecretClears struct {
  1195. TgBotToken bool
  1196. LdapPassword bool
  1197. SmtpPassword bool
  1198. DiscordBotToken bool
  1199. }
  1200. func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting, clears SecretClears) error {
  1201. if err := s.preserveRedactedSecrets(allSetting, clears); err != nil {
  1202. return err
  1203. }
  1204. if err := validateSettingsURLs(allSetting); err != nil {
  1205. return err
  1206. }
  1207. if err := validateSubUserAgentRegexes(allSetting); err != nil {
  1208. return err
  1209. }
  1210. if err := validateSubJsonDnsSetting(allSetting); err != nil {
  1211. return err
  1212. }
  1213. if err := allSetting.CheckValid(); err != nil {
  1214. return err
  1215. }
  1216. v := reflect.ValueOf(allSetting).Elem()
  1217. t := reflect.TypeFor[entity.AllSetting]()
  1218. fields := reflect_util.GetFields(t)
  1219. db := database.GetDB()
  1220. return db.Transaction(func(tx *gorm.DB) error {
  1221. var existing []*model.Setting
  1222. if err := tx.Find(&existing).Error; err != nil {
  1223. return err
  1224. }
  1225. byKey := make(map[string]*model.Setting, len(existing))
  1226. for _, st := range existing {
  1227. byKey[st.Key] = st
  1228. }
  1229. for _, field := range fields {
  1230. key := field.Tag.Get("json")
  1231. fieldV := v.FieldByName(field.Name)
  1232. value := fmt.Sprint(fieldV.Interface())
  1233. if st, ok := byKey[key]; ok {
  1234. if st.Value == value {
  1235. continue
  1236. }
  1237. st.Value = value
  1238. if err := tx.Save(st).Error; err != nil {
  1239. return err
  1240. }
  1241. continue
  1242. }
  1243. if err := tx.Create(&model.Setting{Key: key, Value: value}).Error; err != nil {
  1244. return err
  1245. }
  1246. }
  1247. return nil
  1248. })
  1249. }
  1250. func validateSubUserAgentRegexes(allSetting *entity.AllSetting) error {
  1251. jsonPattern, err := validateSubUserAgentRegex("Xray JSON", allSetting.SubJsonUserAgentRegex, DefaultSubJsonUserAgentRegex)
  1252. if err != nil {
  1253. return err
  1254. }
  1255. clashPattern, err := validateSubUserAgentRegex("Clash/Mihomo", allSetting.SubClashUserAgentRegex, DefaultSubClashUserAgentRegex)
  1256. if err != nil {
  1257. return err
  1258. }
  1259. allSetting.SubJsonUserAgentRegex = jsonPattern
  1260. allSetting.SubClashUserAgentRegex = clashPattern
  1261. return nil
  1262. }
  1263. func validateSubUserAgentRegex(name, pattern, defaultPattern string) (string, error) {
  1264. pattern = strings.TrimSpace(pattern)
  1265. effectivePattern := pattern
  1266. if effectivePattern == "" {
  1267. effectivePattern = defaultPattern
  1268. }
  1269. if len(effectivePattern) > maxRegexLength {
  1270. return "", common.NewErrorf("%s User-Agent regex must not exceed %d characters", name, maxRegexLength)
  1271. }
  1272. if _, err := regexp.Compile(effectivePattern); err != nil {
  1273. return "", common.NewErrorf("%s User-Agent regex is invalid: %v", name, err)
  1274. }
  1275. // Return the original pattern (empty string if cleared) so the caller
  1276. // can distinguish "user explicitly set empty" from "user set a value".
  1277. // The empty value is stored in the DB and inherited as runtime default.
  1278. return pattern, nil
  1279. }
  1280. func ValidateRegex(pattern string) error {
  1281. if len(pattern) > maxRegexLength {
  1282. return common.NewErrorf("Regular expression must not exceed %d characters", maxRegexLength)
  1283. }
  1284. if _, err := regexp.Compile(pattern); err != nil {
  1285. return common.NewError("Regular expression is invalid:", err)
  1286. }
  1287. return nil
  1288. }
  1289. func (s *SettingService) preserveRedactedSecrets(allSetting *entity.AllSetting, clears SecretClears) error {
  1290. if !clears.TgBotToken && strings.TrimSpace(allSetting.TgBotToken) == "" {
  1291. value, err := s.GetTgBotToken()
  1292. if err != nil {
  1293. return err
  1294. }
  1295. allSetting.TgBotToken = value
  1296. }
  1297. if !clears.LdapPassword && strings.TrimSpace(allSetting.LdapPassword) == "" {
  1298. value, err := s.GetLdapPassword()
  1299. if err != nil {
  1300. return err
  1301. }
  1302. allSetting.LdapPassword = value
  1303. }
  1304. if allSetting.TwoFactorEnable && strings.TrimSpace(allSetting.TwoFactorToken) == "" {
  1305. value, err := s.GetTwoFactorToken()
  1306. if err != nil {
  1307. return err
  1308. }
  1309. allSetting.TwoFactorToken = value
  1310. }
  1311. if !clears.SmtpPassword && strings.TrimSpace(allSetting.SmtpPassword) == "" {
  1312. value, err := s.GetSmtpPassword()
  1313. if err != nil {
  1314. return err
  1315. }
  1316. allSetting.SmtpPassword = value
  1317. }
  1318. if !clears.DiscordBotToken && strings.TrimSpace(allSetting.DiscordBotToken) == "" {
  1319. value, err := s.GetDiscordBotToken()
  1320. if err != nil {
  1321. return err
  1322. }
  1323. allSetting.DiscordBotToken = value
  1324. }
  1325. return nil
  1326. }
  1327. func validateSettingsURLs(allSetting *entity.AllSetting) error {
  1328. if allSetting.ExternalTrafficInformURI != "" {
  1329. u, err := SanitizeHTTPURL(allSetting.ExternalTrafficInformURI)
  1330. if err != nil {
  1331. return common.NewError("external traffic inform URI is invalid:", err)
  1332. }
  1333. allSetting.ExternalTrafficInformURI = u
  1334. }
  1335. if allSetting.TgBotAPIServer != "" {
  1336. u, err := SanitizeHTTPURL(allSetting.TgBotAPIServer)
  1337. if err != nil {
  1338. return common.NewError("telegram API server URL is invalid:", err)
  1339. }
  1340. allSetting.TgBotAPIServer = u
  1341. }
  1342. // Support/profile links land in subscription headers and page data, where
  1343. // client apps resolve a scheme-less value against the panel's own domain.
  1344. // Non-http schemes (tg://, mailto:) are legitimate here, so only default
  1345. // the scheme instead of forcing SanitizeHTTPURL's http(s)-only rule.
  1346. allSetting.SubSupportUrl = common.EnsureURLScheme(allSetting.SubSupportUrl)
  1347. allSetting.SubProfileUrl = common.EnsureURLScheme(allSetting.SubProfileUrl)
  1348. for _, ptr := range []*string{
  1349. &allSetting.SubHappNewUrl,
  1350. &allSetting.SubHappFallbackUrl,
  1351. &allSetting.SubHappSubInfoButtonLink,
  1352. &allSetting.SubHappSubExpireButtonLink,
  1353. } {
  1354. if strings.TrimSpace(*ptr) != "" {
  1355. *ptr = common.EnsureURLScheme(strings.TrimSpace(*ptr))
  1356. }
  1357. }
  1358. for name, value := range map[string]*string{
  1359. "Happ routing source": &allSetting.SubRoutingRules,
  1360. "Clash/Mihomo routing source": &allSetting.SubClashRules,
  1361. "Incy routing source": &allSetting.SubIncyRoutingRules,
  1362. "JSON subscription routing source": &allSetting.SubJsonRoutingRules,
  1363. } {
  1364. if err := validateRemoteRoutingURLSetting(name, value); err != nil {
  1365. return err
  1366. }
  1367. }
  1368. return nil
  1369. }
  1370. func validateRemoteRoutingURLSetting(name string, value *string) error {
  1371. canonical, remote, err := common.ParseRemoteRoutingURL(*value)
  1372. if err != nil {
  1373. return common.NewError(name, err.Error())
  1374. }
  1375. if remote {
  1376. *value = canonical
  1377. }
  1378. return nil
  1379. }
  1380. // The same parser the sub server uses, so a value can never be saved as valid
  1381. // and then silently ignored at request time.
  1382. func validateSubJsonDnsSetting(allSetting *entity.AllSetting) error {
  1383. value := strings.TrimSpace(allSetting.SubJsonDns)
  1384. if value != "" {
  1385. if _, err := dnsconf.Parse(value); err != nil {
  1386. return common.NewError("JSON subscription DNS is invalid:", err.Error())
  1387. }
  1388. }
  1389. allSetting.SubJsonDns = value
  1390. return nil
  1391. }
  1392. func (s *SettingService) UpdateSecret(key string, value string) error {
  1393. switch key {
  1394. case "tgBotToken", "ldapPassword", "twoFactorToken":
  1395. return s.saveSetting(key, strings.TrimSpace(value))
  1396. default:
  1397. return common.NewError("secret key is not replaceable:", key)
  1398. }
  1399. }
  1400. func (s *SettingService) GetDefaultXrayConfig() (any, error) {
  1401. var jsonData any
  1402. err := json.Unmarshal([]byte(xrayTemplateConfig), &jsonData)
  1403. if err != nil {
  1404. return nil, err
  1405. }
  1406. return jsonData, nil
  1407. }
  1408. func extractHostname(host string) string {
  1409. h, _, err := net.SplitHostPort(host)
  1410. // Err is not nil means host does not contain port
  1411. if err != nil {
  1412. h = host
  1413. }
  1414. ip := net.ParseIP(h)
  1415. // If it's not an IP, return as is
  1416. if ip == nil {
  1417. return h
  1418. }
  1419. // If it's an IPv4, return as is
  1420. if ip.To4() != nil {
  1421. return h
  1422. }
  1423. // IPv6 needs bracketing
  1424. return "[" + h + "]"
  1425. }
  1426. // BuildSubURIBase is shared by GetDefaultSettings (the panel's Client
  1427. // Information page) and the subscription page so both render subscription
  1428. // URLs identically.
  1429. func (s *SettingService) BuildSubURIBase(host string) string {
  1430. subPort, _ := s.GetSubPort()
  1431. subDomain, _ := s.GetSubDomain()
  1432. subKeyFile, _ := s.GetSubKeyFile()
  1433. subCertFile, _ := s.GetSubCertFile()
  1434. subTLS := subKeyFile != "" && subCertFile != ""
  1435. if subDomain == "" {
  1436. subDomain = extractHostname(host)
  1437. }
  1438. scheme := "http"
  1439. if subTLS {
  1440. scheme = "https"
  1441. }
  1442. if (subPort == 443 && subTLS) || (subPort == 80 && !subTLS) {
  1443. return scheme + "://" + subDomain
  1444. }
  1445. return fmt.Sprintf("%s://%s:%d", scheme, subDomain, subPort)
  1446. }
  1447. func (s *SettingService) GetDefaultSettings(host string) (any, error) {
  1448. type settingFunc func() (any, error)
  1449. settings := map[string]settingFunc{
  1450. "expireDiff": func() (any, error) { return s.GetExpireDiff() },
  1451. "trafficDiff": func() (any, error) { return s.GetTrafficDiff() },
  1452. "pageSize": func() (any, error) { return s.GetPageSize() },
  1453. "defaultCert": func() (any, error) { return s.GetCertFile() },
  1454. "defaultKey": func() (any, error) { return s.GetKeyFile() },
  1455. "tgBotEnable": func() (any, error) { return s.GetTgbotEnabled() },
  1456. "subThemeDir": func() (any, error) { return s.GetSubThemeDir() },
  1457. "happLinkEnable": func() (any, error) { return s.GetHappLinkEnable() },
  1458. "subEnable": func() (any, error) { return s.GetSubEnable() },
  1459. "subJsonEnable": func() (any, error) { return s.GetSubJsonEnable() },
  1460. "subClashEnable": func() (any, error) { return s.GetSubClashEnable() },
  1461. "subTitle": func() (any, error) { return s.GetSubTitle() },
  1462. "subURI": func() (any, error) { return s.GetSubURI() },
  1463. "subJsonURI": func() (any, error) { return s.GetSubJsonURI() },
  1464. "subClashURI": func() (any, error) { return s.GetSubClashURI() },
  1465. "datepicker": func() (any, error) { return s.GetDatepicker() },
  1466. "ipLimitEnable": func() (any, error) { return s.GetIpLimitEnable() },
  1467. "accessLogEnable": func() (any, error) { return s.GetAccessLogEnable() },
  1468. "webDomain": func() (any, error) { return s.GetWebDomain() },
  1469. "subDomain": func() (any, error) { return s.GetSubDomain() },
  1470. "devChannelEnable": func() (any, error) { return s.GetDevChannelEnable() },
  1471. "isDevBuild": func() (any, error) { return config.IsDevBuild(), nil },
  1472. }
  1473. result := make(map[string]any)
  1474. for key, fn := range settings {
  1475. value, err := fn()
  1476. if err != nil {
  1477. return "", err
  1478. }
  1479. result[key] = value
  1480. }
  1481. subEnable := result["subEnable"].(bool)
  1482. subJsonEnable := false
  1483. if v, ok := result["subJsonEnable"]; ok {
  1484. if b, ok2 := v.(bool); ok2 {
  1485. subJsonEnable = b
  1486. }
  1487. }
  1488. subClashEnable := false
  1489. if v, ok := result["subClashEnable"]; ok {
  1490. if b, ok2 := v.(bool); ok2 {
  1491. subClashEnable = b
  1492. }
  1493. }
  1494. if (subEnable && result["subURI"].(string) == "") || (subJsonEnable && result["subJsonURI"].(string) == "") || (subClashEnable && result["subClashURI"].(string) == "") {
  1495. subURI := s.BuildSubURIBase(host)
  1496. subTitle, _ := s.GetSubTitle()
  1497. subPath, _ := s.GetSubPath()
  1498. subJsonPath, _ := s.GetSubJsonPath()
  1499. subClashPath, _ := s.GetSubClashPath()
  1500. if subEnable && result["subURI"].(string) == "" {
  1501. result["subURI"] = subURI + subPath
  1502. }
  1503. if result["subTitle"].(string) == "" {
  1504. result["subTitle"] = subTitle
  1505. }
  1506. if subJsonEnable && result["subJsonURI"].(string) == "" {
  1507. result["subJsonURI"] = subURI + subJsonPath
  1508. }
  1509. if subClashEnable && result["subClashURI"].(string) == "" {
  1510. result["subClashURI"] = subURI + subClashPath
  1511. }
  1512. }
  1513. return result, nil
  1514. }
  1515. var factoryDefaultSecretKeys = map[string]bool{
  1516. "tgBotToken": true,
  1517. "twoFactorToken": true,
  1518. "ldapPassword": true,
  1519. "smtpPassword": true,
  1520. "discordBotToken": true,
  1521. }
  1522. /*
  1523. GetFactoryDefaults returns the shipped default value per setting, keyed by
  1524. the AllSetting json field name. Unlike GetDefaultSettings (which reports
  1525. current effective values), this is defaultValueMap projected through the
  1526. AllSetting field set: only keys that exist as an AllSetting json tag are
  1527. returned, minus the credential fields in factoryDefaultSecretKeys. Keys
  1528. with no AllSetting field (secret, panelGuid, the node mTLS material,
  1529. xrayTemplateConfig) are excluded structurally rather than by deny-list.
  1530. */
  1531. func (s *SettingService) GetFactoryDefaults() map[string]string {
  1532. result := make(map[string]string)
  1533. for _, field := range reflect_util.GetFields(reflect.TypeFor[entity.AllSetting]()) {
  1534. key := field.Tag.Get("json")
  1535. if key == "" || factoryDefaultSecretKeys[key] {
  1536. continue
  1537. }
  1538. if value, ok := defaultValueMap[key]; ok {
  1539. result[key] = value
  1540. }
  1541. }
  1542. return result
  1543. }