| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 | 
							- {{ template "page/head_start" .}}
 
- {{ template "page/head_end" .}}
 
- {{ template "page/body_start" .}}
 
- <a-layout id="app" v-cloak :class="themeSwitcher.currentTheme + ' settings-page'">
 
-   <a-sidebar></a-sidebar>
 
-   <a-layout id="content-layout">
 
-     <a-layout-content>
 
-       <a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}'>
 
-         <transition name="list" appear>
 
-           <a-alert type="error" v-if="confAlerts.length>0 && loadingStates.fetched" :style="{ marginBottom: '10px' }"
 
-             message='{{ i18n "secAlertTitle" }}' color="red" show-icon closable>
 
-             <template slot="description">
 
-               <b>{{ i18n "secAlertConf" }}</b>
 
-               <ul>
 
-                 <li v-for="a in confAlerts">[[ a ]]</li>
 
-               </ul>
 
-             </template>
 
-           </a-alert>
 
-         </transition>
 
-         <transition name="list" appear>
 
-           <template>
 
-             <a-row v-if="!loadingStates.fetched">
 
-               <a-card
 
-                 :style="{ textAlign: 'center', padding: '30px 0', marginTop: '10px', background: 'transparent', border: 'none' }">
 
-                 <a-spin tip='{{ i18n "loading" }}'></a-spin>
 
-               </a-card>
 
-             </a-row>
 
-             <a-row :gutter="[isMobile ? 8 : 16, isMobile ? 0 : 12]" v-else>
 
-               <a-col>
 
-                 <a-card hoverable>
 
-                   <a-row :style="{ display: 'flex', flexWrap: 'wrap', alignItems: 'center' }">
 
-                     <a-col :xs="24" :sm="10" :style="{ padding: '4px' }">
 
-                       <a-space direction="horizontal">
 
-                         <a-button type="primary" :disabled="saveBtnDisable" @click="updateAllSetting">{{ i18n
 
-                           "pages.settings.save" }}</a-button>
 
-                         <a-button type="danger" :disabled="!saveBtnDisable" @click="restartPanel">{{ i18n
 
-                           "pages.settings.restartPanel" }}</a-button>
 
-                       </a-space>
 
-                     </a-col>
 
-                     <a-col :xs="24" :sm="14">
 
-                       <template>
 
-                         <div>
 
-                           <a-back-top :target="() => document.getElementById('content-layout')"
 
-                             visibility-height="200"></a-back-top>
 
-                           <a-alert type="warning" :style="{ float: 'right', width: 'fit-content' }"
 
-                             message='{{ i18n "pages.settings.infoDesc" }}' show-icon>
 
-                           </a-alert>
 
-                         </div>
 
-                       </template>
 
-                     </a-col>
 
-                   </a-row>
 
-                 </a-card>
 
-               </a-col>
 
-               <a-col>
 
-                 <a-tabs default-active-key="1">
 
-                   <a-tab-pane key="1" :style="{ paddingTop: '20px' }">
 
-                     <template #tab>
 
-                       <a-icon type="setting"></a-icon>
 
-                       <span>{{ i18n "pages.settings.panelSettings" }}</span>
 
-                     </template>
 
-                     {{ template "settings/panel/general" . }}
 
-                   </a-tab-pane>
 
-                   <a-tab-pane key="2" :style="{ paddingTop: '20px' }">
 
-                     <template #tab>
 
-                       <a-icon type="safety"></a-icon>
 
-                       <span>{{ i18n "pages.settings.securitySettings" }}</span>
 
-                     </template>
 
-                     {{ template "settings/panel/security" . }}
 
-                   </a-tab-pane>
 
-                   <a-tab-pane key="3" :style="{ paddingTop: '20px' }">
 
-                     <template #tab>
 
-                       <a-icon type="message"></a-icon>
 
-                       <span>{{ i18n "pages.settings.TGBotSettings" }}</span>
 
-                     </template>
 
-                     {{ template "settings/panel/telegram" . }}
 
-                   </a-tab-pane>
 
-                   <a-tab-pane key="4" :style="{ paddingTop: '20px' }">
 
-                     <template #tab>
 
-                       <a-icon type="cloud-server"></a-icon>
 
-                       <span>{{ i18n "pages.settings.subSettings" }}</span>
 
-                     </template>
 
-                     {{ template "settings/panel/subscription/general" . }}
 
-                   </a-tab-pane>
 
-                   <a-tab-pane key="5" v-if="allSetting.subJsonEnable" :style="{ paddingTop: '20px' }">
 
-                     <template #tab>
 
-                       <a-icon type="code"></a-icon>
 
-                       <span>{{ i18n "pages.settings.subSettings" }} (JSON)</span>
 
-                     </template>
 
-                     {{ template "settings/panel/subscription/json" . }}
 
-                   </a-tab-pane>
 
-                 </a-tabs>
 
-               </a-col>
 
-             </a-row>
 
-           </template>
 
-         </transition>
 
-       </a-spin>
 
-     </a-layout-content>
 
-   </a-layout>
 
- </a-layout>
 
- {{template "page/body_scripts" .}}
 
- <script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
 
- <script src="{{ .base_path }}assets/otpauth/otpauth.umd.min.js?{{ .cur_ver }}"></script>
 
- <script src="{{ .base_path }}assets/js/model/setting.js?{{ .cur_ver }}"></script>
 
- {{template "component/aSidebar" .}}
 
- {{template "component/aThemeSwitch" .}}
 
- {{template "component/aSettingListItem" .}}
 
- {{template "modals/twoFactorModal"}}
 
- <script>
 
-   const app = new Vue({
 
-     delimiters: ['[[', ']]'],
 
-     mixins: [MediaQueryMixin],
 
-     el: '#app',
 
-     data: {
 
-       themeSwitcher,
 
-       loadingStates: {
 
-         fetched: false,
 
-         spinning: false
 
-       },
 
-       oldAllSetting: new AllSetting(),
 
-       allSetting: new AllSetting(),
 
-       saveBtnDisable: true,
 
-       user: {},
 
-       lang: LanguageManager.getLanguage(),
 
-       inboundOptions: [],
 
-       remarkModels: { i: 'Inbound', e: 'Email', o: 'Other' },
 
-       remarkSeparators: [' ', '-', '_', '@', ':', '~', '|', ',', '.', '/'],
 
-       datepickerList: [{ name: 'Gregorian (Standard)', value: 'gregorian' }, { name: 'Jalalian (شمسی)', value: 'jalalian' }],
 
-       remarkSample: '',
 
-       defaultFragment: {
 
-         tag: "fragment",
 
-         protocol: "freedom",
 
-         settings: {
 
-           domainStrategy: "AsIs",
 
-           fragment: {
 
-             packets: "tlshello",
 
-             length: "100-200",
 
-             interval: "10-20",
 
-             maxSplit: "300-400"
 
-           }
 
-         },
 
-         streamSettings: {
 
-           sockopt: {
 
-             tcpKeepAliveIdle: 100,
 
-             tcpMptcp: true,
 
-             penetrate: true
 
-           }
 
-         }
 
-       },
 
-       defaultNoises: {
 
-         tag: "noises",
 
-         protocol: "freedom",
 
-         settings: {
 
-           domainStrategy: "AsIs",
 
-           noises: [
 
-             { type: "rand", packet: "10-20", delay: "10-16", applyTo: "ip" },
 
-           ],
 
-         },
 
-       },
 
-       defaultMux: {
 
-         enabled: true,
 
-         concurrency: 8,
 
-         xudpConcurrency: 16,
 
-         xudpProxyUDP443: "reject"
 
-       },
 
-       defaultRules: [
 
-         {
 
-           type: "field",
 
-           outboundTag: "direct",
 
-           domain: [
 
-             "geosite:category-ir"
 
-           ]
 
-         },
 
-         {
 
-           type: "field",
 
-           outboundTag: "direct",
 
-           ip: [
 
-             "geoip:private",
 
-             "geoip:ir"
 
-           ]
 
-         },
 
-       ],
 
-       directIPsOptions: [
 
-         { label: 'Private IP', value: 'geoip:private' },
 
-         { label: '🇮🇷 Iran', value: 'geoip:ir' },
 
-         { label: '🇨🇳 China', value: 'geoip:cn' },
 
-         { label: '🇷🇺 Russia', value: 'geoip:ru' },
 
-         { label: '🇻🇳 Vietnam', value: 'geoip:vn' },
 
-         { label: '🇪🇸 Spain', value: 'geoip:es' },
 
-         { label: '🇮🇩 Indonesia', value: 'geoip:id' },
 
-         { label: '🇺🇦 Ukraine', value: 'geoip:ua' },
 
-         { label: '🇹🇷 Türkiye', value: 'geoip:tr' },
 
-         { label: '🇧🇷 Brazil', value: 'geoip:br' },
 
-       ],
 
-       diretDomainsOptions: [
 
-         { label: 'Private DNS', value: 'geosite:private' },
 
-         { label: '🇮🇷 Iran', value: 'geosite:category-ir' },
 
-         { label: '🇨🇳 China', value: 'geosite:cn' },
 
-         { label: '🇷🇺 Russia', value: 'geosite:category-ru' },
 
-         { label: 'Apple', value: 'geosite:apple' },
 
-         { label: 'Meta', value: 'geosite:meta' },
 
-         { label: 'Google', value: 'geosite:google' },
 
-       ],
 
-       get remarkModel() {
 
-         rm = this.allSetting.remarkModel;
 
-         return rm.length > 1 ? rm.substring(1).split('') : [];
 
-       },
 
-       set remarkModel(value) {
 
-         rs = this.allSetting.remarkModel[0];
 
-         this.allSetting.remarkModel = rs + value.join('');
 
-         this.changeRemarkSample();
 
-       },
 
-       get remarkSeparator() {
 
-         return this.allSetting.remarkModel.length > 1 ? this.allSetting.remarkModel.charAt(0) : '-';
 
-       },
 
-       set remarkSeparator(value) {
 
-         this.allSetting.remarkModel = value + this.allSetting.remarkModel.substring(1);
 
-         this.changeRemarkSample();
 
-       },
 
-       get datepicker() {
 
-         return this.allSetting.datepicker ? this.allSetting.datepicker : 'gregorian';
 
-       },
 
-       set datepicker(value) {
 
-         this.allSetting.datepicker = value;
 
-       },
 
-       changeRemarkSample() {
 
-         sample = []
 
-         this.remarkModel.forEach(r => sample.push(this.remarkModels[r]));
 
-         this.remarkSample = sample.length == 0 ? '' : sample.join(this.remarkSeparator);
 
-       }
 
-     },
 
-     methods: {
 
-       loading(spinning = true) {
 
-         this.loadingStates.spinning = spinning;
 
-       },
 
-       async getAllSetting() {
 
-         const msg = await HttpUtil.post("/panel/setting/all");
 
-         if (msg.success) {
 
-           if (!this.loadingStates.fetched) {
 
-             this.loadingStates.fetched = true
 
-           }
 
-           this.oldAllSetting = new AllSetting(msg.obj);
 
-           this.allSetting = new AllSetting(msg.obj);
 
-           app.changeRemarkSample();
 
-           this.saveBtnDisable = true;
 
-         }
 
-       },
 
-       async loadInboundTags() {
 
-         const msg = await HttpUtil.get("/panel/api/inbounds/list");
 
-         if (msg && msg.success && Array.isArray(msg.obj)) {
 
-           this.inboundOptions = msg.obj.map(ib => ({
 
-             label: `${ib.tag} (${ib.protocol}@${ib.port})`,
 
-             value: ib.tag,
 
-           }));
 
-         } else {
 
-           this.inboundOptions = [];
 
-         }
 
-       },
 
-       async updateAllSetting() {
 
-         this.loading(true);
 
-         const msg = await HttpUtil.post("/panel/setting/update", this.allSetting);
 
-         this.loading(false);
 
-         if (msg.success) {
 
-           await this.getAllSetting();
 
-         }
 
-       },
 
-       async updateUser() {
 
-         const sendUpdateUserRequest = async () => {
 
-           this.loading(true);
 
-           const msg = await HttpUtil.post("/panel/setting/updateUser", this.user);
 
-           this.loading(false);
 
-           if (msg.success) {
 
-             this.user = {};
 
-             window.location.replace(basePath + "logout");
 
-           }
 
-         }
 
-         if (this.allSetting.twoFactorEnable) {
 
-           twoFactorModal.show({
 
-             title: '{{ i18n "pages.settings.security.twoFactorModalChangeCredentialsTitle" }}',
 
-             description: '{{ i18n "pages.settings.security.twoFactorModalChangeCredentialsStep" }}',
 
-             token: this.allSetting.twoFactorToken,
 
-             type: 'confirm',
 
-             confirm: (success) => {
 
-               if (success) {
 
-                 sendUpdateUserRequest();
 
-               }
 
-             }
 
-           })
 
-         } else {
 
-           sendUpdateUserRequest();
 
-         }
 
-       },
 
-       async restartPanel() {
 
-         await new Promise(resolve => {
 
-           this.$confirm({
 
-             title: '{{ i18n "pages.settings.restartPanel" }}',
 
-             content: '{{ i18n "pages.settings.restartPanelDesc" }}',
 
-             class: themeSwitcher.currentTheme,
 
-             okText: '{{ i18n "sure" }}',
 
-             cancelText: '{{ i18n "cancel" }}',
 
-             onOk: () => resolve(),
 
-           });
 
-         });
 
-         this.loading(true);
 
-         const msg = await HttpUtil.post("/panel/setting/restartPanel");
 
-         this.loading(false);
 
-         if (msg.success) {
 
-           this.loading(true);
 
-           await PromiseUtil.sleep(5000);
 
-           var { webCertFile, webKeyFile, webDomain: host, webPort: port, webBasePath: base } = this.allSetting;
 
-           if (host == this.oldAllSetting.webDomain) host = null;
 
-           if (port == this.oldAllSetting.webPort) port = null;
 
-           const isTLS = webCertFile !== "" || webKeyFile !== "";
 
-           const url = URLBuilder.buildURL({ host, port, isTLS, base, path: "panel/settings" });
 
-           window.location.replace(url);
 
-         }
 
-       },
 
-       toggleTwoFactor(newValue) {
 
-         if (newValue) {
 
-           const newTwoFactorToken = RandomUtil.randomBase32String()
 
-           twoFactorModal.show({
 
-             title: '{{ i18n "pages.settings.security.twoFactorModalSetTitle" }}',
 
-             token: newTwoFactorToken,
 
-             type: 'set',
 
-             confirm: (success) => {
 
-               if (success) {
 
-                 Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalSetSuccess" }}')
 
-                 this.allSetting.twoFactorToken = newTwoFactorToken
 
-               }
 
-               this.allSetting.twoFactorEnable = success
 
-             }
 
-           })
 
-         } else {
 
-           twoFactorModal.show({
 
-             title: '{{ i18n "pages.settings.security.twoFactorModalDeleteTitle" }}',
 
-             description: '{{ i18n "pages.settings.security.twoFactorModalRemoveStep" }}',
 
-             token: this.allSetting.twoFactorToken,
 
-             type: 'confirm',
 
-             confirm: (success) => {
 
-               if (success) {
 
-                 Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalDeleteSuccess" }}')
 
-                 this.allSetting.twoFactorEnable = false
 
-                 this.allSetting.twoFactorToken = ""
 
-               }
 
-             }
 
-           })
 
-         }
 
-       },
 
-       addNoise() {
 
-         const newNoise = { type: "rand", packet: "10-20", delay: "10-16", applyTo: "ip" };
 
-         this.noisesArray = [...this.noisesArray, newNoise];
 
-       },
 
-       removeNoise(index) {
 
-         const newNoises = [...this.noisesArray];
 
-         newNoises.splice(index, 1);
 
-         this.noisesArray = newNoises;
 
-       },
 
-       updateNoiseType(index, value) {
 
-         const updatedNoises = [...this.noisesArray];
 
-         updatedNoises[index] = { ...updatedNoises[index], type: value };
 
-         this.noisesArray = updatedNoises;
 
-       },
 
-       updateNoisePacket(index, value) {
 
-         const updatedNoises = [...this.noisesArray];
 
-         updatedNoises[index] = { ...updatedNoises[index], packet: value };
 
-         this.noisesArray = updatedNoises;
 
-       },
 
-       updateNoiseDelay(index, value) {
 
-         const updatedNoises = [...this.noisesArray];
 
-         updatedNoises[index] = { ...updatedNoises[index], delay: value };
 
-         this.noisesArray = updatedNoises;
 
-       },
 
-       updateNoiseApplyTo(index, value) {
 
-         const updatedNoises = [...this.noisesArray];
 
-         updatedNoises[index] = { ...updatedNoises[index], applyTo: value };
 
-         this.noisesArray = updatedNoises;
 
-       },
 
-     },
 
-     computed: {
 
-       ldapInboundTagList: {
 
-         get: function () {
 
-           const csv = this.allSetting.ldapInboundTags || "";
 
-           return csv.length ? csv.split(',').map(s => s.trim()).filter(Boolean) : [];
 
-         },
 
-         set: function (list) {
 
-           this.allSetting.ldapInboundTags = Array.isArray(list) ? list.join(',') : '';
 
-         }
 
-       },
 
-       fragment: {
 
-         get: function () { return this.allSetting?.subJsonFragment != ""; },
 
-         set: function (v) {
 
-           this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : "";
 
-         }
 
-       },
 
-       fragmentPackets: {
 
-         get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.packets : ""; },
 
-         set: function (v) {
 
-           if (v != "") {
 
-             newFragment = JSON.parse(this.allSetting.subJsonFragment);
 
-             newFragment.settings.fragment.packets = v;
 
-             this.allSetting.subJsonFragment = JSON.stringify(newFragment);
 
-           }
 
-         }
 
-       },
 
-       fragmentLength: {
 
-         get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.length : ""; },
 
-         set: function (v) {
 
-           if (v != "") {
 
-             newFragment = JSON.parse(this.allSetting.subJsonFragment);
 
-             newFragment.settings.fragment.length = v;
 
-             this.allSetting.subJsonFragment = JSON.stringify(newFragment);
 
-           }
 
-         }
 
-       },
 
-       fragmentInterval: {
 
-         get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.interval : ""; },
 
-         set: function (v) {
 
-           if (v != "") {
 
-             newFragment = JSON.parse(this.allSetting.subJsonFragment);
 
-             newFragment.settings.fragment.interval = v;
 
-             this.allSetting.subJsonFragment = JSON.stringify(newFragment);
 
-           }
 
-         }
 
-       },
 
-       fragmentMaxSplit: {
 
-         get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).settings.fragment.maxSplit : ""; },
 
-         set: function (v) {
 
-           if (v != "") {
 
-             newFragment = JSON.parse(this.allSetting.subJsonFragment);
 
-             newFragment.settings.fragment.maxSplit = v;
 
-             this.allSetting.subJsonFragment = JSON.stringify(newFragment);
 
-           }
 
-         }
 
-       },
 
-       noises: {
 
-         get() {
 
-           return this.allSetting?.subJsonNoises != "";
 
-         },
 
-         set(v) {
 
-           if (v) {
 
-             this.allSetting.subJsonNoises = JSON.stringify(this.defaultNoises);
 
-           } else {
 
-             this.allSetting.subJsonNoises = "";
 
-           }
 
-         }
 
-       },
 
-       noisesArray: {
 
-         get() {
 
-           return this.noises ? JSON.parse(this.allSetting.subJsonNoises).settings.noises : [];
 
-         },
 
-         set(value) {
 
-           if (this.noises) {
 
-             const newNoises = JSON.parse(this.allSetting.subJsonNoises);
 
-             newNoises.settings.noises = value;
 
-             this.allSetting.subJsonNoises = JSON.stringify(newNoises);
 
-           }
 
-         }
 
-       },
 
-       enableMux: {
 
-         get: function () { return this.allSetting?.subJsonMux != ""; },
 
-         set: function (v) {
 
-           this.allSetting.subJsonMux = v ? JSON.stringify(this.defaultMux) : "";
 
-         }
 
-       },
 
-       muxConcurrency: {
 
-         get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).concurrency : -1; },
 
-         set: function (v) {
 
-           newMux = JSON.parse(this.allSetting.subJsonMux);
 
-           newMux.concurrency = v;
 
-           this.allSetting.subJsonMux = JSON.stringify(newMux);
 
-         }
 
-       },
 
-       muxXudpConcurrency: {
 
-         get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpConcurrency : -1; },
 
-         set: function (v) {
 
-           newMux = JSON.parse(this.allSetting.subJsonMux);
 
-           newMux.xudpConcurrency = v;
 
-           this.allSetting.subJsonMux = JSON.stringify(newMux);
 
-         }
 
-       },
 
-       muxXudpProxyUDP443: {
 
-         get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpProxyUDP443 : "reject"; },
 
-         set: function (v) {
 
-           newMux = JSON.parse(this.allSetting.subJsonMux);
 
-           newMux.xudpProxyUDP443 = v;
 
-           this.allSetting.subJsonMux = JSON.stringify(newMux);
 
-         }
 
-       },
 
-       enableDirect: {
 
-         get: function () { return this.allSetting?.subJsonRules != ""; },
 
-         set: function (v) {
 
-           this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
 
-         }
 
-       },
 
-       directIPs: {
 
-         get: function () {
 
-           if (!this.enableDirect) return [];
 
-           const rules = JSON.parse(this.allSetting.subJsonRules);
 
-           if (!Array.isArray(rules)) return [];
 
-           const ipRule = rules.find(r => r.ip);
 
-           return ipRule?.ip ?? [];
 
-         },
 
-         set: function (v) {
 
-           let rules = JSON.parse(this.allSetting.subJsonRules);
 
-           if (!Array.isArray(rules)) return;
 
-           if (v.length == 0) {
 
-             rules = rules.filter(r => !r.ip);
 
-           } else {
 
-             let ruleIndex = rules.findIndex(r => r.ip);
 
-             if (ruleIndex == -1) ruleIndex = rules.push(this.defaultRules[1]) - 1;
 
-             rules[ruleIndex].ip = [];
 
-             v.forEach(d => {
 
-               rules[ruleIndex].ip.push(d);
 
-             });
 
-           }
 
-           this.allSetting.subJsonRules = JSON.stringify(rules);
 
-         }
 
-       },
 
-       directDomains: {
 
-         get: function () {
 
-           if (!this.enableDirect) return [];
 
-           const rules = JSON.parse(this.allSetting.subJsonRules);
 
-           if (!Array.isArray(rules)) return [];
 
-           const domainRule = rules.find(r => r.domain);
 
-           return domainRule?.domain ?? [];
 
-         },
 
-         set: function (v) {
 
-           let rules = JSON.parse(this.allSetting.subJsonRules);
 
-           if (!Array.isArray(rules)) return;
 
-           if (v.length == 0) {
 
-             rules = rules.filter(r => !r.domain);
 
-           } else {
 
-             let ruleIndex = rules.findIndex(r => r.domain);
 
-             if (ruleIndex == -1) ruleIndex = rules.push(this.defaultRules[0]) - 1;
 
-             rules[ruleIndex].domain = v;
 
-           }
 
-           this.allSetting.subJsonRules = JSON.stringify(rules);
 
-         }
 
-       },
 
-       confAlerts: {
 
-         get: function () {
 
-           if (!this.allSetting) return [];
 
-           var alerts = []
 
-           if (window.location.protocol !== "https:") alerts.push('{{ i18n "secAlertSSL" }}');
 
-           if (this.allSetting.webPort === 2053) alerts.push('{{ i18n "secAlertPanelPort" }}');
 
-           panelPath = window.location.pathname.split('/').length < 4
 
-           if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}');
 
-           if (this.allSetting.subEnable) {
 
-             subPath = this.allSetting.subURI.length > 0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath;
 
-             if (subPath == '/sub/') alerts.push('{{ i18n "secAlertSubURI" }}');
 
-           }
 
-           if (this.allSetting.subJsonEnable) {
 
-             subJsonPath = this.allSetting.subJsonURI.length > 0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath;
 
-             if (subJsonPath == '/json/') alerts.push('{{ i18n "secAlertSubJsonURI" }}');
 
-           }
 
-           return alerts
 
-         }
 
-       }
 
-     },
 
-     async mounted() {
 
-       await this.getAllSetting();
 
-       await this.loadInboundTags();
 
-       while (true) {
 
-         await PromiseUtil.sleep(1000);
 
-         this.saveBtnDisable = this.oldAllSetting.equals(this.allSetting);
 
-       }
 
-     }
 
-   });
 
- </script>
 
- {{ template "page/body_end" .}}
 
 
  |