diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/html/xui/xray.html | 166 | ||||
| -rw-r--r-- | web/html/xui/xray_balancer_modal.html | 111 | ||||
| -rw-r--r-- | web/html/xui/xray_rule_modal.html | 23 | ||||
| -rw-r--r-- | web/translation/translate.en_US.toml | 11 | ||||
| -rw-r--r-- | web/translation/translate.es_ES.toml | 11 | ||||
| -rw-r--r-- | web/translation/translate.fa_IR.toml | 11 | ||||
| -rw-r--r-- | web/translation/translate.ru_RU.toml | 11 | ||||
| -rw-r--r-- | web/translation/translate.vi_VN.toml | 11 | ||||
| -rw-r--r-- | web/translation/translate.zh_Hans.toml | 11 |
9 files changed, 363 insertions, 3 deletions
diff --git a/web/html/xui/xray.html b/web/html/xui/xray.html index 267103cb..a144c766 100644 --- a/web/html/xui/xray.html +++ b/web/html/xui/xray.html @@ -327,6 +327,14 @@ [[ rule.outboundTag ]] </a-popover> </template> + <template slot="balancer" slot-scope="text, rule, index"> + <a-popover :overlay-class-name="themeSwitcher.currentTheme"> + <template slot="content"> + <p v-if="rule.balancerTag">Balancer Tag: [[ rule.balancerTag ]]</p> + </template> + [[ rule.balancerTag ]] + </a-popover> + </template> <template slot="info" slot-scope="text, rule, index"> <a-popover placement="bottomRight" v-if="(rule.source+rule.sourcePort+rule.network+rule.protocol+rule.attrs+rule.ip+rule.domain+rule.port).length>0" @@ -452,6 +460,41 @@ </template> </a-table> </a-tab-pane> + <a-tab-pane key="tpl-balancers" tab='{{ i18n "pages.xray.Balancers"}}' style="padding-top: 20px;" force-render="true"> + <a-button type="primary" icon="plus" @click="addBalancer()" style="margin-bottom: 10px;">{{ i18n "pages.xray.balancer.addBalancer"}}</a-button> + <a-table :columns="balancerColumns" bordered + :row-key="r => r.key" + :data-source="balancersData" + :scroll="isMobile ? {} : { x: 200 }" + :pagination="false" + :indent-size="0" + :style="isMobile ? 'padding: 5px 0' : 'margin-left: 1px;'"> + <template slot="action" slot-scope="text, balancer, index"> + [[ index+1 ]] + <a-dropdown :trigger="['click']"> + <a-icon @click="e => e.preventDefault()" type="more" style="font-size: 16px; text-decoration: bold;"></a-icon> + <a-menu slot="overlay" :theme="themeSwitcher.currentTheme"> + <a-menu-item @click="editBalancer(index)"> + <a-icon type="edit"></a-icon> + {{ i18n "edit" }} + </a-menu-item> + <a-menu-item @click="deleteBalancer(index)"> + <span style="color: #FF4D4F"> + <a-icon type="delete"></a-icon> {{ i18n "delete"}} + </span> + </a-menu-item> + </a-menu> + </a-dropdown> + </template> + <template slot="strategy" slot-scope="text, balancer, index"> + <a-tag style="margin:0;" v-if="balancer.strategy=='random'" color="purple">Random</a-tag> + <a-tag style="margin:0;" v-if="balancer.strategy=='roundRobin'" color="green">Round Robin</a-tag> + </template> + <template slot="selector" slot-scope="text, balancer, index"> + <a-tag class="info-large-tag" style="margin:1;" v-for="sel in balancer.selector">[[ sel ]]</a-tag> + </template> + </a-table> + </a-tab-pane> <a-tab-pane key="tpl-advanced" tab='{{ i18n "pages.xray.advancedTemplate"}}' style="padding-top: 20px;" force-render="true"> <a-list-item-meta title='{{ i18n "pages.xray.Template"}}' description='{{ i18n "pages.xray.TemplateDesc"}}'></a-list-item-meta> <a-radio-group v-model="advSettings" @change="changeCode" button-style="solid" style="margin: 10px 0;" :size="isMobile ? 'small' : ''"> @@ -474,6 +517,7 @@ {{template "ruleModal"}} {{template "outModal"}} {{template "reverseModal"}} +{{template "balancerModal"}} {{template "warpModal"}} <script> const rulesColumns = [ @@ -490,9 +534,10 @@ { title: 'Domain', dataIndex: 'domain', align: 'center', width: 20, ellipsis: true }, { title: 'Port', dataIndex: 'port', align: 'center', width: 10, ellipsis: true }]}, { title: '{{ i18n "pages.xray.rules.inbound"}}', children: [ - { title: 'Inbound Tag', dataIndex: 'inboundTag', align: 'center', width: 20, ellipsis: true }, + { title: 'Inbound Tag', dataIndex: 'inboundTag', align: 'center', width: 15, ellipsis: true }, { title: 'Client Email', dataIndex: 'user', align: 'center', width: 20, ellipsis: true }]}, - { title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 20 }, + { title: '{{ i18n "pages.xray.rules.outbound"}}', dataIndex: 'outboundTag', align: 'center', width: 15 }, + { title: '{{ i18n "pages.xray.rules.balancer"}}', dataIndex: 'balancerTag', align: 'center', width: 15 }, ]; const rulesMobileColumns = [ @@ -517,6 +562,13 @@ { title: '{{ i18n "pages.xray.outbound.domain"}}', dataIndex: 'domain', align: 'center', width: 50 }, ]; + const balancerColumns = [ + { title: "#", align: 'center', width: 20, scopedSlots: { customRender: 'action' } }, + { title: '{{ i18n "pages.xray.balancer.tag"}}', dataIndex: 'tag', align: 'center', width: 50 }, + { title: '{{ i18n "pages.xray.balancer.balancerStrategy"}}', align: 'center', width: 50, scopedSlots: { customRender: 'strategy' }}, + { title: '{{ i18n "pages.xray.balancer.balancerSelectors"}}', align: 'center', width: 100, scopedSlots: { customRender: 'selector' }}, + ]; + const app = new Vue({ delimiters: ['[[', ']]'], el: '#app', @@ -895,6 +947,95 @@ this.refreshing = false; } }, + addBalancer() { + balancerModal.show({ + title: '{{ i18n "pages.xray.balancer.addBalancer"}}', + okText: '{{ i18n "pages.xray.balancer.addBalancer"}}', + balancerTags: this.balancersData.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag), + balancer: { + tag: '', + strategy: 'random', + selector: [] + }, + confirm: (balancer) => { + balancerModal.loading(); + newTemplateSettings = this.templateSettings; + if (newTemplateSettings.routing.balancers == undefined) { + newTemplateSettings.routing.balancers = []; + } + let tmpBalancer = { + 'tag': balancer.tag, + 'selector': balancer.selector + }; + if (balancer.strategy == 'roundRobin') { + tmpBalancer.strategy = { + 'type': balancer.strategy + }; + } + newTemplateSettings.routing.balancers.push(tmpBalancer); + this.templateSettings = newTemplateSettings; + balancerModal.close(); + }, + isEdit: false + }); + }, + editBalancer(index) { + const oldTag = this.balancersData[index].tag; + balancerModal.show({ + title: '{{ i18n "pages.xray.balancer.editBalancer"}}', + okText: '{{ i18n "sure" }}', + balancerTags: this.balancersData.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag), + balancer: this.balancersData[index], + confirm: (balancer) => { + balancerModal.loading(); + newTemplateSettings = this.templateSettings; + + let tmpBalancer = { + 'tag': balancer.tag, + 'selector': balancer.selector + }; + if (balancer.strategy == 'roundRobin') { + tmpBalancer.strategy = { + 'type': balancer.strategy + }; + } + + newTemplateSettings.routing.balancers[index] = tmpBalancer; + // change edited tag if used in rule section + if (oldTag != balancer.tag) { + newTemplateSettings.routing.rules.forEach((rule) => { + if (rule.balancerTag && rule.balancerTag == oldTag) { + rule.balancerTag = balancer.tag; + } + }); + } + this.templateSettings = newTemplateSettings; + balancerModal.close(); + }, + isEdit: true + }); + }, + deleteBalancer(index) { + newTemplateSettings = this.templateSettings; + + //remove from balancers + const oldTag = this.balancersData[index].tag; + this.balancersData.splice(index, 1); + + // remove from settings + let realIndex = newTemplateSettings.routing.balancers.findIndex((b) => b.tag == oldTag); + newTemplateSettings.routing.balancers.splice(realIndex, 1); + + // remove related routing rules + let rules = []; + newTemplateSettings.routing.rules.forEach((r) => { + if (!r.balancerTag || r.balancerTag != oldTag) { + rules.push(r); + } + }); + newTemplateSettings.routing.rules = rules; + this.templateSettings = newTemplateSettings; + }, addReverse(){ reverseModal.show({ title: '{{ i18n "pages.xray.outbound.addReverse"}}', @@ -1084,6 +1225,27 @@ return data; }, }, + balancersData: { + get: function () { + data = [] + if (this.templateSettings != null && this.templateSettings.routing != null && this.templateSettings.routing.balancers != null) { + this.templateSettings.routing.balancers.forEach((o, index) => { + let strategy = "random" + if (o.strategy && o.strategy.type == "roundRobin") { + strategy = o.strategy.type + } + + data.push({ + 'key': index, + 'tag': o.tag ? o.tag : "", + 'strategy': strategy, + 'selector': o.selector ? o.selector : [] + }); + }); + } + return data; + } + }, routingRuleSettings: { get: function () { return this.templateSettings ? JSON.stringify(this.templateSettings.routing.rules, null, 2) : null; }, set: function (newValue) { diff --git a/web/html/xui/xray_balancer_modal.html b/web/html/xui/xray_balancer_modal.html new file mode 100644 index 00000000..11eea378 --- /dev/null +++ b/web/html/xui/xray_balancer_modal.html @@ -0,0 +1,111 @@ +{{define "balancerModal"}} +<a-modal + id="balancer-modal" + v-model="balancerModal.visible" + :title="balancerModal.title" + @ok="balancerModal.ok" + :confirm-loading="balancerModal.confirmLoading" + :ok-button-props="{ props: { disabled: !balancerModal.isValid } }" + :closable="true" + :mask-closable="false" + :ok-text="balancerModal.okText" + cancel-text='{{ i18n "close" }}' + :class="themeSwitcher.currentTheme"> + <a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }"> + <a-form-item label='{{ i18n "pages.xray.balancer.tag" }}' has-feedback + :validate-status="balancerModal.duplicateTag? 'warning' : 'success'"> + <a-input v-model.trim="balancerModal.balancer.tag" @change="balancerModal.check()" + placeholder='{{ i18n "balancerModal.balancer.tagDesc" }}'></a-input> + </a-form-item> + <a-form-item label='{{ i18n "pages.xray.balancer.balancerStrategy" }}'> + <a-select v-model="balancerModal.balancer.strategy" :dropdown-class-name="themeSwitcher.currentTheme"> + <a-select-option value="random">Random</a-select-option> + <a-select-option value="roundRobin">Round Robin</a-select-option> + </a-select> + </a-form-item> + <a-form-item label='{{ i18n "pages.xray.balancer.balancerSelectors" }}' has-feedback :validate-status="balancerModal.emptySelector? 'warning' : 'success'"> + <a-select v-model="balancerModal.balancer.selector" mode="tags" @change="balancerModal.checkSelector()" + :dropdown-class-name="themeSwitcher.currentTheme"> + <a-select-option v-for="tag in balancerModal.outboundTags" :value="tag">[[ tag ]]</a-select-option> + </a-select> + </a-form-item> + </table> + </a-form> +</a-modal> +<script> + const balancerModal = { + title: '', + visible: false, + confirmLoading: false, + okText: '{{ i18n "sure" }}', + isEdit: false, + confirm: null, + duplicateTag: false, + emptySelector: false, + balancer: { + tag: '', + strategy: 'random', + selector: [] + }, + outboundTags: [], + balancerTags:[], + ok() { + if (balancerModal.balancer.selector.length == 0) { + balancerModal.emptySelector = true; + return; + } + balancerModal.emptySelector = false; + ObjectUtil.execute(balancerModal.confirm, balancerModal.balancer); + }, + show({ title = '', okText = '{{ i18n "sure" }}', balancerTags = [], balancer, confirm = (balancer) => { }, isEdit = false }) { + this.title = title; + this.okText = okText; + this.confirm = confirm; + this.visible = true; + if (isEdit) { + balancerModal.balancer = balancer; + } else { + balancerModal.balancer = { + tag: '', + strategy: 'random', + selector: [] + }; + } + this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag); + this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag); + this.isEdit = isEdit; + this.check() + }, + close() { + balancerModal.visible = false; + balancerModal.loading(false); + }, + loading(loading) { + balancerModal.confirmLoading = loading; + }, + check() { + if (balancerModal.balancer.tag == '' || balancerModal.balancerTags.includes(balancerModal.balancer.tag)) { + this.duplicateTag = true; + this.isValid = false; + } else { + this.duplicateTag = false; + this.isValid = true; + } + }, + checkSelector() { + balancerModal.emptySelector = balancerModal.balancer.selector.length == 0; + } + }; + + new Vue({ + delimiters: ['[[', ']]'], + el: '#balancer-modal', + data: { + balancerModal: balancerModal + }, + methods: { + } + }); + +</script> +{{end}}
\ No newline at end of file diff --git a/web/html/xui/xray_rule_modal.html b/web/html/xui/xray_rule_modal.html index 9ed9e06a..07cc3217 100644 --- a/web/html/xui/xray_rule_modal.html +++ b/web/html/xui/xray_rule_modal.html @@ -107,6 +107,19 @@ <a-select-option v-for="tag in ruleModal.outboundTags" :value="tag">[[ tag ]]</a-select-option> </a-select> </a-form-item> + <a-form-item> + <template slot="label"> + <a-tooltip> + <template slot="title"> + <span>{{ i18n "pages.xray.balancer.balancerDesc" }}</span> + </template> + Balancer Tag <a-icon type="question-circle"></a-icon> + </a-tooltip> + </template> + <a-select v-model="ruleModal.rule.balancerTag" :dropdown-class-name="themeSwitcher.currentTheme"> + <a-select-option v-for="tag in ruleModal.balancerTags" :value="tag">[[ tag ]]</a-select-option> + </a-select> + </a-form-item> </table> </a-form> </a-modal> @@ -133,11 +146,12 @@ protocol: [], attrs: [], outboundTag: "", + balancerTag: "", }, inboundTags: [], outboundTags: [], users: [], - balancerTag: [], + balancerTags: [], ok() { newRule = ruleModal.getResult(); ObjectUtil.execute(ruleModal.confirm, newRule); @@ -160,6 +174,7 @@ this.rule.protocol = rule.protocol; this.rule.attrs = rule.attrs ? Object.entries(rule.attrs) : []; this.rule.outboundTag = rule.outboundTag; + this.rule.balancerTag = rule.balancerTag ? rule.balancerTag : "" } else { this.rule = { domainMatcher: "", @@ -174,6 +189,7 @@ protocol: [], attrs: [], outboundTag: "", + balancerTag: "", } } this.isEdit = isEdit; @@ -186,6 +202,10 @@ } if(app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(b => b.tag)); } + + if (app.templateSettings.routing && app.templateSettings.routing.balancers) { + this.balancerTags = app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag) + } }, close() { ruleModal.visible = false; @@ -211,6 +231,7 @@ rule.protocol = value.protocol; rule.attrs = Object.fromEntries(value.attrs); rule.outboundTag = value.outboundTag; + rule.balancerTag = value.balancerTag; for (const [key, value] of Object.entries(rule)) { if ( diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 78c545f6..68a61181 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -388,6 +388,7 @@ "Inbounds" = "Inbounds" "InboundsDesc" = "Accepting the specific clients." "Outbounds" = "Outbounds" +"Balancers" = "Balancers" "OutboundsDesc" = "Set the outgoing traffic pathway." "Routings" = "Routing Rules" "RoutingsDesc" = "The priority of each rule is important!" @@ -406,6 +407,7 @@ "dest" = "Destination" "inbound" = "Inbound" "outbound" = "Outbound" +"balancer" = "Balancer" "info" = "Info" "add" = "Add Rule" "edit" = "Edit Rule" @@ -426,6 +428,15 @@ "portal" = "Portal" "intercon" = "Interconnection" +[pages.xray.balancer] +"addBalancer" = "Add Balancer" +"editBalancer" = "Edit Balancer" +"balancerStrategy" = "Strategy" +"balancerSelectors" = "Selectors" +"tag" = "Tag" +"tagDesc" = "Unique Tag" +"balancerDesc" = "It is not possible to use balancerTag and outboundTag at the same time. If used at the same time, only outboundTag will work." + [pages.xray.wireguard] "secretKey" = "Secret Key" "publicKey" = "Public Key" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index b21fe252..ae4408fd 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -388,6 +388,7 @@ "Inbounds" = "Entrante" "InboundsDesc" = "Cambia la plantilla de configuración para aceptar clientes específicos." "Outbounds" = "Salidas" +"Balancers" = "Equilibradores" "OutboundsDesc" = "Cambia la plantilla de configuración para definir formas de salida para este servidor." "Routings" = "Reglas de enrutamiento" "RoutingsDesc" = "¡La prioridad de cada regla es importante!" @@ -406,6 +407,7 @@ "dest" = "Destino" "inbound" = "Entrante" "outbound" = "saliente" +"balancer" = "Balancín" "info" = "Información" "add" = "Agregar regla" "edit" = "Editar regla" @@ -426,6 +428,15 @@ "portal" = "portal" "intercon" = "Interconexión" +[pages.xray.balancer] +"addBalancer" = "Agregar equilibrador" +"editBalancer" = "Editar balanceador" +"balancerStrategy" = "Estrategia" +"balancerSelectors" = "Selectores" +"tag" = "Etiqueta" +"tagDesc" = "etiqueta única" +"balancerDesc" = "No es posible utilizar balancerTag y outboundTag al mismo tiempo. Si se utilizan al mismo tiempo, sólo funcionará outboundTag." + [pages.xray.wireguard] "secretKey" = "Llave secreta" "publicKey" = "Llave pública" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 7abaf973..114487a4 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -388,6 +388,7 @@ "Inbounds" = "ورودیها" "InboundsDesc" = "پذیرش کلاینت خاص" "Outbounds" = "خروجیها" +"Balancers" = "بالانسرها" "OutboundsDesc" = "مسیر ترافیک خروجی را تنظیم کنید" "Routings" = "قوانین مسیریابی" "RoutingsDesc" = "اولویت هر قانون مهم است" @@ -406,6 +407,7 @@ "dest" = "مقصد" "inbound" = "ورودی" "outbound" = "خروجی" +"balancer" = "بالانسر" "info" = "اطلاعات" "add" = "افزودن قانون" "edit" = "ویرایش قانون" @@ -426,6 +428,15 @@ "portal" = "پورتال" "intercon" = "اتصال میانی" +[pages.xray.balancer] +"addBalancer" = "افزودن بالانسر" +"editBalancer" = "ویرایش بالانسر" +"balancerStrategy" = "استراتژی" +"balancerSelectors" = "انتخابگرها" +"tag" = "برچسب" +"tagDesc" = "برچسب یگانه" +"balancerDesc" = "امکان استفاده همزمان balancerTag و outboundTag باهم وجود ندارد. درصورت استفاده همزمان فقط outboundTag عمل خواهد کرد." + [pages.xray.wireguard] "secretKey" = "کلید شخصی" "publicKey" = "کلید عمومی" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index cbd1d4f9..3c6799da 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -388,6 +388,7 @@ "Inbounds" = "Входящие" "InboundsDesc" = "Изменение шаблона конфигурации для подключения определенных пользователей" "Outbounds" = "Исходящие" +"Balancers" = "Балансиры" "OutboundsDesc" = "Изменение шаблона конфигурации, чтобы определить исходящие пути для этого сервера" "Routings" = "Правила маршрутизации" "RoutingsDesc" = "Важен приоритет каждого правила!" @@ -406,6 +407,7 @@ "dest" = "Пункт назначения" "inbound" = "Входящий" "outbound" = "Исходящий" +"balancer" = "балансир" "info" = "Информация" "add" = "Добавить правило" "edit" = "Редактировать правило" @@ -426,6 +428,15 @@ "portal" = "Портал" "intercon" = "Соединение" +[pages.xray.balancer] +"addBalancer" = "Добавить балансир" +"editBalancer" = "Редактировать балансир" +"balancerStrategy" = "Стратегия" +"balancerSelectors" = "Селекторы" +"tag" = "Тег" +"tagDesc" = "уникальный тег" +"balancerDesc" = "Невозможно одновременно использовать balancerTag и outboundTag. При одновременном использовании будет работать только outboundTag." + [pages.xray.wireguard] "secretKey" = "Секретный ключ" "publicKey" = "Открытый ключ" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index f94b56b0..286d9c83 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -388,6 +388,7 @@ "Inbounds" = "Đầu vào"
"InboundsDesc" = "Thay đổi mẫu cấu hình để chấp nhận các máy khách cụ thể."
"Outbounds" = "Đầu ra"
+"Balancers" = "Cân bằng"
"OutboundsDesc" = "Thay đổi mẫu cấu hình để xác định các cách ra đi cho máy chủ này."
"Routings" = "Quy tắc định tuyến"
"RoutingsDesc" = "Mức độ ưu tiên của mỗi quy tắc đều quan trọng!"
@@ -406,6 +407,7 @@ "dest" = "Đích"
"inbound" = "Vào"
"outbound" = "Ra"
+"balancer" = "Cân bằng"
"info" = "Thông tin"
"add" = "Thêm quy tắc"
"edit" = "Chỉnh sửa quy tắc"
@@ -426,6 +428,15 @@ "portal" = "Cổng thông tin"
"intercon" = "Kết nối"
+[pages.xray.balancer]
+"addBalancer" = "Thêm cân bằng"
+"editBalancer" = "Chỉnh sửa cân bằng"
+"balancerStrategy" = "Chiến lược"
+"balancerSelectors" = "Bộ chọn"
+"tag" = "Thẻ"
+"tagDesc" = "thẻ duy nhất"
+"balancerDesc" = "Không thể sử dụng balancerTag và outboundTag cùng một lúc. Nếu sử dụng cùng lúc thì chỉ outboundTag mới hoạt động."
+
[pages.xray.wireguard]
"secretKey" = "Khoá bí mật"
"publicKey" = "Khóa công khai"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index d64e1331..bef4feeb 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -388,6 +388,7 @@ "Inbounds" = "入站" "InboundsDesc" = "更改配置模板接受特殊客户端" "Outbounds" = "出站" +"Balancers" = "平衡器" "OutboundsDesc" = "更改配置模板定义此服务器的传出方式" "Routings" = "路由规则" "RoutingsDesc" = "每条规则的优先级都很重要" @@ -406,6 +407,7 @@ "dest" = "目的地" "inbound" = "入站" "outbound" = "出站" +"balancer" = "平衡器" "info" = "信息" "add" = "添加规则" "edit" = "编辑规则" @@ -426,6 +428,15 @@ "portal" = "门户" "intercon" = "互连" +[pages.xray.balancer] +"addBalancer" = "添加平衡器" +"editBalancer" = "编辑平衡器" +"balancerStrategy" = "战略" +"balancerSelectors" = "选择器" +"tag" = "标签" +"tagDesc" = "唯一标记" +"balancerDesc" = "不能同时使用balancerTag和outboundTag。 如果同时使用,则只有outboundTag起作用。" + [pages.xray.wireguard] "secretKey" = "密钥" "publicKey" = "公钥" |
