1
0

xray.html 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  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.settings.save" }}</a-button>
  46. <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n "pages.settings.restartPanel" }}</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. await new Promise(resolve => {
  331. this.$confirm({
  332. title: '{{ i18n "pages.settings.restartPanel" }}',
  333. content: '{{ i18n "pages.settings.restartPanelDesc" }}',
  334. class: themeSwitcher.currentTheme,
  335. okText: '{{ i18n "sure" }}',
  336. cancelText: '{{ i18n "cancel" }}',
  337. onOk: () => resolve(),
  338. });
  339. });
  340. this.loading(true);
  341. const msg = await HttpUtil.post("/panel/setting/restartPanel");
  342. this.loading(false);
  343. if (msg.success) {
  344. this.loading(true);
  345. await PromiseUtil.sleep(5000);
  346. var { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.xraySetting;
  347. if (host == this.oldXraySetting.webDomain) host = null;
  348. if (port == this.oldXraySetting.webPort) port = null;
  349. const isTLS = webCertFile !== "" || webKeyFile !== "";
  350. const url = buildURL({ host, port, isTLS, base, path: "panel/settings" });
  351. window.location.replace(url);
  352. }
  353. },
  354. async fetchUserSecret() {
  355. this.loading(true);
  356. const userMessage = await HttpUtil.post("/panel/setting/getUserSecret", this.user);
  357. if (userMessage.success) {
  358. this.user = userMessage.obj;
  359. }
  360. this.loading(false);
  361. },
  362. async updateSecret() {
  363. this.loading(true);
  364. const msg = await HttpUtil.post("/panel/setting/updateUserSecret", this.user);
  365. if (msg.success) {
  366. this.user = msg.obj;
  367. window.location.replace(basePath + "logout");
  368. }
  369. this.loading(false);
  370. await this.updateXraySetting();
  371. },
  372. generateRandomString(length) {
  373. var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
  374. let randomString = "";
  375. for (let i = 0; i < length; i++) {
  376. randomString += chars[Math.floor(Math.random() * chars.length)];
  377. }
  378. return randomString;
  379. },
  380. async getNewSecret() {
  381. this.loading(true);
  382. await PromiseUtil.sleep(600);
  383. const newSecret = this.generateRandomString(64);
  384. this.user.loginSecret = newSecret;
  385. document.getElementById("token").textContent = newSecret;
  386. this.loading(false);
  387. },
  388. async toggleToken(value) {
  389. if (value) {
  390. await this.getNewSecret();
  391. } else {
  392. this.user.loginSecret = "";
  393. }
  394. },
  395. async resetXrayConfigToDefault() {
  396. this.loading(true);
  397. const msg = await HttpUtil.get("/panel/setting/getDefaultJsonConfig");
  398. this.loading(false);
  399. if (msg.success) {
  400. this.templateSettings = JSON.parse(JSON.stringify(msg.obj, null, 2));
  401. this.saveBtnDisable = true;
  402. }
  403. },
  404. syncRulesWithOutbound(tag, setting) {
  405. const newTemplateSettings = {...this.templateSettings};
  406. const haveRules = newTemplateSettings.routing.rules.some((r) => r?.outboundTag === tag);
  407. const outboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.tag === tag);
  408. if (!haveRules && outboundIndex >= 0) {
  409. newTemplateSettings.outbounds.splice(outboundIndex, 1);
  410. }
  411. if (haveRules && outboundIndex === -1) {
  412. newTemplateSettings.outbounds.push(setting);
  413. }
  414. this.templateSettings = newTemplateSettings;
  415. },
  416. templateRuleGetter(routeSettings) {
  417. const { property, outboundTag } = routeSettings;
  418. let result = [];
  419. if (this.templateSettings != null) {
  420. this.templateSettings.routing.rules.forEach(
  421. (routingRule) => {
  422. if (
  423. routingRule.hasOwnProperty(property) &&
  424. routingRule.hasOwnProperty("outboundTag") &&
  425. routingRule.outboundTag === outboundTag
  426. ) {
  427. result.push(...routingRule[property]);
  428. }
  429. }
  430. );
  431. }
  432. return result;
  433. },
  434. templateRuleSetter(routeSettings) {
  435. const { data, property, outboundTag } = routeSettings;
  436. const oldTemplateSettings = this.templateSettings;
  437. const newTemplateSettings = oldTemplateSettings;
  438. currentProperty = this.templateRuleGetter({ outboundTag, property })
  439. if (currentProperty.length == 0) {
  440. const propertyRule = {
  441. type: "field",
  442. outboundTag,
  443. [property]: data
  444. };
  445. newTemplateSettings.routing.rules.push(propertyRule);
  446. }
  447. else {
  448. const newRules = [];
  449. insertedOnce = false;
  450. newTemplateSettings.routing.rules.forEach(
  451. (routingRule) => {
  452. if (
  453. routingRule.hasOwnProperty(property) &&
  454. routingRule.hasOwnProperty("outboundTag") &&
  455. routingRule.outboundTag === outboundTag
  456. ) {
  457. if (!insertedOnce && data.length > 0) {
  458. insertedOnce = true;
  459. routingRule[property] = data;
  460. newRules.push(routingRule);
  461. }
  462. }
  463. else {
  464. newRules.push(routingRule);
  465. }
  466. }
  467. );
  468. newTemplateSettings.routing.rules = newRules;
  469. }
  470. this.templateSettings = newTemplateSettings;
  471. }
  472. },
  473. async mounted() {
  474. await this.getXraySetting();
  475. while (true) {
  476. await PromiseUtil.sleep(600);
  477. this.saveBtnDisable = this.oldXraySetting === this.xraySetting;
  478. }
  479. },
  480. computed: {
  481. templateSettings: {
  482. get: function () { return this.xraySetting ? JSON.parse(this.xraySetting) : null; },
  483. set: function (newValue) { this.xraySetting = JSON.stringify(newValue, null, 2); },
  484. },
  485. inboundSettings: {
  486. get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.inbounds, null, 2) : null; },
  487. set: function (newValue) {
  488. newTemplateSettings = this.templateSettings;
  489. newTemplateSettings.inbounds = JSON.parse(newValue);
  490. this.templateSettings = newTemplateSettings;
  491. },
  492. },
  493. outboundSettings: {
  494. get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.outbounds, null, 2) : null; },
  495. set: function (newValue) {
  496. newTemplateSettings = this.templateSettings;
  497. newTemplateSettings.outbounds = JSON.parse(newValue);
  498. this.templateSettings = newTemplateSettings;
  499. },
  500. },
  501. routingRuleSettings: {
  502. get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.routing.rules, null, 2) : null; },
  503. set: function (newValue) {
  504. newTemplateSettings = this.templateSettings;
  505. newTemplateSettings.routing.rules = JSON.parse(newValue);
  506. this.templateSettings = newTemplateSettings;
  507. },
  508. },
  509. freedomStrategy: {
  510. get: function () {
  511. if (!this.templateSettings) return "AsIs";
  512. freedomOutbound = this.templateSettings.outbounds.find((o) => o.protocol === "freedom" && !o.tag);
  513. if (!freedomOutbound) return "AsIs";
  514. if (!freedomOutbound.settings || !freedomOutbound.settings.domainStrategy) return "AsIs";
  515. return freedomOutbound.settings.domainStrategy;
  516. },
  517. set: function (newValue) {
  518. newTemplateSettings = this.templateSettings;
  519. freedomOutboundIndex = newTemplateSettings.outbounds.findIndex((o) => o.protocol === "freedom" && !o.tag);
  520. if (!newTemplateSettings.outbounds[freedomOutboundIndex].settings) {
  521. newTemplateSettings.outbounds[freedomOutboundIndex].settings = {"domainStrategy": newValue};
  522. } else {
  523. newTemplateSettings.outbounds[freedomOutboundIndex].settings.domainStrategy = newValue;
  524. }
  525. this.templateSettings = newTemplateSettings;
  526. }
  527. },
  528. routingStrategy: {
  529. get: function () {
  530. if (!this.templateSettings || !this.templateSettings.routing || !this.templateSettings.routing.domainStrategy) return "AsIs";
  531. return this.templateSettings.routing.domainStrategy;
  532. },
  533. set: function (newValue) {
  534. newTemplateSettings = this.templateSettings;
  535. newTemplateSettings.routing.domainStrategy = newValue;
  536. this.templateSettings = newTemplateSettings;
  537. }
  538. },
  539. blockedIPs: {
  540. get: function () {
  541. return this.templateRuleGetter({ outboundTag: "blocked", property: "ip" });
  542. },
  543. set: function (newValue) {
  544. this.templateRuleSetter({ outboundTag: "blocked", property: "ip", data: newValue });
  545. }
  546. },
  547. blockedDomains: {
  548. get: function () {
  549. return this.templateRuleGetter({ outboundTag: "blocked", property: "domain" });
  550. },
  551. set: function (newValue) {
  552. this.templateRuleSetter({ outboundTag: "blocked", property: "domain", data: newValue });
  553. }
  554. },
  555. blockedProtocols: {
  556. get: function () {
  557. return this.templateRuleGetter({ outboundTag: "blocked", property: "protocol" });
  558. },
  559. set: function (newValue) {
  560. this.templateRuleSetter({ outboundTag: "blocked", property: "protocol", data: newValue });
  561. }
  562. },
  563. directIPs: {
  564. get: function () {
  565. return this.templateRuleGetter({ outboundTag: "direct", property: "ip" });
  566. },
  567. set: function (newValue) {
  568. this.templateRuleSetter({ outboundTag: "direct", property: "ip", data: newValue });
  569. this.syncRulesWithOutbound("direct", this.directSettings);
  570. }
  571. },
  572. directDomains: {
  573. get: function () {
  574. return this.templateRuleGetter({ outboundTag: "direct", property: "domain" });
  575. },
  576. set: function (newValue) {
  577. this.templateRuleSetter({ outboundTag: "direct", property: "domain", data: newValue });
  578. this.syncRulesWithOutbound("direct", this.directSettings);
  579. }
  580. },
  581. ipv4Domains: {
  582. get: function () {
  583. return this.templateRuleGetter({ outboundTag: "IPv4", property: "domain" });
  584. },
  585. set: function (newValue) {
  586. this.templateRuleSetter({ outboundTag: "IPv4", property: "domain", data: newValue });
  587. this.syncRulesWithOutbound("IPv4", this.ipv4Settings);
  588. }
  589. },
  590. warpDomains: {
  591. get: function () {
  592. return this.templateRuleGetter({ outboundTag: "WARP", property: "domain" });
  593. },
  594. set: function (newValue) {
  595. this.templateRuleSetter({ outboundTag: "WARP", property: "domain", data: newValue });
  596. this.syncRulesWithOutbound("WARP", this.warpSettings);
  597. }
  598. },
  599. manualBlockedIPs: {
  600. get: function () { return JSON.stringify(this.blockedIPs, null, 2); },
  601. set: debounce(function (value) { this.blockedIPs = JSON.parse(value); }, 1000)
  602. },
  603. manualBlockedDomains: {
  604. get: function () { return JSON.stringify(this.blockedDomains, null, 2); },
  605. set: debounce(function (value) { this.blockedDomains = JSON.parse(value); }, 1000)
  606. },
  607. manualDirectIPs: {
  608. get: function () { return JSON.stringify(this.directIPs, null, 2); },
  609. set: debounce(function (value) { this.directIPs = JSON.parse(value); }, 1000)
  610. },
  611. manualDirectDomains: {
  612. get: function () { return JSON.stringify(this.directDomains, null, 2); },
  613. set: debounce(function (value) { this.directDomains = JSON.parse(value); }, 1000)
  614. },
  615. manualIPv4Domains: {
  616. get: function () { return JSON.stringify(this.ipv4Domains, null, 2); },
  617. set: debounce(function (value) { this.ipv4Domains = JSON.parse(value); }, 1000)
  618. },
  619. manualWARPDomains: {
  620. get: function () { return JSON.stringify(this.warpDomains, null, 2); },
  621. set: debounce(function (value) { this.warpDomains = JSON.parse(value); }, 1000)
  622. },
  623. torrentSettings: {
  624. get: function () {
  625. return doAllItemsExist(this.settingsData.protocols.bittorrent, this.blockedProtocols);
  626. },
  627. set: function (newValue) {
  628. if (newValue) {
  629. this.blockedProtocols = [...this.blockedProtocols, ...this.settingsData.protocols.bittorrent];
  630. } else {
  631. this.blockedProtocols = this.blockedProtocols.filter(data => !this.settingsData.protocols.bittorrent.includes(data));
  632. }
  633. },
  634. },
  635. privateIpSettings: {
  636. get: function () {
  637. return doAllItemsExist(this.settingsData.ips.local, this.blockedIPs);
  638. },
  639. set: function (newValue) {
  640. if (newValue) {
  641. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.local];
  642. } else {
  643. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.local.includes(data));
  644. }
  645. },
  646. },
  647. AdsSettings: {
  648. get: function () {
  649. return doAllItemsExist(this.settingsData.domains.ads, this.blockedDomains);
  650. },
  651. set: function (newValue) {
  652. if (newValue) {
  653. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ads];
  654. } else {
  655. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ads.includes(data));
  656. }
  657. },
  658. },
  659. SpeedTestSettings: {
  660. get: function () {
  661. return doAllItemsExist(this.settingsData.domains.speedtest, this.blockedDomains);
  662. },
  663. set: function (newValue) {
  664. if (newValue) {
  665. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.speedtest];
  666. } else {
  667. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.speedtest.includes(data));
  668. }
  669. },
  670. },
  671. familyProtectSettings: {
  672. get: function () {
  673. if (!this.templateSettings || !this.templateSettings.dns || !this.templateSettings.dns.servers) return false;
  674. return doAllItemsExist(this.templateSettings.dns.servers, this.settingsData.familyProtectDNS.servers);
  675. },
  676. set: function (newValue) {
  677. newTemplateSettings = this.templateSettings;
  678. if (newValue) {
  679. newTemplateSettings.dns = this.settingsData.familyProtectDNS;
  680. } else {
  681. delete newTemplateSettings.dns;
  682. }
  683. this.templateSettings = newTemplateSettings;
  684. },
  685. },
  686. GoogleIPv4Settings: {
  687. get: function () {
  688. return doAllItemsExist(this.settingsData.domains.google, this.ipv4Domains);
  689. },
  690. set: function (newValue) {
  691. if (newValue) {
  692. this.ipv4Domains = [...this.ipv4Domains, ...this.settingsData.domains.google];
  693. } else {
  694. this.ipv4Domains = this.ipv4Domains.filter(data => !this.settingsData.domains.google.includes(data));
  695. }
  696. },
  697. },
  698. NetflixIPv4Settings: {
  699. get: function () {
  700. return doAllItemsExist(this.settingsData.domains.netflix, this.ipv4Domains);
  701. },
  702. set: function (newValue) {
  703. if (newValue) {
  704. this.ipv4Domains = [...this.ipv4Domains, ...this.settingsData.domains.netflix];
  705. } else {
  706. this.ipv4Domains = this.ipv4Domains.filter(data => !this.settingsData.domains.netflix.includes(data));
  707. }
  708. },
  709. },
  710. IRIpSettings: {
  711. get: function () {
  712. return doAllItemsExist(this.settingsData.ips.ir, this.blockedIPs);
  713. },
  714. set: function (newValue) {
  715. if (newValue) {
  716. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.ir];
  717. } else {
  718. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.ir.includes(data));
  719. }
  720. }
  721. },
  722. IRDomainSettings: {
  723. get: function () {
  724. return doAllItemsExist(this.settingsData.domains.ir, this.blockedDomains);
  725. },
  726. set: function (newValue) {
  727. if (newValue) {
  728. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ir];
  729. } else {
  730. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ir.includes(data));
  731. }
  732. }
  733. },
  734. ChinaIpSettings: {
  735. get: function () {
  736. return doAllItemsExist(this.settingsData.ips.cn, this.blockedIPs);
  737. },
  738. set: function (newValue) {
  739. if (newValue) {
  740. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.cn];
  741. } else {
  742. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.cn.includes(data));
  743. }
  744. }
  745. },
  746. ChinaDomainSettings: {
  747. get: function () {
  748. return doAllItemsExist(this.settingsData.domains.cn, this.blockedDomains);
  749. },
  750. set: function (newValue) {
  751. if (newValue) {
  752. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.cn];
  753. } else {
  754. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.cn.includes(data));
  755. }
  756. }
  757. },
  758. RussiaIpSettings: {
  759. get: function () {
  760. return doAllItemsExist(this.settingsData.ips.ru, this.blockedIPs);
  761. },
  762. set: function (newValue) {
  763. if (newValue) {
  764. this.blockedIPs = [...this.blockedIPs, ...this.settingsData.ips.ru];
  765. } else {
  766. this.blockedIPs = this.blockedIPs.filter(data => !this.settingsData.ips.ru.includes(data));
  767. }
  768. }
  769. },
  770. RussiaDomainSettings: {
  771. get: function () {
  772. return doAllItemsExist(this.settingsData.domains.ru, this.blockedDomains);
  773. },
  774. set: function (newValue) {
  775. if (newValue) {
  776. this.blockedDomains = [...this.blockedDomains, ...this.settingsData.domains.ru];
  777. } else {
  778. this.blockedDomains = this.blockedDomains.filter(data => !this.settingsData.domains.ru.includes(data));
  779. }
  780. }
  781. },
  782. IRIpDirectSettings: {
  783. get: function () {
  784. return doAllItemsExist(this.settingsData.ips.ir, this.directIPs);
  785. },
  786. set: function (newValue) {
  787. if (newValue) {
  788. this.directIPs = [...this.directIPs, ...this.settingsData.ips.ir];
  789. } else {
  790. this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.ir.includes(data));
  791. }
  792. }
  793. },
  794. IRDomainDirectSettings: {
  795. get: function () {
  796. return doAllItemsExist(this.settingsData.domains.ir, this.directDomains);
  797. },
  798. set: function (newValue) {
  799. if (newValue) {
  800. this.directDomains = [...this.directDomains, ...this.settingsData.domains.ir];
  801. } else {
  802. this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.ir.includes(data));
  803. }
  804. }
  805. },
  806. ChinaIpDirectSettings: {
  807. get: function () {
  808. return doAllItemsExist(this.settingsData.ips.cn, this.directIPs);
  809. },
  810. set: function (newValue) {
  811. if (newValue) {
  812. this.directIPs = [...this.directIPs, ...this.settingsData.ips.cn];
  813. } else {
  814. this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.cn.includes(data));
  815. }
  816. }
  817. },
  818. ChinaDomainDirectSettings: {
  819. get: function () {
  820. return doAllItemsExist(this.settingsData.domains.cn, this.directDomains);
  821. },
  822. set: function (newValue) {
  823. if (newValue) {
  824. this.directDomains = [...this.directDomains, ...this.settingsData.domains.cn];
  825. } else {
  826. this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.cn.includes(data));
  827. }
  828. }
  829. },
  830. RussiaIpDirectSettings: {
  831. get: function () {
  832. return doAllItemsExist(this.settingsData.ips.ru, this.directIPs);
  833. },
  834. set: function (newValue) {
  835. if (newValue) {
  836. this.directIPs = [...this.directIPs, ...this.settingsData.ips.ru];
  837. } else {
  838. this.directIPs = this.directIPs.filter(data => !this.settingsData.ips.ru.includes(data));
  839. }
  840. }
  841. },
  842. RussiaDomainDirectSettings: {
  843. get: function () {
  844. return doAllItemsExist(this.settingsData.domains.ru, this.directDomains);
  845. },
  846. set: function (newValue) {
  847. if (newValue) {
  848. this.directDomains = [...this.directDomains, ...this.settingsData.domains.ru];
  849. } else {
  850. this.directDomains = this.directDomains.filter(data => !this.settingsData.domains.ru.includes(data));
  851. }
  852. }
  853. },
  854. GoogleWARPSettings: {
  855. get: function () {
  856. return doAllItemsExist(this.settingsData.domains.google, this.warpDomains);
  857. },
  858. set: function (newValue) {
  859. if (newValue) {
  860. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.google];
  861. } else {
  862. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.google.includes(data));
  863. }
  864. },
  865. },
  866. OpenAIWARPSettings: {
  867. get: function () {
  868. return doAllItemsExist(this.settingsData.domains.openai, this.warpDomains);
  869. },
  870. set: function (newValue) {
  871. if (newValue) {
  872. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.openai];
  873. } else {
  874. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.openai.includes(data));
  875. }
  876. },
  877. },
  878. NetflixWARPSettings: {
  879. get: function () {
  880. return doAllItemsExist(this.settingsData.domains.netflix, this.warpDomains);
  881. },
  882. set: function (newValue) {
  883. if (newValue) {
  884. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.netflix];
  885. } else {
  886. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.netflix.includes(data));
  887. }
  888. },
  889. },
  890. SpotifyWARPSettings: {
  891. get: function () {
  892. return doAllItemsExist(this.settingsData.domains.spotify, this.warpDomains);
  893. },
  894. set: function (newValue) {
  895. if (newValue) {
  896. this.warpDomains = [...this.warpDomains, ...this.settingsData.domains.spotify];
  897. } else {
  898. this.warpDomains = this.warpDomains.filter(data => !this.settingsData.domains.spotify.includes(data));
  899. }
  900. },
  901. },
  902. },
  903. });
  904. </script>
  905. </body>
  906. </html>