mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-02-13 13:57:59 +00:00
outbound: finalmask
This commit is contained in:
parent
6b3da4fe5e
commit
c59f54bb0e
3 changed files with 115 additions and 39 deletions
|
|
@ -318,7 +318,7 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
|
||||||
|
|
||||||
class KcpStreamSettings extends XrayCommonClass {
|
class KcpStreamSettings extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
mtu = 1250,
|
mtu = 1350,
|
||||||
tti = 50,
|
tti = 50,
|
||||||
uplinkCapacity = 5,
|
uplinkCapacity = 5,
|
||||||
downlinkCapacity = 20,
|
downlinkCapacity = 20,
|
||||||
|
|
@ -2509,7 +2509,7 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
|
||||||
Inbound.WireguardSettings = class extends XrayCommonClass {
|
Inbound.WireguardSettings = class extends XrayCommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
protocol,
|
protocol,
|
||||||
mtu = 1250,
|
mtu = 1420,
|
||||||
secretKey = Wireguard.generateKeypair().privateKey,
|
secretKey = Wireguard.generateKeypair().privateKey,
|
||||||
peers = [new Inbound.WireguardSettings.Peer()],
|
peers = [new Inbound.WireguardSettings.Peer()],
|
||||||
noKernelTun = false
|
noKernelTun = false
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ class TcpStreamSettings extends CommonClass {
|
||||||
|
|
||||||
class KcpStreamSettings extends CommonClass {
|
class KcpStreamSettings extends CommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
mtu = 1250,
|
mtu = 1350,
|
||||||
tti = 50,
|
tti = 50,
|
||||||
uplinkCapacity = 5,
|
uplinkCapacity = 5,
|
||||||
downlinkCapacity = 20,
|
downlinkCapacity = 20,
|
||||||
|
|
@ -558,27 +558,49 @@ class SockoptStreamSettings extends CommonClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UdpMask extends CommonClass {
|
class FinalMask extends CommonClass {
|
||||||
constructor(type = 'salamander', password = '') {
|
constructor(type = 'salamander', settings = {}) {
|
||||||
super();
|
super();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.password = password;
|
this.settings = this._getDefaultSettings(type, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getDefaultSettings(type, settings = {}) {
|
||||||
|
switch (type) {
|
||||||
|
case 'salamander':
|
||||||
|
case 'mkcp-aes128gcm':
|
||||||
|
return { password: settings.password || '' };
|
||||||
|
case 'header-dns':
|
||||||
|
case 'xdns':
|
||||||
|
return { domain: settings.domain || '' };
|
||||||
|
case 'mkcp-original':
|
||||||
|
case 'header-dtls':
|
||||||
|
case 'header-srtp':
|
||||||
|
case 'header-utp':
|
||||||
|
case 'header-wechat':
|
||||||
|
case 'header-wireguard':
|
||||||
|
return {}; // No settings needed
|
||||||
|
default:
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
return new UdpMask(
|
return new FinalMask(
|
||||||
json.type,
|
json.type || 'salamander',
|
||||||
json.settings?.password || ''
|
json.settings || {}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
const result = {
|
||||||
type: this.type,
|
type: this.type
|
||||||
settings: {
|
|
||||||
password: this.password
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
// Only include settings if they exist and are not empty
|
||||||
|
if (this.settings && Object.keys(this.settings).length > 0) {
|
||||||
|
result.settings = this.settings;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -595,7 +617,7 @@ class StreamSettings extends CommonClass {
|
||||||
httpupgradeSettings = new HttpUpgradeStreamSettings(),
|
httpupgradeSettings = new HttpUpgradeStreamSettings(),
|
||||||
xhttpSettings = new xHTTPStreamSettings(),
|
xhttpSettings = new xHTTPStreamSettings(),
|
||||||
hysteriaSettings = new HysteriaStreamSettings(),
|
hysteriaSettings = new HysteriaStreamSettings(),
|
||||||
udpmasks = [],
|
finalmask = { udp: [] },
|
||||||
sockopt = undefined,
|
sockopt = undefined,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -610,16 +632,21 @@ class StreamSettings extends CommonClass {
|
||||||
this.httpupgrade = httpupgradeSettings;
|
this.httpupgrade = httpupgradeSettings;
|
||||||
this.xhttp = xhttpSettings;
|
this.xhttp = xhttpSettings;
|
||||||
this.hysteria = hysteriaSettings;
|
this.hysteria = hysteriaSettings;
|
||||||
this.udpmasks = udpmasks;
|
this.finalmask = finalmask;
|
||||||
this.sockopt = sockopt;
|
this.sockopt = sockopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
addUdpMask() {
|
addUdpMask(type = 'salamander') {
|
||||||
this.udpmasks.push(new UdpMask());
|
if (!this.finalmask.udp) {
|
||||||
|
this.finalmask.udp = [];
|
||||||
|
}
|
||||||
|
this.finalmask.udp.push(new FinalMask(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
delUdpMask(index) {
|
delUdpMask(index) {
|
||||||
this.udpmasks.splice(index, 1);
|
if (this.finalmask.udp) {
|
||||||
|
this.finalmask.udp.splice(index, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get isTls() {
|
get isTls() {
|
||||||
|
|
@ -639,7 +666,16 @@ class StreamSettings extends CommonClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromJson(json = {}) {
|
static fromJson(json = {}) {
|
||||||
const udpmasks = json.udpmasks ? json.udpmasks.map(mask => UdpMask.fromJson(mask)) : [];
|
let finalmask = { udp: [] };
|
||||||
|
if (json.finalmask) {
|
||||||
|
if (Array.isArray(json.finalmask)) {
|
||||||
|
// Legacy format: direct array (backward compatibility)
|
||||||
|
finalmask.udp = json.finalmask.map(mask => FinalMask.fromJson(mask));
|
||||||
|
} else if (json.finalmask.udp) {
|
||||||
|
// New format: object with udp array
|
||||||
|
finalmask.udp = json.finalmask.udp.map(mask => FinalMask.fromJson(mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
return new StreamSettings(
|
return new StreamSettings(
|
||||||
json.network,
|
json.network,
|
||||||
json.security,
|
json.security,
|
||||||
|
|
@ -652,7 +688,7 @@ class StreamSettings extends CommonClass {
|
||||||
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
|
||||||
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
xHTTPStreamSettings.fromJson(json.xhttpSettings),
|
||||||
HysteriaStreamSettings.fromJson(json.hysteriaSettings),
|
HysteriaStreamSettings.fromJson(json.hysteriaSettings),
|
||||||
udpmasks,
|
finalmask,
|
||||||
SockoptStreamSettings.fromJson(json.sockopt),
|
SockoptStreamSettings.fromJson(json.sockopt),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -671,7 +707,9 @@ class StreamSettings extends CommonClass {
|
||||||
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
|
||||||
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
xhttpSettings: network === 'xhttp' ? this.xhttp.toJson() : undefined,
|
||||||
hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
|
hysteriaSettings: network === 'hysteria' ? this.hysteria.toJson() : undefined,
|
||||||
udpmasks: this.udpmasks.length > 0 ? this.udpmasks.map(mask => mask.toJson()) : undefined,
|
finalmask: (this.finalmask.udp && this.finalmask.udp.length > 0) ? {
|
||||||
|
udp: this.finalmask.udp.map(mask => mask.toJson())
|
||||||
|
} : undefined,
|
||||||
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
sockopt: this.sockopt != undefined ? this.sockopt.toJson() : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1285,11 +1323,14 @@ Outbound.VLESSSettings = class extends CommonClass {
|
||||||
flow: this.flow,
|
flow: this.flow,
|
||||||
encryption: this.encryption,
|
encryption: this.encryption,
|
||||||
};
|
};
|
||||||
if (this.testpre > 0) {
|
// Only include Vision settings when flow is set
|
||||||
result.testpre = this.testpre;
|
if (this.flow && this.flow !== '') {
|
||||||
}
|
if (this.testpre > 0) {
|
||||||
if (this.testseed && this.testseed.length >= 4) {
|
result.testpre = this.testpre;
|
||||||
result.testseed = this.testseed;
|
}
|
||||||
|
if (this.testseed && this.testseed.length >= 4) {
|
||||||
|
result.testseed = this.testseed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -1422,7 +1463,7 @@ Outbound.HttpSettings = class extends CommonClass {
|
||||||
|
|
||||||
Outbound.WireguardSettings = class extends CommonClass {
|
Outbound.WireguardSettings = class extends CommonClass {
|
||||||
constructor(
|
constructor(
|
||||||
mtu = 1250,
|
mtu = 1420,
|
||||||
secretKey = '',
|
secretKey = '',
|
||||||
address = [''],
|
address = [''],
|
||||||
workers = 2,
|
workers = 2,
|
||||||
|
|
|
||||||
|
|
@ -546,8 +546,9 @@
|
||||||
<a-input v-model.trim="outbound.stream.hysteria.auth"></a-input>
|
<a-input v-model.trim="outbound.stream.hysteria.auth"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Congestion'>
|
<a-form-item label='Congestion'>
|
||||||
<a-select v-model="outbound.stream.hysteria.congestion" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="outbound.stream.hysteria.congestion"
|
||||||
<a-select-option value="">BBR (Auto)</a-select-option>
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option value>BBR (Auto)</a-select-option>
|
||||||
<a-select-option value="brutal">Brutal</a-select-option>
|
<a-select-option value="brutal">Brutal</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
@ -602,25 +603,59 @@
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- udpmasks settings -->
|
<!-- finalmask settings -->
|
||||||
<template v-if="outbound.canEnableStream()">
|
<template v-if="outbound.canEnableStream()">
|
||||||
<a-form-item label="UDP Masks">
|
<a-form-item label="UDP Masks">
|
||||||
<a-button icon="plus" type="primary" size="small" @click="outbound.stream.addUdpMask()"></a-button>
|
<a-button icon="plus" type="primary" size="small"
|
||||||
|
@click="outbound.stream.addUdpMask(outbound.protocol === Protocols.Hysteria ? 'salamander' : 'mkcp-aes128gcm')"></a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<template v-if="outbound.stream.udpmasks.length > 0">
|
<template
|
||||||
<a-form v-for="(mask, index) in outbound.stream.udpmasks" :key="index" :colon="false"
|
v-if="outbound.stream.finalmask.udp && outbound.stream.finalmask.udp.length > 0">
|
||||||
|
<a-form v-for="(mask, index) in outbound.stream.finalmask.udp"
|
||||||
|
:key="index" :colon="false"
|
||||||
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||||
<a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]]
|
<a-divider :style="{ margin: '0' }"> UDP Mask [[ index + 1 ]]
|
||||||
<a-icon type="delete" @click="() => outbound.stream.delUdpMask(index)"
|
<a-icon type="delete"
|
||||||
|
@click="() => outbound.stream.delUdpMask(index)"
|
||||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<a-form-item label='Type'>
|
<a-form-item label='Type'>
|
||||||
<a-select v-model="mask.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
<a-select v-model="mask.type"
|
||||||
<a-select-option value="salamander">Salamander</a-select-option>
|
@change="(type) => mask.settings = mask._getDefaultSettings(type, {})"
|
||||||
|
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||||
|
<a-select-option v-if="outbound.protocol === Protocols.Hysteria" value="salamander">
|
||||||
|
Salamander (Hysteria2)</a-select-option>
|
||||||
|
<a-select-option value="mkcp-aes128gcm">
|
||||||
|
mKCP AES-128-GCM</a-select-option>
|
||||||
|
<a-select-option value="header-dns">
|
||||||
|
Header DNS</a-select-option>
|
||||||
|
<a-select-option value="header-dtls">
|
||||||
|
Header DTLS 1.2</a-select-option>
|
||||||
|
<a-select-option value="header-srtp">
|
||||||
|
Header SRTP</a-select-option>
|
||||||
|
<a-select-option value="header-utp">
|
||||||
|
Header uTP</a-select-option>
|
||||||
|
<a-select-option value="header-wechat">
|
||||||
|
Header WeChat Video</a-select-option>
|
||||||
|
<a-select-option value="header-wireguard">
|
||||||
|
Header WireGuard</a-select-option>
|
||||||
|
<a-select-option value="mkcp-original">
|
||||||
|
mKCP Original</a-select-option>
|
||||||
|
<a-select-option value="xdns">
|
||||||
|
xDNS (Experimental)</a-select-option>
|
||||||
</a-select>
|
</a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label='Password'>
|
<!-- Settings for password-based masks -->
|
||||||
<a-input v-model.trim="mask.password" placeholder="Obfuscation password"></a-input>
|
<a-form-item label='Password'
|
||||||
|
v-if="['salamander', 'mkcp-aes128gcm'].includes(mask.type)">
|
||||||
|
<a-input v-model.trim="mask.settings.password"
|
||||||
|
placeholder="Obfuscation password"></a-input>
|
||||||
|
</a-form-item>
|
||||||
|
<!-- Settings for domain-based masks -->
|
||||||
|
<a-form-item label='Domain'
|
||||||
|
v-if="['header-dns', 'xdns'].includes(mask.type)">
|
||||||
|
<a-input v-model.trim="mask.settings.domain"
|
||||||
|
placeholder="e.g., www.example.com"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue