mhsanaei 4 days ago
parent
commit
a15c8f8bfe

+ 32 - 9
web/assets/js/model/inbound.js

@@ -1301,6 +1301,7 @@ class Inbound extends XrayCommonClass {
         const security = forceTls == 'same' ? this.stream.security : forceTls;
         const params = new Map();
         params.set("type", this.stream.network);
+        params.set("encryption", this.settings.encryption);
         switch (type) {
             case "tcp":
                 const tcp = this.stream.tcp;
@@ -1859,13 +1860,15 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
     constructor(
         protocol,
         vlesses = [new Inbound.VLESSSettings.VLESS()],
-        decryption = 'none',
+        decryption = "none",
         fallbacks = []
     ) {
         super(protocol);
         this.vlesses = vlesses;
         this.decryption = decryption;
         this.fallbacks = fallbacks;
+        this.selectedAuth = "X25519, not Post-Quantum";
+        this.encryption = "";
     }
 
     addFallback() {
@@ -1876,22 +1879,42 @@ Inbound.VLESSSettings = class extends Inbound.Settings {
         this.fallbacks.splice(index, 1);
     }
 
-    // decryption should be set to static value
     static fromJson(json = {}) {
-        return new Inbound.VLESSSettings(
+        const obj = new Inbound.VLESSSettings(
             Protocols.VLESS,
-            json.clients.map(client => Inbound.VLESSSettings.VLESS.fromJson(client)),
-            json.decryption || 'none',
-            Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks),);
+            (json.clients || []).map(client => Inbound.VLESSSettings.VLESS.fromJson(client)),
+            json.decryption || "none",
+            Inbound.VLESSSettings.Fallback.fromJson(json.fallbacks || [])
+        );
+
+        obj.encryption = json.encryption || "";
+        obj.selectedAuth = json.selectedAuth || "X25519, not Post-Quantum";
+
+        return obj;
     }
 
+
     toJson() {
-        return {
+        const json = {
             clients: Inbound.VLESSSettings.toJsonArray(this.vlesses),
-            decryption: this.decryption,
-            fallbacks: Inbound.VLESSSettings.toJsonArray(this.fallbacks),
         };
+
+        if (this.decryption) {
+            json.decryption = this.decryption;
+        }
+
+        if (this.encryption) {
+            json.encryption = this.encryption;
+        }
+
+        if (this.fallbacks && this.fallbacks.length > 0) {
+            json.fallbacks = Inbound.VLESSSettings.toJsonArray(this.fallbacks);
+        }
+
+        return json;
     }
+
+
 };
 
 Inbound.VLESSSettings.VLESS = class extends XrayCommonClass {

+ 3 - 3
web/controller/server.go

@@ -55,7 +55,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
 	g.POST("/getNewX25519Cert", a.getNewX25519Cert)
 	g.POST("/getNewmldsa65", a.getNewmldsa65)
 	g.POST("/getNewEchCert", a.getNewEchCert)
-	g.POST("/getNewmlkem768", a.getNewmlkem768)
+	g.POST("/getNewVlessEnc", a.getNewVlessEnc)
 }
 
 func (a *ServerController) refreshStatus() {
@@ -268,8 +268,8 @@ func (a *ServerController) getNewEchCert(c *gin.Context) {
 	jsonObj(c, cert, nil)
 }
 
-func (a *ServerController) getNewmlkem768(c *gin.Context) {
-	out, err := a.serverService.GetNewmlkem768()
+func (a *ServerController) getNewVlessEnc(c *gin.Context) {
+	out, err := a.serverService.GetNewVlessEnc()
 	if err != nil {
 		jsonMsg(c, I18nWeb(c, "pages.inbounds.toasts.getNewmlkem768Error"), err)
 		return

+ 20 - 0
web/html/form/protocol/vless.html

@@ -18,6 +18,26 @@
     </table>
   </a-collapse-panel>
 </a-collapse>
+<template>
+  <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
+    <a-form-item label="Authentication">
+      <a-select v-model="inbound.settings.selectedAuth" @change="getNewVlessEnc"
+        :dropdown-class-name="themeSwitcher.currentTheme">
+        <a-select-option value="X25519, not Post-Quantum">X25519 (not Post-Quantum)</a-select-option>
+        <a-select-option value="ML-KEM-768, Post-Quantum">ML-KEM-768 (Post-Quantum)</a-select-option>
+      </a-select>
+    </a-form-item>
+    <a-form-item label="decryption">
+       <a-input v-model.trim="inbound.settings.decryption"></a-input>
+    </a-form-item>
+    <a-form-item label="encryption">
+      <a-input v-model="inbound.settings.encryption" disabled></a-input>
+    </a-form-item>
+    <a-form-item label=" ">
+        <a-button type="primary" icon="import" @click="getNewVlessEnc">Get New keys</a-button>
+    </a-form-item>
+  </a-form>
+</template>
 <template v-if="inbound.isTcp">
   <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
     <a-form-item label="Fallbacks">

+ 31 - 17
web/html/modals/inbound_modal.html

@@ -1,9 +1,7 @@
 {{define "modals/inboundModal"}}
-<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title"
-        :dialog-style="{ top: '20px' }" @ok="inModal.ok"
-        :confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
-        :class="themeSwitcher.currentTheme"
-        :ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>
+<a-modal id="inbound-modal" v-model="inModal.visible" :title="inModal.title" :dialog-style="{ top: '20px' }"
+    @ok="inModal.ok" :confirm-loading="inModal.confirmLoading" :closable="true" :mask-closable="false"
+    :class="themeSwitcher.currentTheme" :ok-text="inModal.okText" cancel-text='{{ i18n "close" }}'>
     {{template "form/inbound"}}
 </a-modal>
 <script>
@@ -20,7 +18,7 @@
         ok() {
             ObjectUtil.execute(inModal.confirm, inModal.inbound, inModal.dbInbound);
         },
-        show({ title = '', okText = '{{ i18n "sure" }}', inbound = null, dbInbound = null, confirm = (inbound, dbInbound) => {}, isEdit = false }) {
+        show({ title = '', okText = '{{ i18n "sure" }}', inbound = null, dbInbound = null, confirm = (inbound, dbInbound) => { }, isEdit = false }) {
             this.title = title;
             this.okText = okText;
             if (inbound) {
@@ -41,7 +39,7 @@
             inModal.visible = false;
             inModal.loading(false);
         },
-        loading(loading=true) {
+        loading(loading = true) {
             inModal.confirmLoading = loading;
         },
     };
@@ -105,9 +103,9 @@
             },
             SSMethodChange() {
                 this.inModal.inbound.settings.password = RandomUtil.randomShadowsocksPassword(this.inModal.inbound.settings.method)
-                
+
                 if (this.inModal.inbound.isSSMultiUser) {
-                    if (this.inModal.inbound.settings.shadowsockses.length ==0){
+                    if (this.inModal.inbound.settings.shadowsockses.length == 0) {
                         this.inModal.inbound.settings.shadowsockses = [new Inbound.ShadowsocksSettings.Shadowsocks()];
                     }
                     if (!this.inModal.inbound.isSS2022) {
@@ -123,7 +121,7 @@
                         client.password = RandomUtil.randomShadowsocksPassword(this.inModal.inbound.settings.method)
                     })
                 } else {
-                    if (this.inModal.inbound.settings.shadowsockses.length > 0){
+                    if (this.inModal.inbound.settings.shadowsockses.length > 0) {
                         this.inModal.inbound.settings.shadowsockses = [];
                     }
                 }
@@ -154,7 +152,7 @@
             },
             async getNewEchCert() {
                 inModal.loading(true);
-                const msg = await HttpUtil.post('/server/getNewEchCert', {sni: inModal.inbound.stream.tls.sni});
+                const msg = await HttpUtil.post('/server/getNewEchCert', { sni: inModal.inbound.stream.tls.sni });
                 inModal.loading(false);
                 if (!msg.success) {
                     return;
@@ -162,18 +160,34 @@
                 inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys;
                 inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList;
             },
-            async getNewmlkem768() {
+            async getNewVlessEnc() {
                 inModal.loading(true);
-                const msg = await HttpUtil.post('/server/getNewmlkem768');
+                const msg = await HttpUtil.post('/server/getNewVlessEnc');
                 inModal.loading(false);
+
                 if (!msg.success) {
                     return;
                 }
-                inModal.inbound.stream.reality.mlkem768Seed = msg.obj.seed;
-                inModal.inbound.stream.reality.settings.mlkem768Client = msg.obj.Client;
-            },
+
+                const auths = msg.obj.auths || [];
+                const selected = inModal.inbound.settings.selectedAuth;
+
+                const block = auths.find(a => a.label === selected);
+
+                if (!block) {
+                    console.error("No auth block for", selected);
+                    return;
+                }
+
+                // ✅ server config field
+                inModal.inbound.settings.decryption = block.decryption;
+
+                // ✅ UI-only field (not saved in config)
+                inModal.inbound.settings.encryption = block.encryption;
+            }
+
         },
     });
 
 </script>
-{{end}}
+{{end}}

+ 36 - 13
web/service/server.go

@@ -872,28 +872,51 @@ func (s *ServerService) GetNewEchCert(sni string) (interface{}, error) {
 	}, nil
 }
 
-func (s *ServerService) GetNewmlkem768() (any, error) {
-	// Run the command
-	cmd := exec.Command(xray.GetBinaryPath(), "mlkem768")
+type AuthBlock struct {
+	Label      string `json:"label"`
+	Decryption string `json:"decryption"`
+	Encryption string `json:"encryption"`
+}
+
+func (s *ServerService) GetNewVlessEnc() (any, error) {
+	cmd := exec.Command(xray.GetBinaryPath(), "vlessenc")
 	var out bytes.Buffer
 	cmd.Stdout = &out
-	err := cmd.Run()
-	if err != nil {
+	if err := cmd.Run(); err != nil {
 		return nil, err
 	}
 
 	lines := strings.Split(out.String(), "\n")
 
-	SeedLine := strings.Split(lines[0], ":")
-	ClientLine := strings.Split(lines[1], ":")
+	var blocks []AuthBlock
+	var current *AuthBlock
 
-	seed := strings.TrimSpace(SeedLine[1])
-	client := strings.TrimSpace(ClientLine[1])
+	for _, line := range lines {
+		line = strings.TrimSpace(line)
+		if strings.HasPrefix(line, "Authentication:") {
+			if current != nil {
+				blocks = append(blocks, *current)
+			}
+			current = &AuthBlock{Label: strings.TrimSpace(strings.TrimPrefix(line, "Authentication:"))}
+		} else if strings.HasPrefix(line, `"decryption"`) || strings.HasPrefix(line, `"encryption"`) {
+			parts := strings.SplitN(line, ":", 2)
+			if len(parts) == 2 && current != nil {
+				key := strings.Trim(parts[0], `" `)
+				val := strings.Trim(parts[1], `" `)
+				if key == "decryption" {
+					current.Decryption = val
+				} else if key == "encryption" {
+					current.Encryption = val
+				}
+			}
+		}
+	}
 
-	keyPair := map[string]any{
-		"seed":   seed,
-		"client": client,
+	if current != nil {
+		blocks = append(blocks, *current)
 	}
 
-	return keyPair, nil
+	return map[string]any{
+		"auths": blocks,
+	}, nil
 }