diff options
| author | MHSanaei <ho3ein.sanaei@gmail.com> | 2026-04-27 00:04:47 +0300 |
|---|---|---|
| committer | MHSanaei <ho3ein.sanaei@gmail.com> | 2026-04-27 00:04:47 +0300 |
| commit | 0aca2d3b3d7e54f0408670db5f7075eaee190066 (patch) | |
| tree | 7628c810f9b6105748b032500e83ca196f81e740 /web | |
| parent | 8529f4f0cfbe40e211959571807af7128408b960 (diff) | |
sub: kcp finalmask
Diffstat (limited to 'web')
| -rw-r--r-- | web/assets/js/model/inbound.js | 54 | ||||
| -rw-r--r-- | web/assets/js/model/outbound.js | 11 | ||||
| -rw-r--r-- | web/html/settings/panel/subscription/subpage.html | 19 |
3 files changed, 81 insertions, 3 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') { diff --git a/web/html/settings/panel/subscription/subpage.html b/web/html/settings/panel/subscription/subpage.html index 64c1224d..2b3c7939 100644 --- a/web/html/settings/panel/subscription/subpage.html +++ b/web/html/settings/panel/subscription/subpage.html @@ -6,6 +6,23 @@ <script src="{{ .base_path }}assets/js/util/index.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script> <style> + .subscription-page tr-qr-box.qr-box { + display: inline-flex; + flex-direction: column; + align-items: center; + width: 220px; + } + + .subscription-page tr-qr-box.qr-box .qr-tag { + width: 100%; + justify-content: center; + } + + .subscription-page tr-qr-box.qr-box .qr-bg, + .subscription-page tr-qr-box.qr-box .qr-bg-sub { + margin-inline: auto; + } + .subscription-page .subscription-link-box { cursor: pointer; border-radius: 12px; @@ -193,8 +210,6 @@ </div> </div> </div> - - </div> <br /> <a-form layout="vertical"> |
