xray.html 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. {{template "head" .}}
  4. <style>
  5. @media (min-width: 769px) {
  6. .ant-layout-content {
  7. margin: 24px 16px;
  8. }
  9. }
  10. @media (max-width: 768px) {
  11. .ant-tabs-nav .ant-tabs-tab {
  12. margin: 0;
  13. padding: 12px .5rem;
  14. }
  15. }
  16. .ant-tabs-bar {
  17. margin: 0;
  18. }
  19. .ant-list-item {
  20. display: block;
  21. }
  22. .collapse-title {
  23. color: inherit;
  24. font-weight: bold;
  25. font-size: 18px;
  26. padding: 10px 20px;
  27. border-bottom: 2px solid;
  28. }
  29. .collapse-title > i {
  30. color: inherit;
  31. font-size: 24px;
  32. }
  33. </style>
  34. <body>
  35. <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme">
  36. {{ template "commonSider" . }}
  37. <a-layout id="content-layout">
  38. <a-layout-content>
  39. <a-spin :spinning="spinning" :delay="500" tip='{{ i18n "loading"}}'>
  40. <a-space direction="vertical">
  41. <a-card hoverable style="margin-bottom: .5rem;">
  42. <a-row>
  43. <a-col :xs="24" :sm="8" style="padding: 4px;">
  44. <a-space direction="horizontal">
  45. <a-button type="primary" :disabled="saveBtnDisable" @click="updateXraySetting">{{ i18n "pages.xray.save" }}</a-button>
  46. <a-button type="danger" :disabled="!saveBtnDisable" @click="restartXray">{{ i18n "pages.xray.restart" }}</a-button>
  47. </a-space>
  48. </a-col>
  49. <a-col :xs="24" :sm="16">
  50. <a-alert type="warning" style="float: right; width: fit-content"
  51. message='{{ i18n "pages.settings.infoDesc" }}'
  52. show-icon
  53. >
  54. </a-col>
  55. </a-row>
  56. </a-card>
  57. <a-tabs class="ant-card-dark-box-nohover" default-active-key="tpl-1" :class="themeSwitcher.currentTheme" style="padding: 20px 20px;">
  58. <a-tab-pane key="tpl-1" tab='{{ i18n "pages.xray.basicTemplate"}}' style="padding-top: 20px;">
  59. <a-space direction="horizontal" style="padding: 20px 20px">
  60. <a-button type="primary" @click="resetXrayConfigToDefault">{{ i18n "pages.settings.resetDefaultConfig" }}</a-button>
  61. </a-space>
  62. <a-collapse>
  63. <a-collapse-panel header='{{ i18n "pages.xray.generalConfigs"}}'>
  64. <a-row :xs="24" :sm="24" :lg="12">
  65. <h2 class="collapse-title">
  66. <a-icon type="warning"></a-icon>
  67. {{ i18n "pages.xray.generalConfigsDesc" }}
  68. </h2>
  69. </a-row>
  70. <a-list-item>
  71. <a-row style="padding: 20px">
  72. <a-col :lg="24" :xl="12">
  73. <a-list-item-meta
  74. title='{{ i18n "pages.xray.xrayConfigFreedomStrategy" }}'
  75. description='{{ i18n "pages.xray.xrayConfigFreedomStrategyDesc" }}'/>
  76. </a-col>
  77. <a-col :lg="24" :xl="12">
  78. <template>
  79. <a-select
  80. v-model="freedomStrategy"
  81. :dropdown-class-name="themeSwitcher.currentTheme"
  82. style="width: 100%">
  83. <a-select-option v-for="s in outboundDomainStrategies" :value="s">[[ s ]]</a-select-option>
  84. </a-select>
  85. </template>
  86. </a-col>
  87. </a-row>
  88. </a-list-item>
  89. <a-row style="padding: 20px">
  90. <a-col :lg="24" :xl="12">
  91. <a-list-item-meta
  92. title='{{ i18n "pages.xray.xrayConfigRoutingStrategy" }}'
  93. description='{{ i18n "pages.xray.xrayConfigRoutingStrategyDesc" }}'/>
  94. </a-col>
  95. <a-col :lg="24" :xl="12">
  96. <template>
  97. <a-select
  98. v-model="routingStrategy"
  99. :dropdown-class-name="themeSwitcher.currentTheme"
  100. style="width: 100%">
  101. <a-select-option v-for="s in routingDomainStrategies" :value="s">[[ s ]]</a-select-option>
  102. </a-select>
  103. </template>
  104. </a-col>
  105. </a-row>
  106. </a-list-item>
  107. </a-collapse-panel>
  108. <a-collapse-panel header='{{ i18n "pages.xray.blockConfigs"}}'>
  109. <a-row :xs="24" :sm="24" :lg="12">
  110. <h2 class="collapse-title">
  111. <a-icon type="warning"></a-icon>
  112. {{ i18n "pages.xray.blockConfigsDesc" }}
  113. </h2>
  114. </a-row>
  115. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigTorrent"}}' desc='{{ i18n "pages.xray.xrayConfigTorrentDesc"}}' v-model="torrentSettings"></setting-list-item>
  116. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigPrivateIp"}}' desc='{{ i18n "pages.xray.xrayConfigPrivateIpDesc"}}' v-model="privateIpSettings"></setting-list-item>
  117. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigAds"}}' desc='{{ i18n "pages.xray.xrayConfigAdsDesc"}}' v-model="AdsSettings"></setting-list-item>
  118. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigFamily"}}' desc='{{ i18n "pages.xray.xrayConfigFamilyDesc"}}' v-model="familyProtectSettings"></setting-list-item>
  119. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigSpeedtest"}}' desc='{{ i18n "pages.xray.xrayConfigSpeedtestDesc"}}' v-model="SpeedTestSettings"></setting-list-item>
  120. </a-collapse-panel>
  121. <a-collapse-panel header='{{ i18n "pages.xray.blockCountryConfigs"}}'>
  122. <a-row :xs="24" :sm="24" :lg="12">
  123. <h2 class="collapse-title">
  124. <a-icon type="warning"></a-icon>
  125. {{ i18n "pages.xray.blockCountryConfigsDesc" }}
  126. </h2>
  127. </a-row>
  128. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigIRIp"}}' desc='{{ i18n "pages.xray.xrayConfigIRIpDesc"}}' v-model="IRIpSettings"></setting-list-item>
  129. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigIRDomain"}}' desc='{{ i18n "pages.xray.xrayConfigIRDomainDesc"}}' v-model="IRDomainSettings"></setting-list-item>
  130. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigChinaIp"}}' desc='{{ i18n "pages.xray.xrayConfigChinaIpDesc"}}' v-model="ChinaIpSettings"></setting-list-item>
  131. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigChinaDomain"}}' desc='{{ i18n "pages.xray.xrayConfigChinaDomainDesc"}}' v-model="ChinaDomainSettings"></setting-list-item>
  132. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigRussiaIp"}}' desc='{{ i18n "pages.xray.xrayConfigRussiaIpDesc"}}' v-model="RussiaIpSettings"></setting-list-item>
  133. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigRussiaDomain"}}' desc='{{ i18n "pages.xray.xrayConfigRussiaDomainDesc"}}' v-model="RussiaDomainSettings"></setting-list-item>
  134. </a-collapse-panel>
  135. <a-collapse-panel header='{{ i18n "pages.xray.directCountryConfigs"}}'>
  136. <a-row :xs="24" :sm="24" :lg="12">
  137. <h2 class="collapse-title">
  138. <a-icon type="warning"></a-icon>
  139. {{ i18n "pages.xray.directCountryConfigsDesc" }}
  140. </h2>
  141. </a-row>
  142. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigDirectIRIp"}}' desc='{{ i18n "pages.xray.xrayConfigDirectIRIpDesc"}}' v-model="IRIpDirectSettings"></setting-list-item>
  143. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigDirectIRDomain"}}' desc='{{ i18n "pages.xray.xrayConfigDirectIRDomainDesc"}}' v-model="IRDomainDirectSettings"></setting-list-item>
  144. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigDirectChinaIp"}}' desc='{{ i18n "pages.xray.xrayConfigDirectChinaIpDesc"}}' v-model="ChinaIpDirectSettings"></setting-list-item>
  145. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigDirectChinaDomain"}}' desc='{{ i18n "pages.xray.xrayConfigDirectChinaDomainDesc"}}' v-model="ChinaDomainDirectSettings"></setting-list-item>
  146. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigDirectRussiaIp"}}' desc='{{ i18n "pages.xray.xrayConfigDirectRussiaIpDesc"}}' v-model="RussiaIpDirectSettings"></setting-list-item>
  147. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigDirectRussiaDomain"}}' desc='{{ i18n "pages.xray.xrayConfigDirectRussiaDomainDesc"}}' v-model="RussiaDomainDirectSettings"></setting-list-item>
  148. </a-collapse-panel>
  149. <a-collapse-panel header='{{ i18n "pages.xray.ipv4Configs"}}'>
  150. <a-row :xs="24" :sm="24" :lg="12">
  151. <h2 class="collapse-title">
  152. <a-icon type="warning"></a-icon>
  153. {{ i18n "pages.xray.ipv4ConfigsDesc" }}
  154. </h2>
  155. </a-row>
  156. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigGoogleIPv4"}}' desc='{{ i18n "pages.xray.xrayConfigGoogleIPv4Desc"}}' v-model="GoogleIPv4Settings"></setting-list-item>
  157. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigNetflixIPv4"}}' desc='{{ i18n "pages.xray.xrayConfigNetflixIPv4Desc"}}' v-model="NetflixIPv4Settings"></setting-list-item>
  158. </a-collapse-panel>
  159. <a-collapse-panel header='{{ i18n "pages.xray.warpConfigs"}}'>
  160. <a-row :xs="24" :sm="24" :lg="12">
  161. <h2 class="collapse-title">
  162. <a-icon type="warning"></a-icon>
  163. {{ i18n "pages.xray.warpConfigsDesc" }}
  164. </h2>
  165. </a-row>
  166. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigGoogleWARP"}}' desc='{{ i18n "pages.xray.xrayConfigGoogleWARPDesc"}}' v-model="GoogleWARPSettings"></setting-list-item>
  167. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigOpenAIWARP"}}' desc='{{ i18n "pages.xray.xrayConfigOpenAIWARPDesc"}}' v-model="OpenAIWARPSettings"></setting-list-item>
  168. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigNetflixWARP"}}' desc='{{ i18n "pages.xray.xrayConfigNetflixWARPDesc"}}' v-model="NetflixWARPSettings"></setting-list-item>
  169. <setting-list-item type="switch" title='{{ i18n "pages.xray.xrayConfigSpotifyWARP"}}' desc='{{ i18n "pages.xray.xrayConfigSpotifyWARPDesc"}}' v-model="SpotifyWARPSettings"></setting-list-item>
  170. </a-collapse-panel>
  171. </a-collapse>
  172. </a-tab-pane>
  173. <a-tab-pane key="tpl-2" tab='{{ i18n "pages.xray.manualLists"}}' style="padding-top: 20px;">
  174. <a-row :xs="24" :sm="24" :lg="12">
  175. <h2 class="collapse-title">
  176. <a-icon type="warning"></a-icon>
  177. {{ i18n "pages.xray.manualListsDesc" }}
  178. </h2>
  179. </a-row>
  180. <a-collapse>
  181. <a-collapse-panel header='{{ i18n "pages.xray.manualBlockedIPs"}}'>
  182. <setting-list-item type="textarea" v-model="manualBlockedIPs"></setting-list-item>
  183. </a-collapse-panel>
  184. <a-collapse-panel header='{{ i18n "pages.xray.manualBlockedDomains"}}'>
  185. <setting-list-item type="textarea" v-model="manualBlockedDomains"></setting-list-item>
  186. </a-collapse-panel>
  187. <a-collapse-panel header='{{ i18n "pages.xray.manualDirectIPs"}}'>
  188. <setting-list-item type="textarea" v-model="manualDirectIPs"></setting-list-item>
  189. </a-collapse-panel>
  190. <a-collapse-panel header='{{ i18n "pages.xray.manualDirectDomains"}}'>
  191. <setting-list-item type="textarea" v-model="manualDirectDomains"></setting-list-item>
  192. </a-collapse-panel>
  193. <a-collapse-panel header='{{ i18n "pages.xray.manualIPv4Domains"}}'>
  194. <setting-list-item type="textarea" v-model="manualIPv4Domains"></setting-list-item>
  195. </a-collapse-panel>
  196. <a-collapse-panel header='{{ i18n "pages.xray.manualWARPDomains"}}'>
  197. <setting-list-item type="textarea" v-model="manualWARPDomains"></setting-list-item>
  198. </a-collapse-panel>
  199. </a-collapse>
  200. </a-tab-pane>
  201. <a-tab-pane key="tpl-3" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;">
  202. <a-collapse>
  203. <a-collapse-panel header='{{ i18n "pages.xray.xrayConfigInbounds"}}'>
  204. <setting-list-item type="textarea" title='{{ i18n "pages.xray.xrayConfigInbounds"}}' desc='{{ i18n "pages.xray.xrayConfigInboundsDesc"}}' v-model="inboundSettings"></setting-list-item>
  205. </a-collapse-panel>
  206. <a-collapse-panel header='{{ i18n "pages.xray.xrayConfigOutbounds"}}'>
  207. <setting-list-item type="textarea" title='{{ i18n "pages.xray.xrayConfigOutbounds"}}' desc='{{ i18n "pages.xray.xrayConfigOutboundsDesc"}}' v-model="outboundSettings"></setting-list-item>
  208. </a-collapse-panel>
  209. <a-collapse-panel header='{{ i18n "pages.xray.xrayConfigRoutings"}}'>
  210. <setting-list-item type="textarea" title='{{ i18n "pages.xray.xrayConfigRoutings"}}' desc='{{ i18n "pages.xray.xrayConfigRoutingsDesc"}}' v-model="routingRuleSettings"></setting-list-item>
  211. </a-collapse-panel>
  212. </a-collapse>
  213. </a-tab-pane>
  214. <a-tab-pane key="tpl-4" tab='{{ i18n "pages.xray.completeTemplate"}}' style="padding-top: 20px;">
  215. <setting-list-item type="textarea" title='{{ i18n "pages.xray.xrayConfigTemplate"}}' desc='{{ i18n "pages.xray.xrayConfigTemplateDesc"}}' v-model="this.xraySetting"></setting-list-item>
  216. </a-tab-pane>
  217. </a-tabs>
  218. </a-space>
  219. </a-spin>
  220. </a-layout-content>
  221. </a-layout>
  222. </a-layout>
  223. {{template "js" .}}
  224. {{template "component/themeSwitcher" .}}
  225. {{template "component/setting"}}
  226. <script>
  227. const app = new Vue({
  228. delimiters: ['[[', ']]'],
  229. el: '#app',
  230. data: {
  231. siderDrawer,
  232. themeSwitcher,
  233. spinning: false,
  234. oldXraySetting: '',
  235. xraySetting: '',
  236. saveBtnDisable: true,
  237. ipv4Settings: {
  238. tag: "IPv4",
  239. protocol: "freedom",
  240. settings: {
  241. domainStrategy: "UseIPv4"
  242. }
  243. },
  244. warpSettings: {
  245. tag: "WARP",
  246. protocol: "socks",
  247. settings: {
  248. servers: [
  249. {
  250. address: "127.0.0.1",
  251. port: 40000
  252. }
  253. ]
  254. }
  255. },
  256. directSettings: {
  257. tag: "direct",
  258. protocol: "freedom"
  259. },
  260. outboundDomainStrategies: ["AsIs", "UseIP", "UseIPv4", "UseIPv6"],
  261. routingDomainStrategies: ["AsIs", "IPIfNonMatch", "IPOnDemand"],
  262. settingsData: {
  263. protocols: {
  264. bittorrent: ["bittorrent"],
  265. },
  266. ips: {
  267. local: ["geoip:private"],
  268. cn: ["geoip:cn"],
  269. ir: ["ext:geoip_IR.dat:ir","ext:geoip_IR.dat:arvancloud","ext:geoip_IR.dat:derakcloud","ext:geoip_IR.dat:iranserver"],
  270. ru: ["geoip:ru"],
  271. },
  272. domains: {
  273. ads: [
  274. "geosite:category-ads-all",
  275. "ext:geosite_IR.dat:category-ads-all"
  276. ],
  277. speedtest: ["geosite:speedtest"],
  278. openai: ["geosite:openai"],
  279. google: ["geosite:google"],
  280. spotify: ["geosite:spotify"],
  281. netflix: ["geosite:netflix"],
  282. cn: [
  283. "geosite:cn",
  284. "regexp:.*\\.cn$"
  285. ],
  286. ru: [
  287. "geosite:category-gov-ru",
  288. "regexp:.*\\.ru$"
  289. ],
  290. ir: [
  291. "regexp:.*\\.ir$",
  292. "regexp:.*\\.xn--mgba3a4f16a$", // .ایران
  293. "ext:geosite_IR.dat:ir" // have rules to bypass all .ir domains.
  294. ]
  295. },
  296. familyProtectDNS: {
  297. "servers": [
  298. "1.1.1.3", // https://developers.cloudflare.com/1.1.1.1/setup/
  299. "1.0.0.3",
  300. "94.140.14.15", // https://adguard-dns.io/kb/general/dns-providers/
  301. "94.140.15.16"
  302. ],
  303. "queryStrategy": "UseIPv4"
  304. },
  305. }
  306. },
  307. methods: {
  308. loading(spinning = true) {
  309. this.spinning = spinning;
  310. },
  311. async getXraySetting() {
  312. this.loading(true);
  313. const msg = await HttpUtil.post("/panel/xray/");
  314. this.loading(false);
  315. if (msg.success) {
  316. this.oldXraySetting = msg.obj;
  317. this.xraySetting = msg.obj;
  318. this.saveBtnDisable = true;
  319. }
  320. },
  321. async updateXraySetting() {
  322. this.loading(true);
  323. const msg = await HttpUtil.post("/panel/xray/update", {xraySetting : this.xraySetting});
  324. this.loading(false);
  325. if (msg.success) {
  326. await this.getXraySetting();
  327. }
  328. },
  329. async restartPanel() {
  330. this.loading(true);
  331. const msg = await HttpUtil.post("server/restartXrayService");
  332. this.loading(false);
  333. if (msg.success) {
  334. this.loading(true);
  335. }
  336. },
  337. async fetchUserSecret() {
  338. this.loading(true);
  339. const userMessage = await HttpUtil.post("/panel/setting/getUserSecret", this.user);
  340. if (userMessage.success) {
  341. this.user = userMessage.obj;
  342. }
  343. this.loading(false);
  344. },
  345. async updateSecret() {
  346. this.loading(true);
  347. const msg = await HttpUtil.post("/panel/setting/updateUserSecret", this.user);
  348. if (msg.success) {
  349. this.user = msg.obj;
  350. window.location.replace(basePath + "logout");
  351. }
  352. this.loading(false);
  353. await this.updateXraySetting();
  354. },
  355. generateRandomString(length) {
  356. var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
  357. let randomString = "";
  358. for (let i = 0; i < length; i++) {
  359. randomString += chars[Math.floor(Math.random() * chars.length)];
  360. }
  361. return randomString;
  362. },
  363. async getNewSecret() {
  364. this.loading(true);
  365. await PromiseUtil.sleep(600);
  366. const newSecret = this.generateRandomString(64);
  367. this.user.loginSecret = newSecret;
  368. document.getElementById("token").textContent = newSecret;
  369. this.loading(false);
  370. },
  371. async toggleToken(value) {
  372. if (value) {
  373. await this.getNewSecret();
  374. } else {
  375. this.user.loginSecret = "";
  376. }
  377. },
  378. async resetXrayConfigToDefault() {
  379. this.loading(true);
  380. const msg = await HttpUtil.get("/panel/setting/getDefaultJsonConfig");
  381. this.loading(false);
  382. if (msg.success) {
  383. this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2));
  384. this.saveBtnDisable = true;
  385. }
  386. },
  387. syncRulesWithOutbound(tag, setting) {
  388. const newTemplateSettings = {...this.templateSettings};
  389. const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag);
  390. const outboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.tag === tag);
  391. if (!haveRules && outboundIndex >= 0) {
  392. newTemplateSettings.outbounds.splice(outboundIndex, 1);
  393. }
  394. if (haveRules && outboundIndex === -1) {
  395. newTemplateSettings.outbounds.push(setting);
  396. }
  397. this.templateSettings = newTemplateSettings;
  398. },
  399. templateRuleGetter(routeSettings) {
  400. const { property, outboundTag } = routeSettings;
  401. let result = [];
  402. if (this.templateSettings != null) {
  403. this.templateSettings.routing.rules.forEach(
  404. (routingRule) => {
  405. if (
  406. routingRule.hasOwnProperty(property) &&
  407. routingRule.hasOwnProperty("outboundTag") &&
  408. routingRule.outboundTag === outboundTag
  409. ) {
  410. result.push(...routingRule[property]);
  411. }
  412. }
  413. );
  414. }
  415. return result;
  416. },
  417. templateRuleSetter(routeSettings) {
  418. const { data, property, outboundTag } = routeSettings;
  419. const oldTemplateSettings = this.templateSettings;
  420. const newTemplateSettings = oldTemplateSettings;
  421. currentProperty = this.templateRuleGetter({ outboundTag, property })
  422. if (currentProperty.length == 0) {
  423. const propertyRule = {
  424. type: "field",
  425. outboundTag,
  426. [property]: data
  427. };
  428. newTemplateSettings.routing.rules.push(propertyRule);
  429. }
  430. else {
  431. const newRules = [];
  432. insertedOnce = false;
  433. newTemplateSettings.routing.rules.forEach(
  434. (routingRule) => {
  435. if (
  436. routingRule.hasOwnProperty(property) &&
  437. routingRule.hasOwnProperty("outboundTag") &&
  438. routingRule.outboundTag === outboundTag
  439. ) {
  440. if (!insertedOnce && data.length > 0) {
  441. insertedOnce = true;
  442. routingRule[property] = data;
  443. newRules.push(routingRule);
  444. }
  445. }
  446. else {
  447. newRules.push(routingRule);
  448. }
  449. }
  450. );
  451. newTemplateSettings.routing.rules = newRules;
  452. }
  453. this.templateSettings = newTemplateSettings;
  454. }
  455. },
  456. async mounted() {
  457. await this.getXraySetting();
  458. while (true) {
  459. await PromiseUtil.sleep(600);
  460. this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
  461. }
  462. },
  463. computed: {
  464. templateSettings: {
  465. get: function () { return this.xraySetting ? JSON.parse(this.xraySetting) : null; },
  466. set: function (newValue) { this.xraySetting = JSON.stringify(newValue, null, 2); },
  467. },
  468. inboundSettings: {
  469. get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; },
  470. set: function (newValue) {
  471. newTemplateSettings = this.templateSettings;
  472. newTemplateSettings.inbounds = JSON.parse(newValue);
  473. this.templateSettings = newTemplateSettings;
  474. },
  475. },
  476. outboundSettings: {
  477. get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.outbounds, null, 2) : null; },
  478. set: function (newValue) {
  479. newTemplateSettings = this.templateSettings;
  480. newTemplateSettings.outbounds = JSON.parse(newValue);
  481. this.templateSettings = newTemplateSettings;
  482. },
  483. },
  484. routingRuleSettings: {
  485. get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.routing.rules, null, 2) : null; },
  486. set: function (newValue) {
  487. newTemplateSettings = this.templateSettings;
  488. newTemplateSettings.routing.rules = JSON.parse(newValue);
  489. this.templateSettings = newTemplateSettings;
  490. },
  491. },
  492. freedomStrategy: {
  493. get: function () {
  494. if (!this.templateSettings) return "AsIs";
  495. freedomOutbound = this.templateSettings.outbounds.find((o) => o.protocol === "freedom" && !o.tag);
  496. if (!freedomOutbound) return "AsIs";
  497. if (!freedomOutbound.settings || !freedomOutbound.settings.domainStrategy) return "AsIs";
  498. return freedomOutbound.settings.domainStrategy;
  499. },
  500. set: function (newValue) {
  501. newTemplateSettings = this.templateSettings;
  502. freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && !o.tag);
  503. if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
  504. newTemplateSettings.outbounds[freedomOutboundIndex].settings = {"domainStrategy": newValue};
  505. } else {
  506. newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
  507. }
  508. this.templateSettings = newTemplateSettings;
  509. }
  510. },
  511. routingStrategy: {
  512. get: function () {
  513. if (!this.templateSettings || !this.templateSettings.routing || !this.templateSettings.routing.domainStrategy) return "AsIs";
  514. return this.templateSettings.routing.domainStrategy;
  515. },
  516. set: function (newValue) {
  517. newTemplateSettings = this.templateSettings;
  518. newTemplateSettings.routing.domainStrategy = newValue;
  519. this.templateSettings = newTemplateSettings;
  520. }
  521. },
  522. blockedIPs: {
  523. get: function () {
  524. return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" });
  525. },
  526. set: function (newValue) {
  527. this.templateRuleSetter({ outboundTag: "blocked", property: "ip", data: newValue });
  528. }
  529. },
  530. blockedDomains: {
  531. get: function () {
  532. return this.templateRuleGetter({ outboundTag: "blocked", property: "domain" });
  533. },
  534. set: function (newValue) {
  535. this.templateRuleSetter({ outboundTag: "blocked", property: "domain", data: newValue });
  536. }
  537. },
  538. blockedProtocols: {
  539. get: function () {
  540. return this.templateRuleGetter({ outboundTag: "blocked", property: "protocol" });
  541. },
  542. set: function (newValue) {
  543. this.templateRuleSetter({ outboundTag: "blocked", property: "protocol", data: newValue });
  544. }
  545. },
  546. directIPs: {
  547. get: function () {
  548. return this.templateRuleGetter({ outboundTag: "direct", property: "ip" });
  549. },
  550. set: function (newValue) {
  551. this.templateRuleSetter({ outboundTag: "direct", property: "ip", data: newValue });
  552. this.syncRulesWithOutbound("direct", this.directSettings);
  553. }
  554. },
  555. directDomains: {
  556. get: function () {
  557. return this.templateRuleGetter({ outboundTag: "direct", property: "domain" });
  558. },
  559. set: function (newValue) {
  560. this.templateRuleSetter({ outboundTag: "direct", property: "domain", data: newValue });
  561. this.syncRulesWithOutbound("direct", this.directSettings);
  562. }
  563. },
  564. ipv4Domains: {
  565. get: function () {
  566. return this.templateRuleGetter({ outboundTag: "IPv4", property: "domain" });
  567. },
  568. set: function (newValue) {
  569. this.templateRuleSetter({ outboundTag: "IPv4", property: "domain", data: newValue });
  570. this.syncRulesWithOutbound("IPv4", this.ipv4Settings);
  571. }
  572. },
  573. warpDomains: {
  574. get: function () {
  575. return this.templateRuleGetter({ outboundTag: "WARP", property: "domain" });
  576. },
  577. set: function (newValue) {
  578. this.templateRuleSetter({ outboundTag: "WARP", property: "domain", data: newValue });
  579. this.syncRulesWithOutbound("WARP", this.warpSettings);
  580. }
  581. },
  582. manualBlockedIPs: {
  583. get: function () { return JSON.stringify(this.blockedIPs, null, 2); },
  584. set: debounce(function (value) { this.blockedIPs = JSON.parse(value); }, 1000)
  585. },
  586. manualBlockedDomains: {
  587. get: function () { return JSON.stringify(this.blockedDomains, null, 2); },
  588. set: debounce(function (value) { this.blockedDomains = JSON.parse(value); }, 1000)
  589. },
  590. manualDirectIPs: {
  591. get: function () { return JSON.stringify(this.directIPs, null, 2); },
  592. set: debounce(function (value) { this.directIPs = JSON.parse(value); }, 1000)
  593. },
  594. manualDirectDomains: {
  595. get: function () { return JSON.stringify(this.directDomains, null, 2); },
  596. set: debounce(function (value) { this.directDomains = JSON.parse(value); }, 1000)
  597. },
  598. manualIPv4Domains: {
  599. get: function () { return JSON.stringify(this.ipv4Domains, null, 2); },
  600. set: debounce(function (value) { this.ipv4Domains = JSON.parse(value); }, 1000)
  601. },
  602. manualWARPDomains: {
  603. get: function () { return JSON.stringify(this.warpDomains, null, 2); },
  604. set: debounce(function (value) { this.warpDomains = JSON.parse(value); }, 1000)
  605. },
  606. torrentSettings: {
  607. get: function () {
  608. return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
  609. },
  610. set: function (newValue) {
  611. if (newValue) {
  612. this.blockedProtocols = [...this.blockedProtocols, ...this.settingsData.protocols.bittorrent];
  613. } else {
  614. this.blockedProtocols = this.blockedProtocols.filter(data => !this.settingsData.protocols.bittorrent.includes(data));
  615. }
  616. },
  617. },
  618. privateIpSettings: {
  619. get: function () {
  620. return doAllItemsExist(this.settingsData.ips.local, this.blockedIPs);
  621. },
  622. set: function (newValue) {
  623. if (newValue) {
  624. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.local];
  625. } else {
  626. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.local.includes(data));
  627. }
  628. },
  629. },
  630. AdsSettings: {
  631. get: function () {
  632. return doAllItemsExist(this.settingsData.domains.ads, this.blockedDomains);
  633. },
  634. set: function (newValue) {
  635. if (newValue) {
  636. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ads];
  637. } else {
  638. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ads.includes(data));
  639. }
  640. },
  641. },
  642. SpeedTestSettings: {
  643. get: function () {
  644. return doAllItemsExist(this.settingsData.domains.speedtest, this.blockedDomains);
  645. },
  646. set: function (newValue) {
  647. if (newValue) {
  648. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.speedtest];
  649. } else {
  650. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.speedtest.includes(data));
  651. }
  652. },
  653. },
  654. familyProtectSettings: {
  655. get: function () {
  656. if (!this.templateSettings || !this.templateSettings.dns || !this.templateSettings.dns.servers) return false;
  657. return doAllItemsExist(this.templateSettings.dns.servers, this.settingsData.familyProtectDNS.servers);
  658. },
  659. set: function (newValue) {
  660. newTemplateSettings = this.templateSettings;
  661. if (newValue) {
  662. newTemplateSettings.dns = this.settingsData.familyProtectDNS;
  663. } else {
  664. delete newTemplateSettings.dns;
  665. }
  666. this.templateSettings = newTemplateSettings;
  667. },
  668. },
  669. GoogleIPv4Settings: {
  670. get: function () {
  671. return doAllItemsExist(this.settingsData.domains.google, this.ipv4Domains);
  672. },
  673. set: function (newValue) {
  674. if (newValue) {
  675. this.ipv4Domains = [...this.ipv4Domains, ...this.settingsData.domains.google];
  676. } else {
  677. this.ipv4Domains = this.ipv4Domains.filter(data => !this.settingsData.domains.google.includes(data));
  678. }
  679. },
  680. },
  681. NetflixIPv4Settings: {
  682. get: function () {
  683. return doAllItemsExist(this.settingsData.domains.netflix, this.ipv4Domains);
  684. },
  685. set: function (newValue) {
  686. if (newValue) {
  687. this.ipv4Domains = [...this.ipv4Domains, ...this.settingsData.domains.netflix];
  688. } else {
  689. this.ipv4Domains = this.ipv4Domains.filter(data => !this.settingsData.domains.netflix.includes(data));
  690. }
  691. },
  692. },
  693. IRIpSettings: {
  694. get: function () {
  695. return doAllItemsExist(this.settingsData.ips.ir, this.blockedIPs);
  696. },
  697. set: function (newValue) {
  698. if (newValue) {
  699. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.ir];
  700. } else {
  701. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.ir.includes(data));
  702. }
  703. }
  704. },
  705. IRDomainSettings: {
  706. get: function () {
  707. return doAllItemsExist(this.settingsData.domains.ir, this.blockedDomains);
  708. },
  709. set: function (newValue) {
  710. if (newValue) {
  711. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ir];
  712. } else {
  713. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ir.includes(data));
  714. }
  715. }
  716. },
  717. ChinaIpSettings: {
  718. get: function () {
  719. return doAllItemsExist(this.settingsData.ips.cn, this.blockedIPs);
  720. },
  721. set: function (newValue) {
  722. if (newValue) {
  723. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.cn];
  724. } else {
  725. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.cn.includes(data));
  726. }
  727. }
  728. },
  729. ChinaDomainSettings: {
  730. get: function () {
  731. return doAllItemsExist(this.settingsData.domains.cn, this.blockedDomains);
  732. },
  733. set: function (newValue) {
  734. if (newValue) {
  735. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.cn];
  736. } else {
  737. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.cn.includes(data));
  738. }
  739. }
  740. },
  741. RussiaIpSettings: {
  742. get: function () {
  743. return doAllItemsExist(this.settingsData.ips.ru, this.blockedIPs);
  744. },
  745. set: function (newValue) {
  746. if (newValue) {
  747. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.ru];
  748. } else {
  749. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.ru.includes(data));
  750. }
  751. }
  752. },
  753. RussiaDomainSettings: {
  754. get: function () {
  755. return doAllItemsExist(this.settingsData.domains.ru, this.blockedDomains);
  756. },
  757. set: function (newValue) {
  758. if (newValue) {
  759. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ru];
  760. } else {
  761. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ru.includes(data));
  762. }
  763. }
  764. },
  765. IRIpDirectSettings: {
  766. get: function () {
  767. return doAllItemsExist(this.settingsData.ips.ir, this.directIPs);
  768. },
  769. set: function (newValue) {
  770. if (newValue) {
  771. this.directIPs = [...this.directIPs, ...this.settingsData.ips.ir];
  772. } else {
  773. this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.ir.includes(data));
  774. }
  775. }
  776. },
  777. IRDomainDirectSettings: {
  778. get: function () {
  779. return doAllItemsExist(this.settingsData.domains.ir, this.directDomains);
  780. },
  781. set: function (newValue) {
  782. if (newValue) {
  783. this.directDomains = [...this.directDomains, ...this.settingsData.domains.ir];
  784. } else {
  785. this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.ir.includes(data));
  786. }
  787. }
  788. },
  789. ChinaIpDirectSettings: {
  790. get: function () {
  791. return doAllItemsExist(this.settingsData.ips.cn, this.directIPs);
  792. },
  793. set: function (newValue) {
  794. if (newValue) {
  795. this.directIPs = [...this.directIPs, ...this.settingsData.ips.cn];
  796. } else {
  797. this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.cn.includes(data));
  798. }
  799. }
  800. },
  801. ChinaDomainDirectSettings: {
  802. get: function () {
  803. return doAllItemsExist(this.settingsData.domains.cn, this.directDomains);
  804. },
  805. set: function (newValue) {
  806. if (newValue) {
  807. this.directDomains = [...this.directDomains, ...this.settingsData.domains.cn];
  808. } else {
  809. this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.cn.includes(data));
  810. }
  811. }
  812. },
  813. RussiaIpDirectSettings: {
  814. get: function () {
  815. return doAllItemsExist(this.settingsData.ips.ru, this.directIPs);
  816. },
  817. set: function (newValue) {
  818. if (newValue) {
  819. this.directIPs = [...this.directIPs, ...this.settingsData.ips.ru];
  820. } else {
  821. this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.ru.includes(data));
  822. }
  823. }
  824. },
  825. RussiaDomainDirectSettings: {
  826. get: function () {
  827. return doAllItemsExist(this.settingsData.domains.ru, this.directDomains);
  828. },
  829. set: function (newValue) {
  830. if (newValue) {
  831. this.directDomains = [...this.directDomains, ...this.settingsData.domains.ru];
  832. } else {
  833. this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.ru.includes(data));
  834. }
  835. }
  836. },
  837. GoogleWARPSettings: {
  838. get: function () {
  839. return doAllItemsExist(this.settingsData.domains.google, this.warpDomains);
  840. },
  841. set: function (newValue) {
  842. if (newValue) {
  843. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.google];
  844. } else {
  845. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.google.includes(data));
  846. }
  847. },
  848. },
  849. OpenAIWARPSettings: {
  850. get: function () {
  851. return doAllItemsExist(this.settingsData.domains.openai, this.warpDomains);
  852. },
  853. set: function (newValue) {
  854. if (newValue) {
  855. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.openai];
  856. } else {
  857. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.openai.includes(data));
  858. }
  859. },
  860. },
  861. NetflixWARPSettings: {
  862. get: function () {
  863. return doAllItemsExist(this.settingsData.domains.netflix, this.warpDomains);
  864. },
  865. set: function (newValue) {
  866. if (newValue) {
  867. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.netflix];
  868. } else {
  869. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.netflix.includes(data));
  870. }
  871. },
  872. },
  873. SpotifyWARPSettings: {
  874. get: function () {
  875. return doAllItemsExist(this.settingsData.domains.spotify, this.warpDomains);
  876. },
  877. set: function (newValue) {
  878. if (newValue) {
  879. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.spotify];
  880. } else {
  881. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.spotify.includes(data));
  882. }
  883. },
  884. },
  885. },
  886. });
  887. </script>
  888. </body>
  889. </html>