瀏覽代碼

outbound: finalmask

MHSanaei 1 天之前
父節點
當前提交
c59f54bb0e
共有 3 個文件被更改,包括 115 次插入39 次删除
  1. 2 2
      web/assets/js/model/inbound.js
  2. 67 26
      web/assets/js/model/outbound.js
  3. 46 11
      web/html/form/outbound.html

+ 2 - 2
web/assets/js/model/inbound.js

@@ -318,7 +318,7 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass {
 
 class KcpStreamSettings extends XrayCommonClass {
     constructor(
-        mtu = 1250,
+        mtu = 1350,
         tti = 50,
         uplinkCapacity = 5,
         downlinkCapacity = 20,
@@ -2509,7 +2509,7 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
 Inbound.WireguardSettings = class extends XrayCommonClass {
     constructor(
         protocol,
-        mtu = 1250,
+        mtu = 1420,
         secretKey = Wireguard.generateKeypair().privateKey,
         peers = [new Inbound.WireguardSettings.Peer()],
         noKernelTun = false

+ 67 - 26
web/assets/js/model/outbound.js

@@ -165,7 +165,7 @@ class TcpStreamSettings extends CommonClass {
 
 class KcpStreamSettings extends CommonClass {
     constructor(
-        mtu = 1250,
+        mtu = 1350,
         tti = 50,
         uplinkCapacity = 5,
         downlinkCapacity = 20,
@@ -558,27 +558,49 @@ class SockoptStreamSettings extends CommonClass {
     }
 }
 
-class UdpMask extends CommonClass {
-    constructor(type = 'salamander', password = '') {
+class FinalMask extends CommonClass {
+    constructor(type = 'salamander', settings = {}) {
         super();
         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 = {}) {
-        return new UdpMask(
-            json.type,
-            json.settings?.password || ''
+        return new FinalMask(
+            json.type || 'salamander',
+            json.settings || {}
         );
     }
 
     toJson() {
-        return {
-            type: this.type,
-            settings: {
-                password: this.password
-            }
+        const result = {
+            type: this.type
         };
+        // 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(),
         xhttpSettings = new xHTTPStreamSettings(),
         hysteriaSettings = new HysteriaStreamSettings(),
-        udpmasks = [],
+        finalmask = { udp: [] },
         sockopt = undefined,
     ) {
         super();
@@ -610,16 +632,21 @@ class StreamSettings extends CommonClass {
         this.httpupgrade = httpupgradeSettings;
         this.xhttp = xhttpSettings;
         this.hysteria = hysteriaSettings;
-        this.udpmasks = udpmasks;
+        this.finalmask = finalmask;
         this.sockopt = sockopt;
     }
 
-    addUdpMask() {
-        this.udpmasks.push(new UdpMask());
+    addUdpMask(type = 'salamander') {
+        if (!this.finalmask.udp) {
+            this.finalmask.udp = [];
+        }
+        this.finalmask.udp.push(new FinalMask(type));
     }
 
     delUdpMask(index) {
-        this.udpmasks.splice(index, 1);
+        if (this.finalmask.udp) {
+            this.finalmask.udp.splice(index, 1);
+        }
     }
 
     get isTls() {
@@ -639,7 +666,16 @@ class StreamSettings extends CommonClass {
     }
 
     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(
             json.network,
             json.security,
@@ -652,7 +688,7 @@ class StreamSettings extends CommonClass {
             HttpUpgradeStreamSettings.fromJson(json.httpupgradeSettings),
             xHTTPStreamSettings.fromJson(json.xhttpSettings),
             HysteriaStreamSettings.fromJson(json.hysteriaSettings),
-            udpmasks,
+            finalmask,
             SockoptStreamSettings.fromJson(json.sockopt),
         );
     }
@@ -671,7 +707,9 @@ class StreamSettings extends CommonClass {
             httpupgradeSettings: network === 'httpupgrade' ? this.httpupgrade.toJson() : undefined,
             xhttpSettings: network === 'xhttp' ? this.xhttp.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,
         };
     }
@@ -1285,11 +1323,14 @@ Outbound.VLESSSettings = class extends CommonClass {
             flow: this.flow,
             encryption: this.encryption,
         };
-        if (this.testpre > 0) {
-            result.testpre = this.testpre;
-        }
-        if (this.testseed && this.testseed.length >= 4) {
-            result.testseed = this.testseed;
+        // Only include Vision settings when flow is set
+        if (this.flow && this.flow !== '') {
+            if (this.testpre > 0) {
+                result.testpre = this.testpre;
+            }
+            if (this.testseed && this.testseed.length >= 4) {
+                result.testseed = this.testseed;
+            }
         }
         return result;
     }
@@ -1422,7 +1463,7 @@ Outbound.HttpSettings = class extends CommonClass {
 
 Outbound.WireguardSettings = class extends CommonClass {
     constructor(
-        mtu = 1250,
+        mtu = 1420,
         secretKey = '',
         address = [''],
         workers = 2,

+ 46 - 11
web/html/form/outbound.html

@@ -546,8 +546,9 @@
             <a-input v-model.trim="outbound.stream.hysteria.auth"></a-input>
           </a-form-item>
           <a-form-item label='Congestion'>
-            <a-select v-model="outbound.stream.hysteria.congestion" :dropdown-class-name="themeSwitcher.currentTheme">
-              <a-select-option value="">BBR (Auto)</a-select-option>
+            <a-select v-model="outbound.stream.hysteria.congestion"
+              :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>
           </a-form-item>
@@ -602,25 +603,59 @@
         </template>
       </template>
 
-      <!-- udpmasks settings -->
+      <!-- finalmask settings -->
       <template v-if="outbound.canEnableStream()">
         <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>
-        <template v-if="outbound.stream.udpmasks.length > 0">
-          <a-form v-for="(mask, index) in outbound.stream.udpmasks" :key="index" :colon="false"
+        <template
+          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} }">
             <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>
             </a-divider>
             <a-form-item label='Type'>
-              <a-select v-model="mask.type" :dropdown-class-name="themeSwitcher.currentTheme">
-                <a-select-option value="salamander">Salamander</a-select-option>
+              <a-select v-model="mask.type"
+                @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-form-item>
-            <a-form-item label='Password'>
-              <a-input v-model.trim="mask.password" placeholder="Obfuscation password"></a-input>
+            <!-- Settings for password-based masks -->
+            <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>
         </template>