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.
This commit is contained in:
acidroper 2026-03-30 21:10:57 +05:00
parent 38d87230d3
commit e28efa7adc
5 changed files with 27 additions and 3 deletions

View file

@ -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
}

View file

@ -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" {

View file

@ -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
);
}

View file

@ -113,6 +113,9 @@
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-if="inbound.protocol === Protocols.VLESS && inbound.network === 'xhttp'" label='XHTTP Extra raw JSON'>
<a-textarea v-model.trim="client.extra" :auto-size="{ minRows: 2, maxRows: 5 }" placeholder='{"xmux":{"maxConnections":1}}'></a-textarea>
</a-form-item>
<a-form-item>
<template slot="label">
<a-tooltip>

View file

@ -39,6 +39,9 @@
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-if="clientsBulkModal.inbound.protocol === Protocols.VLESS && clientsBulkModal.inbound.network === 'xhttp'" label='XHTTP Extra raw JSON'>
<a-textarea v-model.trim="clientsBulkModal.extra" :auto-size="{ minRows: 2, maxRows: 5 }" placeholder='{"xmux":{"maxConnections":1}}'></a-textarea>
</a-form-item>
<a-form-item v-if="app.subSettings?.enable">
<template slot="label">
<a-tooltip>
@ -147,6 +150,7 @@
tgId: '',
security: "auto",
flow: "",
extra: "",
delayedStart: false,
reset: 0,
ok() {
@ -175,6 +179,9 @@
if (clientsBulkModal.inbound.canEnableTlsFlow()) {
newClient.flow = clientsBulkModal.flow;
}
if (clientsBulkModal.inbound.protocol === Protocols.VLESS && clientsBulkModal.inbound.network === 'xhttp') {
newClient.extra = clientsBulkModal.extra;
}
newClient.reset = clientsBulkModal.reset;
clients.push(newClient);
}
@ -203,6 +210,7 @@
this.tgId = '';
this.security = "auto";
this.flow = "";
this.extra = "";
this.dbInbound = new DBInbound(dbInbound);
this.inbound = dbInbound.toInbound();
this.delayedStart = false;