diff --git a/database/model/model.go b/database/model/model.go
index 01654d22..047780e5 100644
--- a/database/model/model.go
+++ b/database/model/model.go
@@ -129,22 +129,27 @@ type CustomGeoResource struct {
UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime;column:updated_at"`
}
+type ClientReverse struct {
+ Tag string `json:"tag"`
+}
+
// Client represents a client configuration for Xray inbounds with traffic limits and settings.
type Client struct {
- ID string `json:"id,omitempty"` // Unique client identifier
- Security string `json:"security"` // Security method (e.g., "auto", "aes-128-gcm")
- Password string `json:"password,omitempty"` // Client password
- Flow string `json:"flow,omitempty"` // Flow control (XTLS)
- Auth string `json:"auth,omitempty"` // Auth password (Hysteria)
- Email string `json:"email"` // Client email identifier
- LimitIP int `json:"limitIp"` // IP limit for this client
- TotalGB int64 `json:"totalGB" form:"totalGB"` // Total traffic limit in GB
- ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp
- Enable bool `json:"enable" form:"enable"` // Whether the client is enabled
- TgID int64 `json:"tgId" form:"tgId"` // Telegram user ID for notifications
- SubID string `json:"subId" form:"subId"` // Subscription identifier
- Comment string `json:"comment" form:"comment"` // Client comment
- Reset int `json:"reset" form:"reset"` // Reset period in days
- CreatedAt int64 `json:"created_at,omitempty"` // Creation timestamp
- UpdatedAt int64 `json:"updated_at,omitempty"` // Last update timestamp
+ ID string `json:"id,omitempty"` // Unique client identifier
+ Security string `json:"security"` // Security method (e.g., "auto", "aes-128-gcm")
+ Password string `json:"password,omitempty"` // Client password
+ Flow string `json:"flow,omitempty"` // Flow control (XTLS)
+ Reverse *ClientReverse `json:"reverse,omitempty"` // VLESS simple reverse proxy settings
+ Auth string `json:"auth,omitempty"` // Auth password (Hysteria)
+ Email string `json:"email"` // Client email identifier
+ LimitIP int `json:"limitIp"` // IP limit for this client
+ TotalGB int64 `json:"totalGB" form:"totalGB"` // Total traffic limit in GB
+ ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp
+ Enable bool `json:"enable" form:"enable"` // Whether the client is enabled
+ TgID int64 `json:"tgId" form:"tgId"` // Telegram user ID for notifications
+ SubID string `json:"subId" form:"subId"` // Subscription identifier
+ Comment string `json:"comment" form:"comment"` // Client comment
+ Reset int `json:"reset" form:"reset"` // Reset period in days
+ CreatedAt int64 `json:"created_at,omitempty"` // Creation timestamp
+ UpdatedAt int64 `json:"updated_at,omitempty"` // Last update timestamp
}
diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js
index dfffeffe..bb30dbcc 100644
--- a/web/assets/js/model/inbound.js
+++ b/web/assets/js/model/inbound.js
@@ -2616,27 +2616,34 @@ Inbound.VLESSSettings.VLESS = class extends Inbound.ClientBase {
constructor(
id = RandomUtil.randomUUID(),
flow = '',
+ reverseTag = '',
email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at,
) {
super(email, limitIp, totalGB, expiryTime, enable, tgId, subId, comment, reset, created_at, updated_at);
this.id = id;
this.flow = flow;
+ this.reverseTag = reverseTag;
}
static fromJson(json = {}) {
return new Inbound.VLESSSettings.VLESS(
json.id,
json.flow,
+ json.reverse?.tag ?? '',
...Inbound.ClientBase.commonArgsFromJson(json),
);
}
toJson() {
- return {
+ const json = {
id: this.id,
flow: this.flow,
...this._clientBaseToJson(),
};
+ if (this.reverseTag) {
+ json.reverse = { tag: this.reverseTag };
+ }
+ return json;
}
};
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index 1c2d67d9..9b740297 100644
--- a/web/assets/js/model/outbound.js
+++ b/web/assets/js/model/outbound.js
@@ -1747,13 +1747,14 @@ Outbound.VmessSettings = class extends CommonClass {
}
};
Outbound.VLESSSettings = class extends CommonClass {
- constructor(address, port, id, flow, encryption, testpre = 0, testseed = [900, 500, 900, 256]) {
+ constructor(address, port, id, flow, encryption, reverseTag = '', testpre = 0, testseed = [900, 500, 900, 256]) {
super();
this.address = address;
this.port = port;
this.id = id;
this.flow = flow;
this.encryption = encryption;
+ this.reverseTag = reverseTag;
this.testpre = testpre;
this.testseed = testseed;
}
@@ -1766,6 +1767,7 @@ Outbound.VLESSSettings = class extends CommonClass {
json.id,
json.flow,
json.encryption,
+ json.reverse?.tag || '',
json.testpre || 0,
json.testseed && json.testseed.length >= 4 ? json.testseed : [900, 500, 900, 256]
);
@@ -1779,6 +1781,9 @@ Outbound.VLESSSettings = class extends CommonClass {
flow: this.flow,
encryption: this.encryption,
};
+ if (!ObjectUtil.isEmpty(this.reverseTag)) {
+ result.reverse = { tag: this.reverseTag };
+ }
// Only include Vision settings when flow is set
if (this.flow && this.flow !== '') {
if (this.testpre > 0) {
diff --git a/web/html/form/client.html b/web/html/form/client.html
index 989bb471..737c102e 100644
--- a/web/html/form/client.html
+++ b/web/html/form/client.html
@@ -129,6 +129,18 @@
[[ key ]]
+
+
+
+
+ {{ i18n "pages.xray.outbound.reverseTagDesc" }}
+
+ {{ i18n "pages.xray.outbound.reverseTag" }}
+
+
+
+
+
diff --git a/web/html/form/outbound.html b/web/html/form/outbound.html
index 519c4cbc..96891b04 100644
--- a/web/html/form/outbound.html
+++ b/web/html/form/outbound.html
@@ -342,6 +342,18 @@
+
+
+
+
+ {{ i18n "pages.xray.outbound.reverseTagDesc" }}
+
+ {{ i18n "pages.xray.outbound.reverseTag" }}
+
+
+
+
+
diff --git a/web/html/modals/xray_reverse_modal.html b/web/html/modals/xray_reverse_modal.html
deleted file mode 100644
index addc8515..00000000
--- a/web/html/modals/xray_reverse_modal.html
+++ /dev/null
@@ -1,164 +0,0 @@
-{{define "modals/reverseModal"}}
-
-
-
-
- [[ x ]]
-
-
-
-
-
-
-
-
-
-
-
- [[ x ]]
-
-
-
-
- [[ x ]]
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-{{end}}
\ No newline at end of file
diff --git a/web/html/modals/xray_rule_modal.html b/web/html/modals/xray_rule_modal.html
index d079aac6..c542e029 100644
--- a/web/html/modals/xray_rule_modal.html
+++ b/web/html/modals/xray_rule_modal.html
@@ -204,13 +204,6 @@
if (app.enableDNS && !ObjectUtil.isEmpty(app.dnsTag)) this.inboundTags.push(app.dnsTag)
this.outboundTags = ["", ...app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj =>
obj.tag)];
- if (app.templateSettings.reverse) {
- if (app.templateSettings.reverse.bridges) {
- this.inboundTags.push(...app.templateSettings.reverse.bridges.map(b => b.tag));
- }
- if (app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(
- b => b.tag));
- }
this.balancerTags = [""];
if (app.templateSettings.routing && app.templateSettings.routing.balancers) {
this.balancerTags = ["", ...app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag))
diff --git a/web/html/settings/xray/reverse.html b/web/html/settings/xray/reverse.html
deleted file mode 100644
index c15b4a8c..00000000
--- a/web/html/settings/xray/reverse.html
+++ /dev/null
@@ -1,39 +0,0 @@
-{{define "settings/xray/reverse"}}
-
-
-
- {{ i18n "pages.xray.outbound.addReverse" }}
-
-
-
- [[ index+1 ]]
-
- e.preventDefault()" type="more"
- :style="{ fontSize: '16px', textDecoration: 'bold' }">
-
-
-
- {{ i18n "edit" }}
-
-
-
-
- {{ i18n "delete"}}
-
-
-
-
-
-
-
-
-
-
-
- {{ i18n "pages.xray.outbound.addReverse" }}
-
-
-
-{{end}}
\ No newline at end of file
diff --git a/web/html/xray.html b/web/html/xray.html
index 0907bcbe..1fffd76f 100644
--- a/web/html/xray.html
+++ b/web/html/xray.html
@@ -81,13 +81,6 @@
{{ template "settings/xray/outbounds" . }}
-
-
-
- {{ i18n "pages.xray.outbound.reverse"}}
-
- {{ template "settings/xray/reverse" . }}
-
@@ -135,7 +128,6 @@
{{template "component/aSettingListItem" .}}
{{template "modals/ruleModal" .}}
{{template "modals/outModal" .}}
-{{template "modals/reverseModal" .}}
{{template "modals/balancerModal" .}}
{{template "modals/dnsModal" .}}
{{template "modals/dnsPresetsModal" .}}
@@ -184,34 +176,6 @@
{ title: '{{ i18n "pages.xray.outbound.test" }}', align: 'center', width: 70, scopedSlots: { customRender: 'test' } },
];
- const reverseColumns = [{
- title: "#",
- align: 'center',
- width: 20,
- scopedSlots: {
- customRender: 'action'
- }
- },
- {
- title: '{{ i18n "pages.xray.outbound.type"}}',
- dataIndex: 'type',
- align: 'center',
- width: 50
- },
- {
- title: '{{ i18n "pages.xray.outbound.tag"}}',
- dataIndex: 'tag',
- align: 'center',
- width: 50
- },
- {
- title: '{{ i18n "pages.xray.outbound.domain"}}',
- dataIndex: 'domain',
- align: 'center',
- width: 50
- },
- ];
-
const balancerColumns = [{
title: "#",
align: 'center',
@@ -945,110 +909,6 @@
Vue.prototype.$message.error('{{ i18n "pages.xray.outbound.testError" }}: ' + error.message);
}
},
- addReverse() {
- reverseModal.show({
- title: '{{ i18n "pages.xray.outbound.addReverse"}}',
- okText: '{{ i18n "pages.xray.outbound.addReverse" }}',
- confirm: (reverse, rules) => {
- reverseModal.loading();
- if (reverse.tag.length > 0) {
- newTemplateSettings = this.templateSettings;
- if (newTemplateSettings.reverse == undefined) newTemplateSettings.reverse = {};
- if (newTemplateSettings.reverse[reverse.type + 's'] == undefined) newTemplateSettings.reverse[
- reverse.type + 's'] = [];
- newTemplateSettings.reverse[reverse.type + 's'].push({
- tag: reverse.tag,
- domain: reverse.domain
- });
- this.templateSettings = newTemplateSettings;
-
- // Add related rules
- this.templateSettings.routing.rules.push(...rules);
- this.routingRuleSettings = JSON.stringify(this.templateSettings.routing.rules);
- }
- reverseModal.close();
- },
- isEdit: false
- });
- },
- editReverse(index) {
- if (this.reverseData[index].type == "bridge") {
- oldRules = this.templateSettings.routing.rules.filter(r => r.inboundTag && r.inboundTag[0] == this
- .reverseData[index].tag);
- } else {
- oldRules = this.templateSettings.routing.rules.filter(r => r.outboundTag && r.outboundTag == this
- .reverseData[index].tag);
- }
- reverseModal.show({
- title: '{{ i18n "pages.xray.outbound.editReverse"}} ' + (index + 1),
- reverse: this.reverseData[index],
- rules: oldRules,
- confirm: (reverse, rules) => {
- reverseModal.loading();
- if (reverse.tag.length > 0) {
- oldData = this.reverseData[index];
- newTemplateSettings = this.templateSettings;
- oldReverseIndex = newTemplateSettings.reverse[oldData.type + 's'].findIndex(rs => rs.tag ==
- oldData.tag);
- oldRuleIndex0 = oldRules.length > 0 ? newTemplateSettings.routing.rules.findIndex(r => JSON
- .stringify(r) == JSON.stringify(oldRules[0])) : -1;
- oldRuleIndex1 = oldRules.length == 2 ? newTemplateSettings.routing.rules.findIndex(r => JSON
- .stringify(r) == JSON.stringify(oldRules[1])) : -1;
- if (oldData.type == reverse.type) {
- newTemplateSettings.reverse[oldData.type + 's'][oldReverseIndex] = {
- tag: reverse.tag,
- domain: reverse.domain
- };
- } else {
- newTemplateSettings.reverse[oldData.type + 's'].splice(oldReverseIndex, 1);
- // delete empty object
- if (newTemplateSettings.reverse[oldData.type + 's'].length == 0) Reflect.deleteProperty(
- newTemplateSettings.reverse, oldData.type + 's');
- // add other type of reverse if it is not exist
- if (!newTemplateSettings.reverse[reverse.type + 's']) newTemplateSettings.reverse[reverse
- .type + 's'] = [];
- newTemplateSettings.reverse[reverse.type + 's'].push({
- tag: reverse.tag,
- domain: reverse.domain
- });
- }
- this.templateSettings = newTemplateSettings;
-
- // Adjust Rules
- newRules = this.templateSettings.routing.rules;
- oldRuleIndex0 != -1 ? newRules[oldRuleIndex0] = rules[0] : newRules.push(rules[0]);
- oldRuleIndex1 != -1 ? newRules[oldRuleIndex1] = rules[1] : newRules.push(rules[1]);
- this.routingRuleSettings = JSON.stringify(newRules);
- }
- reverseModal.close();
- },
- isEdit: true
- });
- },
- deleteReverse(index) {
- oldData = this.reverseData[index];
- newTemplateSettings = this.templateSettings;
- reverseTypeObj = newTemplateSettings.reverse[oldData.type + 's'];
- realIndex = reverseTypeObj.findIndex(r => r.tag == oldData.tag && r.domain == oldData.domain);
- newTemplateSettings.reverse[oldData.type + 's'].splice(realIndex, 1);
-
- // delete empty objects
- if (reverseTypeObj.length == 0) Reflect.deleteProperty(newTemplateSettings.reverse, oldData.type + 's');
- if (Object.keys(newTemplateSettings.reverse).length === 0) Reflect.deleteProperty(newTemplateSettings,
- 'reverse');
-
- // delete related routing rules
- newRules = newTemplateSettings.routing.rules;
- if (oldData.type == "bridge") {
- newRules = newTemplateSettings.routing.rules.filter(r => !(r.inboundTag && r.inboundTag.length == 1 && r
- .inboundTag[0] == oldData.tag));
- } else if (oldData.type == "portal") {
- newRules = newTemplateSettings.routing.rules.filter(r => r.outboundTag != oldData.tag);
- }
- newTemplateSettings.routing.rules = newRules;
-
- this.templateSettings = newTemplateSettings;
- },
async refreshOutboundTraffic() {
if (!this.refreshing) {
this.refreshing = true;
@@ -1457,32 +1317,6 @@
return data;
},
},
- reverseData: {
- get: function() {
- data = []
- if (this.templateSettings != null && this.templateSettings.reverse != null) {
- if (this.templateSettings.reverse.bridges) {
- this.templateSettings.reverse.bridges.forEach((o, index) => {
- data.push({
- 'key': index,
- 'type': 'bridge',
- ...o
- });
- });
- }
- if (this.templateSettings.reverse.portals) {
- this.templateSettings.reverse.portals.forEach((o, index) => {
- data.push({
- 'key': index,
- 'type': 'portal',
- ...o
- });
- });
- }
- }
- return data;
- },
- },
routingRuleSettings: {
get: function() {
return this.templateSettings ? JSON.stringify(this.templateSettings.routing.rules, null, 2) : null;
diff --git a/web/service/xray.go b/web/service/xray.go
index 385936d5..b2d39646 100644
--- a/web/service/xray.go
+++ b/web/service/xray.go
@@ -148,7 +148,7 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
// clear client config for additional parameters
for key := range c {
- if key != "email" && key != "id" && key != "password" && key != "flow" && key != "method" && key != "auth" {
+ if key != "email" && key != "id" && key != "password" && key != "flow" && key != "method" && key != "auth" && key != "reverse" {
delete(c, key)
}
if flow, ok := c["flow"].(string); ok && flow == "xtls-rprx-vision-udp443" {
diff --git a/web/translation/translate.ar_EG.toml b/web/translation/translate.ar_EG.toml
index a509c7aa..b0d33dcf 100644
--- a/web/translation/translate.ar_EG.toml
+++ b/web/translation/translate.ar_EG.toml
@@ -584,6 +584,9 @@
"addReverse" = "أضف عكسي"
"editOutbound" = "عدل المخرج"
"editReverse" = "عدل العكسي"
+"reverseTag" = "وسم العكسي"
+"reverseTagDesc" = "وسم الخروج لبروكسي VLESS العكسي البسيط. اتركه فارغاً لتعطيله."
+"reverseTagPlaceholder" = "وسم الخروج (اتركه فارغاً للتعطيل)"
"tag" = "تاج"
"tagDesc" = "تاج فريد"
"address" = "العنوان"
diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml
index afa29080..25343fe4 100644
--- a/web/translation/translate.en_US.toml
+++ b/web/translation/translate.en_US.toml
@@ -584,6 +584,9 @@
"addReverse" = "Add Reverse"
"editOutbound" = "Edit Outbound"
"editReverse" = "Edit Reverse"
+"reverseTag" = "Reverse Tag"
+"reverseTagDesc" = "VLESS simple reverse proxy tag. Leave empty to disable."
+"reverseTagPlaceholder" = "reverse tag (leave empty to disable)"
"tag" = "Tag"
"tagDesc" = "Unique Tag"
"address" = "Address"
diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml
index 6802d1aa..7f7e9f71 100644
--- a/web/translation/translate.es_ES.toml
+++ b/web/translation/translate.es_ES.toml
@@ -584,6 +584,9 @@
"addReverse" = "Agregar reverso"
"editOutbound" = "Editar salida"
"editReverse" = "Editar reverso"
+"reverseTag" = "Etiqueta Reverso"
+"reverseTagDesc" = "Etiqueta de salida del proxy inverso simple VLESS. Dejar vacío para deshabilitar. Cuando se establece, las conexiones de este cliente pueden usarse como túnel de proxy inverso."
+"reverseTagPlaceholder" = "etiqueta de salida (vacío para deshabilitar)"
"tag" = "Etiqueta"
"tagDesc" = "etiqueta única"
"address" = "Dirección"
diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml
index 4f2792d7..c4d3eb27 100644
--- a/web/translation/translate.fa_IR.toml
+++ b/web/translation/translate.fa_IR.toml
@@ -584,6 +584,9 @@
"addReverse" = "افزودن معکوس"
"editOutbound" = "ویرایش خروجی"
"editReverse" = "ویرایش معکوس"
+"reverseTag" = "تگ معکوس"
+"reverseTagDesc" = "تگ خروجی پروکسی معکوس ساده VLESS. برای غیرفعال کردن خالی بگذارید. در صورت تنظیم، اتصالات این کلاینت میتوانند به عنوان تونل پروکسی معکوس استفاده شوند."
+"reverseTagPlaceholder" = "تگ خروجی (خالی = غیرفعال)"
"tag" = "برچسب"
"tagDesc" = "برچسب یگانه"
"address" = "آدرس"
diff --git a/web/translation/translate.id_ID.toml b/web/translation/translate.id_ID.toml
index 57615d89..bfede30e 100644
--- a/web/translation/translate.id_ID.toml
+++ b/web/translation/translate.id_ID.toml
@@ -584,6 +584,9 @@
"addReverse" = "Tambahkan Revers"
"editOutbound" = "Edit Keluar"
"editReverse" = "Edit Revers"
+"reverseTag" = "Tag Revers"
+"reverseTagDesc" = "Tag outbound proxy revers sederhana VLESS. Kosongkan untuk menonaktifkan."
+"reverseTagPlaceholder" = "tag outbound (kosong untuk menonaktifkan)"
"tag" = "Tag"
"tagDesc" = "Tag Unik"
"address" = "Alamat"
diff --git a/web/translation/translate.ja_JP.toml b/web/translation/translate.ja_JP.toml
index 62d9f789..2ee6c77c 100644
--- a/web/translation/translate.ja_JP.toml
+++ b/web/translation/translate.ja_JP.toml
@@ -584,6 +584,9 @@
"addReverse" = "リバース追加"
"editOutbound" = "アウトバウンド編集"
"editReverse" = "リバース編集"
+"reverseTag" = "リバースタグ"
+"reverseTagDesc" = "VLESSシンプルリバースプロキシのアウトバウンドタグ。無効にするには空欄にしてください。"
+"reverseTagPlaceholder" = "アウトバウンドタグ(空欄で無効)"
"tag" = "タグ"
"tagDesc" = "一意のタグ"
"address" = "アドレス"
diff --git a/web/translation/translate.pt_BR.toml b/web/translation/translate.pt_BR.toml
index 0c51772e..bb8d412d 100644
--- a/web/translation/translate.pt_BR.toml
+++ b/web/translation/translate.pt_BR.toml
@@ -584,6 +584,9 @@
"addReverse" = "Adicionar Reverso"
"editOutbound" = "Editar Saída"
"editReverse" = "Editar Reverso"
+"reverseTag" = "Tag de Reverso"
+"reverseTagDesc" = "Tag de saída do proxy reverso simples VLESS. Deixe vazio para desabilitar."
+"reverseTagPlaceholder" = "tag de saída (vazio para desabilitar)"
"tag" = "Tag"
"tagDesc" = "Tag Única"
"address" = "Endereço"
diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml
index abb3cf9e..299cc019 100644
--- a/web/translation/translate.ru_RU.toml
+++ b/web/translation/translate.ru_RU.toml
@@ -584,6 +584,9 @@
"addReverse" = "Создать реверс-прокси"
"editOutbound" = "Изменить исходящее подключение"
"editReverse" = "Редактировать реверс-прокси"
+"reverseTag" = "Тег реверс-прокси"
+"reverseTagDesc" = "Тег исходящего подключения для простого реверс-прокси VLESS. Оставьте пустым для отключения."
+"reverseTagPlaceholder" = "тег исходящего (пусто = отключено)"
"tag" = "Тег"
"tagDesc" = "Уникальный тег"
"address" = "Адрес"
diff --git a/web/translation/translate.tr_TR.toml b/web/translation/translate.tr_TR.toml
index 13a61c64..b08255af 100644
--- a/web/translation/translate.tr_TR.toml
+++ b/web/translation/translate.tr_TR.toml
@@ -584,6 +584,9 @@
"addReverse" = "Ters Ekle"
"editOutbound" = "Gideni Düzenle"
"editReverse" = "Tersi Düzenle"
+"reverseTag" = "Ters Etiket"
+"reverseTagDesc" = "VLESS basit ters proxy çıkış etiketi. Devre dışı bırakmak için boş bırakın."
+"reverseTagPlaceholder" = "çıkış etiketi (boş = devre dışı)"
"tag" = "Etiket"
"tagDesc" = "Benzersiz Etiket"
"address" = "Adres"
diff --git a/web/translation/translate.uk_UA.toml b/web/translation/translate.uk_UA.toml
index d99bf955..ba9e9f29 100644
--- a/web/translation/translate.uk_UA.toml
+++ b/web/translation/translate.uk_UA.toml
@@ -584,6 +584,9 @@
"addReverse" = "Додати реверс"
"editOutbound" = "Редагувати вихідні"
"editReverse" = "Редагувати реверс"
+"reverseTag" = "Тег реверс-проксі"
+"reverseTagDesc" = "Тег вихідного з'єднання для простого реверс-проксі VLESS. Залиште порожнім для вимкнення."
+"reverseTagPlaceholder" = "тег вихідного (порожнє = вимкнено)"
"tag" = "Тег"
"tagDesc" = "Унікальний тег"
"address" = "Адреса"
diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml
index db8c424a..77a2f236 100644
--- a/web/translation/translate.vi_VN.toml
+++ b/web/translation/translate.vi_VN.toml
@@ -584,6 +584,9 @@
"addReverse" = "Thêm đảo ngược"
"editOutbound" = "Chỉnh sửa gửi đi"
"editReverse" = "Chỉnh sửa ngược lại"
+"reverseTag" = "Thẻ Ngược"
+"reverseTagDesc" = "Thẻ outbound của proxy ngược đơn giản VLESS. Để trống để vô hiệu hóa."
+"reverseTagPlaceholder" = "thẻ outbound (để trống để vô hiệu hóa)"
"tag" = "Thẻ"
"tagDesc" = "thẻ duy nhất"
"address" = "Địa chỉ"
diff --git a/web/translation/translate.zh_CN.toml b/web/translation/translate.zh_CN.toml
index 428ea92a..9a0c694c 100644
--- a/web/translation/translate.zh_CN.toml
+++ b/web/translation/translate.zh_CN.toml
@@ -584,6 +584,9 @@
"addReverse" = "添加反向"
"editOutbound" = "编辑出站"
"editReverse" = "编辑反向"
+"reverseTag" = "反向标签"
+"reverseTagDesc" = "VLESS 简易反向代理出站标签。留空则禁用。设置后,此客户端的连接可用作反向代理隧道。"
+"reverseTagPlaceholder" = "出站标签(留空则禁用)"
"tag" = "标签"
"tagDesc" = "唯一标签"
"address" = "地址"
diff --git a/web/translation/translate.zh_TW.toml b/web/translation/translate.zh_TW.toml
index 6216c885..dc5883c9 100644
--- a/web/translation/translate.zh_TW.toml
+++ b/web/translation/translate.zh_TW.toml
@@ -584,6 +584,9 @@
"addReverse" = "新增反向"
"editOutbound" = "編輯出站"
"editReverse" = "編輯反向"
+"reverseTag" = "反向標籤"
+"reverseTagDesc" = "VLESS 簡易反向代理出站標籤。留空則停用。設定後,此客戶端的連線可作為反向代理隧道。"
+"reverseTagPlaceholder" = "出站標籤(留空則停用)"
"tag" = "標籤"
"tagDesc" = "唯一標籤"
"address" = "地址"