From e28efa7adc4b6e53557d6a6f93cfa31ed114048d Mon Sep 17 00:00:00 2001 From: acidroper Date: Mon, 30 Mar 2026 21:10:57 +0500 Subject: [PATCH] feat: add support for 'extra' field in VLESS clients with XHTTP transmission - Persist 'extra' field in the database. - Update UI to include 'XHTTP Extra raw JSON' input in single and bulk client forms. - Include 'extra' parameter in VLESS client URLs generated in the panel. - Support 'extra' parameter in subscription link generation on the backend. --- database/model/model.go | 1 + sub/subService.go | 3 +++ web/assets/js/model/inbound.js | 15 ++++++++++++--- web/html/form/client.html | 3 +++ web/html/modals/client_bulk_modal.html | 8 ++++++++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/database/model/model.go b/database/model/model.go index 6225df52..e2ecc56c 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -121,4 +121,5 @@ type Client struct { 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 + Extra string `json:"extra,omitempty" form:"extra"` // XHTTP extra raw JSON } diff --git a/sub/subService.go b/sub/subService.go index 818f193b..6b9d2697 100644 --- a/sub/subService.go +++ b/sub/subService.go @@ -406,6 +406,9 @@ func (s *SubService) genVlessLink(inbound *model.Inbound, email string) string { params["host"] = searchHost(headers) } params["mode"] = xhttp["mode"].(string) + if clients[clientIndex].Extra != "" { + params["extra"] = clients[clientIndex].Extra + } } security, _ := stream["security"].(string) if security == "tls" { diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js index b6059cf7..0267715e 100644 --- a/web/assets/js/model/inbound.js +++ b/web/assets/js/model/inbound.js @@ -1418,7 +1418,7 @@ class Inbound extends XrayCommonClass { return 'vmess://' + Base64.encode(JSON.stringify(obj, null, 2)); } - genVLESSLink(address = '', port = this.port, forceTls, remark = '', clientId, flow) { + genVLESSLink(address = '', port = this.port, forceTls, remark = '', clientId, flow, extra = '') { const uuid = clientId; const type = this.stream.network; const security = forceTls == 'same' ? this.stream.security : forceTls; @@ -1465,6 +1465,12 @@ class Inbound extends XrayCommonClass { params.set("path", xhttp.path); params.set("host", xhttp.host?.length > 0 ? xhttp.host : this.getHeader(xhttp, 'host')); params.set("mode", xhttp.mode); + if (extra && extra.trim() !== "") { + // xray handles it but let's make sure it's valid json structure. + // The client already inputs it or handles it. + // We just pass the string. + params.set("extra", extra.trim()); + } break; } @@ -1716,7 +1722,7 @@ class Inbound extends XrayCommonClass { case Protocols.VMESS: return this.genVmessLink(address, port, forceTls, remark, client.id, client.security); case Protocols.VLESS: - return this.genVLESSLink(address, port, forceTls, remark, client.id, client.flow); + return this.genVLESSLink(address, port, forceTls, remark, client.id, client.flow, client.extra); case Protocols.SHADOWSOCKS: return this.genSSLink(address, port, forceTls, remark, this.isSSMultiUser ? client.password : ''); case Protocols.TROJAN: @@ -2059,7 +2065,8 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { comment = '', reset = 0, created_at = undefined, - updated_at = undefined + updated_at = undefined, + extra = '' ) { super(); this.id = id; @@ -2075,6 +2082,7 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { this.reset = reset; this.created_at = created_at; this.updated_at = updated_at; + this.extra = extra; } static fromJson(json = {}) { @@ -2092,6 +2100,7 @@ Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { json.reset, json.created_at, json.updated_at, + json.extra ); } diff --git a/web/html/form/client.html b/web/html/form/client.html index 908f28d2..68d418ba 100644 --- a/web/html/form/client.html +++ b/web/html/form/client.html @@ -113,6 +113,9 @@ [[ key ]] + + +