Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2026-04-27 00:04:47 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2026-04-27 00:04:47 +0300
commit0aca2d3b3d7e54f0408670db5f7075eaee190066 (patch)
tree7628c810f9b6105748b032500e83ca196f81e740 /web/assets
parent8529f4f0cfbe40e211959571807af7128408b960 (diff)
sub: kcp finalmask
Diffstat (limited to 'web/assets')
-rw-r--r--web/assets/js/model/inbound.js54
-rw-r--r--web/assets/js/model/outbound.js11
2 files changed, 64 insertions, 1 deletions
diff --git a/web/assets/js/model/inbound.js b/web/assets/js/model/inbound.js
index a091e1ef..ef9ad19c 100644
--- a/web/assets/js/model/inbound.js
+++ b/web/assets/js/model/inbound.js
@@ -1370,6 +1370,50 @@ class Inbound extends XrayCommonClass {
}
}
+ static hasShareableFinalMaskValue(value) {
+ if (value == null) {
+ return false;
+ }
+ if (Array.isArray(value)) {
+ return value.some(item => Inbound.hasShareableFinalMaskValue(item));
+ }
+ if (typeof value === 'object') {
+ return Object.values(value).some(item => Inbound.hasShareableFinalMaskValue(item));
+ }
+ if (typeof value === 'string') {
+ return value.length > 0;
+ }
+ return true;
+ }
+
+ static serializeFinalMask(finalmask) {
+ if (!finalmask) {
+ return '';
+ }
+ const value = typeof finalmask.toJson === 'function' ? finalmask.toJson() : finalmask;
+ return Inbound.hasShareableFinalMaskValue(value) ? JSON.stringify(value) : '';
+ }
+
+ // Export finalmask with the same compact JSON payload shape that
+ // v2rayN-compatible share links use: fm=<json>.
+ static applyFinalMaskToParams(finalmask, params) {
+ if (!params) return;
+ const payload = Inbound.serializeFinalMask(finalmask);
+ if (payload.length > 0) {
+ params.set("fm", payload);
+ }
+ }
+
+ // VMess links are a base64 JSON object, so keep the same fm payload
+ // under a flat property instead of a URL query string.
+ static applyFinalMaskToObj(finalmask, obj) {
+ if (!obj) return;
+ const payload = Inbound.serializeFinalMask(finalmask);
+ if (payload.length > 0) {
+ obj.fm = payload;
+ }
+ }
+
get clients() {
switch (this.protocol) {
case Protocols.VMESS: return this.settings.vmesses;
@@ -1590,6 +1634,8 @@ class Inbound extends XrayCommonClass {
Inbound.applyXhttpPaddingToObj(xhttp, obj);
}
+ Inbound.applyFinalMaskToObj(this.stream.finalmask, obj);
+
if (tls === 'tls') {
if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
obj.sni = this.stream.tls.sni;
@@ -1658,6 +1704,8 @@ class Inbound extends XrayCommonClass {
break;
}
+ Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
+
if (security === 'tls') {
params.set("security", "tls");
if (this.stream.isTls) {
@@ -1761,6 +1809,8 @@ class Inbound extends XrayCommonClass {
break;
}
+ Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
+
if (security === 'tls') {
params.set("security", "tls");
if (this.stream.isTls) {
@@ -1840,6 +1890,8 @@ class Inbound extends XrayCommonClass {
break;
}
+ Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
+
if (security === 'tls') {
params.set("security", "tls");
if (this.stream.isTls) {
@@ -1907,6 +1959,8 @@ class Inbound extends XrayCommonClass {
}
}
+ Inbound.applyFinalMaskToParams(this.stream.finalmask, params);
+
const url = new URL(link);
for (const [key, value] of params) {
url.searchParams.set(key, value);
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js
index 8db9d8e2..a84c0318 100644
--- a/web/assets/js/model/outbound.js
+++ b/web/assets/js/model/outbound.js
@@ -992,6 +992,10 @@ class Outbound extends CommonClass {
stream.kcp = new KcpStreamSettings();
stream.type = json.type;
stream.seed = json.path;
+ const mtu = Number(json.mtu);
+ if (Number.isFinite(mtu) && mtu > 0) stream.kcp.mtu = mtu;
+ const tti = Number(json.tti);
+ if (Number.isFinite(tti) && tti > 0) stream.kcp.tti = tti;
} else if (network === 'ws') {
stream.ws = new WsStreamSettings(json.path, json.host);
} else if (network === 'grpc') {
@@ -1029,6 +1033,7 @@ class Outbound extends CommonClass {
let headerType = url.searchParams.get('headerType') ?? undefined;
let host = url.searchParams.get('host') ?? undefined;
let path = url.searchParams.get('path') ?? undefined;
+ let seed = url.searchParams.get('seed') ?? path ?? undefined;
let mode = url.searchParams.get('mode') ?? undefined;
if (type === 'tcp' || type === 'none') {
@@ -1036,7 +1041,11 @@ class Outbound extends CommonClass {
} else if (type === 'kcp') {
stream.kcp = new KcpStreamSettings();
stream.kcp.type = headerType ?? 'none';
- stream.kcp.seed = path;
+ stream.kcp.seed = seed;
+ const mtu = Number(url.searchParams.get('mtu'));
+ if (Number.isFinite(mtu) && mtu > 0) stream.kcp.mtu = mtu;
+ const tti = Number(url.searchParams.get('tti'));
+ if (Number.isFinite(tti) && tti > 0) stream.kcp.tti = tti;
} else if (type === 'ws') {
stream.ws = new WsStreamSettings(path, host);
} else if (type === 'grpc') {