3 Commits 3edf79e589 ... fe9f0d1d0e

Author SHA1 Message Date
  Sanaei fe9f0d1d0e api (#3434) 1 day ago
  mhsanaei 18d74d54ca outbound: ECH Config List 1 day ago
  mhsanaei c7ba6ae909 add clear button 1 day ago

+ 16 - 36
web/controller/api.go

@@ -9,6 +9,7 @@ import (
 type APIController struct {
 	BaseController
 	inboundController *InboundController
+	serverController  *ServerController
 	Tgbot             service.Tgbot
 }
 
@@ -19,43 +20,22 @@ func NewAPIController(g *gin.RouterGroup) *APIController {
 }
 
 func (a *APIController) initRouter(g *gin.RouterGroup) {
-	g = g.Group("/panel/api/inbounds")
-	g.Use(a.checkLogin)
-
-	a.inboundController = NewInboundController(g)
-
-	inboundRoutes := []struct {
-		Method  string
-		Path    string
-		Handler gin.HandlerFunc
-	}{
-		{"GET", "/createbackup", a.createBackup},
-		{"GET", "/list", a.inboundController.getInbounds},
-		{"GET", "/get/:id", a.inboundController.getInbound},
-		{"GET", "/getClientTraffics/:email", a.inboundController.getClientTraffics},
-		{"GET", "/getClientTrafficsById/:id", a.inboundController.getClientTrafficsById},
-		{"POST", "/add", a.inboundController.addInbound},
-		{"POST", "/del/:id", a.inboundController.delInbound},
-		{"POST", "/update/:id", a.inboundController.updateInbound},
-		{"POST", "/clientIps/:email", a.inboundController.getClientIps},
-		{"POST", "/clearClientIps/:email", a.inboundController.clearClientIps},
-		{"POST", "/addClient", a.inboundController.addInboundClient},
-		{"POST", "/:id/delClient/:clientId", a.inboundController.delInboundClient},
-		{"POST", "/updateClient/:clientId", a.inboundController.updateInboundClient},
-		{"POST", "/:id/resetClientTraffic/:email", a.inboundController.resetClientTraffic},
-		{"POST", "/resetAllTraffics", a.inboundController.resetAllTraffics},
-		{"POST", "/resetAllClientTraffics/:id", a.inboundController.resetAllClientTraffics},
-		{"POST", "/delDepletedClients/:id", a.inboundController.delDepletedClients},
-		{"POST", "/onlines", a.inboundController.onlines},
-		{"POST", "/lastOnline", a.inboundController.lastOnline},
-		{"POST", "/updateClientTraffic/:email", a.inboundController.updateClientTraffic},
-	}
-
-	for _, route := range inboundRoutes {
-		g.Handle(route.Method, route.Path, route.Handler)
-	}
+	// Main API group
+	api := g.Group("/panel/api")
+	api.Use(a.checkLogin)
+
+	// Inbounds API
+	inbounds := api.Group("/inbounds")
+	a.inboundController = NewInboundController(inbounds)
+
+	// Server API
+	server := api.Group("/server")
+	a.serverController = NewServerController(server)
+
+	// Extra routes
+	api.GET("/backuptotgbot", a.BackuptoTgbot)
 }
 
-func (a *APIController) createBackup(c *gin.Context) {
+func (a *APIController) BackuptoTgbot(c *gin.Context) {
 	a.Tgbot.SendBackupToAdmins()
 }

+ 7 - 2
web/controller/inbound.go

@@ -24,9 +24,12 @@ func NewInboundController(g *gin.RouterGroup) *InboundController {
 }
 
 func (a *InboundController) initRouter(g *gin.RouterGroup) {
-	g = g.Group("/inbound")
 
-	g.POST("/list", a.getInbounds)
+	g.GET("/list", a.getInbounds)
+	g.GET("/get/:id", a.getInbound)
+	g.GET("/getClientTraffics/:email", a.getClientTraffics)
+	g.GET("/getClientTrafficsById/:id", a.getClientTrafficsById)
+
 	g.POST("/add", a.addInbound)
 	g.POST("/del/:id", a.delInbound)
 	g.POST("/update/:id", a.updateInbound)
@@ -41,6 +44,8 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) {
 	g.POST("/delDepletedClients/:id", a.delDepletedClients)
 	g.POST("/import", a.importInbound)
 	g.POST("/onlines", a.onlines)
+	g.POST("/lastOnline", a.lastOnline)
+	g.POST("/updateClientTraffic/:email", a.updateClientTraffic)
 }
 
 func (a *InboundController) getInbounds(c *gin.Context) {

+ 29 - 9
web/controller/server.go

@@ -37,11 +37,17 @@ func NewServerController(g *gin.RouterGroup) *ServerController {
 }
 
 func (a *ServerController) initRouter(g *gin.RouterGroup) {
-	g = g.Group("/server")
 
-	g.Use(a.checkLogin)
-	g.POST("/status", a.status)
-	g.POST("/getXrayVersion", a.getXrayVersion)
+	g.GET("/status", a.status)
+	g.GET("/getXrayVersion", a.getXrayVersion)
+	g.GET("/getConfigJson", a.getConfigJson)
+	g.GET("/getDb", a.getDb)
+	g.GET("/getNewUUID", a.getNewUUID)
+	g.GET("/getNewX25519Cert", a.getNewX25519Cert)
+	g.GET("/getNewmldsa65", a.getNewmldsa65)
+	g.GET("/getNewmlkem768", a.getNewmlkem768)
+	g.GET("/getNewVlessEnc", a.getNewVlessEnc)
+
 	g.POST("/stopXrayService", a.stopXrayService)
 	g.POST("/restartXrayService", a.restartXrayService)
 	g.POST("/installXray/:version", a.installXray)
@@ -49,13 +55,8 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
 	g.POST("/updateGeofile/:fileName", a.updateGeofile)
 	g.POST("/logs/:count", a.getLogs)
 	g.POST("/xraylogs/:count", a.getXrayLogs)
-	g.POST("/getConfigJson", a.getConfigJson)
-	g.GET("/getDb", a.getDb)
 	g.POST("/importDB", a.importDB)
-	g.POST("/getNewX25519Cert", a.getNewX25519Cert)
-	g.POST("/getNewmldsa65", a.getNewmldsa65)
 	g.POST("/getNewEchCert", a.getNewEchCert)
-	g.POST("/getNewVlessEnc", a.getNewVlessEnc)
 }
 
 func (a *ServerController) refreshStatus() {
@@ -276,3 +277,22 @@ func (a *ServerController) getNewVlessEnc(c *gin.Context) {
 	}
 	jsonObj(c, out, nil)
 }
+
+func (a *ServerController) getNewUUID(c *gin.Context) {
+	uuidResp, err := a.serverService.GetNewUUID()
+	if err != nil {
+		jsonMsg(c, "Failed to generate UUID", err)
+		return
+	}
+
+	jsonObj(c, uuidResp, nil)
+}
+
+func (a *ServerController) getNewmlkem768(c *gin.Context) {
+	out, err := a.serverService.GetNewmlkem768()
+	if err != nil {
+		jsonMsg(c, "Failed to generate mlkem768 keys", err)
+		return
+	}
+	jsonObj(c, out, nil)
+}

+ 2 - 0
web/controller/xui.go

@@ -8,6 +8,7 @@ type XUIController struct {
 	BaseController
 
 	inboundController     *InboundController
+	serverController      *ServerController
 	settingController     *SettingController
 	xraySettingController *XraySettingController
 }
@@ -28,6 +29,7 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) {
 	g.GET("/xray", a.xraySettings)
 
 	a.inboundController = NewInboundController(g)
+	a.serverController = NewServerController(g)
 	a.settingController = NewSettingController(g)
 	a.xraySettingController = NewXraySettingController(g)
 }

+ 3 - 0
web/html/form/outbound.html

@@ -441,6 +441,9 @@
               <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
             </a-select>
           </a-form-item>
+          <a-form-item label="ECH Config List">
+            <a-input v-model.trim="outbound.stream.tls.echConfigList"></a-input>
+          </a-form-item>
           <a-form-item label="Allow Insecure">
             <a-switch v-model="outbound.stream.tls.allowInsecure"></a-switch>
           </a-form-item>

+ 3 - 4
web/html/form/protocol/vless.html

@@ -22,7 +22,6 @@
   <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="undefined">None</a-select-option>
         <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>
@@ -31,17 +30,17 @@
       <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-input v-model="inbound.settings.encryption"></a-input>
     </a-form-item>
     <a-form-item label=" ">
       <a-space>
         <a-button type="primary" icon="import" @click="getNewVlessEnc">Get New keys</a-button>
-        <a-button danger @click="clearKeys">Clear</a-button>
+        <a-button danger @click="clearVlessEnc">Clear</a-button>
       </a-space>
     </a-form-item>
   </a-form>
 </template>
-<template v-if="inbound.isTcp && !inbound.settings.encryption">
+<template v-if="inbound.isTcp && !inbound.settings.selectedAuth">
   <a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
     <a-form-item label="Fallbacks">
       <a-button icon="plus" type="primary" size="small" @click="inbound.settings.addFallback()"></a-button>

+ 8 - 2
web/html/form/reality_settings.html

@@ -48,7 +48,10 @@
         <a-textarea v-model="inbound.stream.reality.privateKey"></a-textarea>
     </a-form-item>
     <a-form-item label=" ">
-        <a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
+        <a-space>
+            <a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Cert</a-button>
+            <a-button danger @click="clearX25519Cert">Clear</a-button>
+        </a-space>
     </a-form-item>
     <a-form-item label="mldsa65 Seed">
         <a-textarea v-model="inbound.stream.reality.mldsa65Seed"></a-textarea>
@@ -57,7 +60,10 @@
         <a-textarea v-model="inbound.stream.reality.settings.mldsa65Verify"></a-textarea>
     </a-form-item>
     <a-form-item label=" ">
-        <a-button type="primary" icon="import" @click="getNewmldsa65">Get New Seed</a-button>
+        <a-space>
+            <a-button type="primary" icon="import" @click="getNewmldsa65">Get New Seed</a-button>
+            <a-button danger @click="clearMldsa65">Clear</a-button>
+        </a-space>
     </a-form-item>
 </template>
 {{end}}

+ 7 - 4
web/html/form/tls_settings.html

@@ -5,13 +5,13 @@
   <a-form-item label='{{ i18n "security" }}'>
     <a-radio-group v-model="inbound.stream.security" button-style="solid">
       <a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
-      <a-radio-button v-if="inbound.canEnableReality() && !inbound.settings.encryption" value="reality">Reality</a-radio-button>
-      <a-radio-button v-if="!inbound.settings.encryption" value="tls">TLS</a-radio-button>
+      <a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
+      <a-radio-button value="tls">TLS</a-radio-button>
     </a-radio-group>
   </a-form-item>
 
   <!-- tls settings -->
-  <template v-if="inbound.stream.isTls && !inbound.settings.encryption">
+  <template v-if="inbound.stream.isTls">
     <a-form-item label="SNI" placeholder="Server Name Indication">
       <a-input v-model.trim="inbound.stream.tls.sni"></a-input>
     </a-form-item>
@@ -116,12 +116,15 @@
         </a-select>
     </a-form-item>
     <a-form-item label=" ">
+      <a-space>
         <a-button type="primary" icon="import" @click="getNewEchCert">Get New ECH Cert</a-button>
+        <a-button danger @click="clearEchCert">Clear</a-button>
+      </a-space>
     </a-form-item>
   </template>
 
   <!-- reality settings -->
-  <template v-if="inbound.stream.isReality && !inbound.settings.encryption">
+  <template v-if="inbound.stream.isReality">
     {{template "form/realitySettings"}}
   </template>
 </a-form>

+ 17 - 17
web/html/inbounds.html

@@ -830,7 +830,7 @@
             },
             async getDBInbounds() {
                 this.refreshing = true;
-                const msg = await HttpUtil.post('/panel/inbound/list');
+                const msg = await HttpUtil.get('/panel/api/inbounds/list');
                 if (!msg.success) {
                     this.refreshing = false;
                     return;
@@ -845,7 +845,7 @@
                 }, 500);
             },
             async getOnlineUsers() {
-                const msg = await HttpUtil.post('/panel/inbound/onlines');
+                const msg = await HttpUtil.post('/panel/api/inbounds/onlines');
                 if (!msg.success) {
                     return;
                 }
@@ -1099,7 +1099,7 @@
                     streamSettings: baseInbound.stream.toString(),
                     sniffing: baseInbound.sniffing.toString(),
                 };
-                await this.submit('/panel/inbound/add', data, inModal);
+                await this.submit('/panel/api/inbounds/add', data, inModal);
             },
             openAddInbound() {
                 inModal.show({
@@ -1148,7 +1148,7 @@
                 }
                 data.sniffing = inbound.sniffing.toString();
 
-                await this.submit('/panel/inbound/add', data, inModal);
+                await this.submit('/panel/api/inbounds/add', data, inModal);
             },
             async updateInbound(inbound, dbInbound) {
                 const data = {
@@ -1171,7 +1171,7 @@
                 }
                 data.sniffing = inbound.sniffing.toString();
 
-                await this.submit(`/panel/inbound/update/${dbInbound.id}`, data, inModal);
+                await this.submit(`/panel/api/inbounds/update/${dbInbound.id}`, data, inModal);
             },
             openAddClient(dbInboundId) {
                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
@@ -1226,14 +1226,14 @@
                     id: dbInboundId,
                     settings: '{"clients": [' + clients.toString() + ']}',
                 };
-                await this.submit(`/panel/inbound/addClient`, data, modal);
+                await this.submit(`/panel/api/inbounds/addClient`, data, modal);
             },
             async updateClient(client, dbInboundId, clientId) {
                 const data = {
                     id: dbInboundId,
                     settings: '{"clients": [' + client.toString() + ']}',
                 };
-                await this.submit(`/panel/inbound/updateClient/${clientId}`, data, clientModal);
+                await this.submit(`/panel/api/inbounds/updateClient/${clientId}`, data, clientModal);
             },
             resetTraffic(dbInboundId) {
                 dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
@@ -1258,7 +1258,7 @@
                     class: themeSwitcher.currentTheme,
                     okText: '{{ i18n "delete"}}',
                     cancelText: '{{ i18n "cancel"}}',
-                    onOk: () => this.submit('/panel/inbound/del/' + dbInboundId),
+                    onOk: () => this.submit('/panel/api/inbounds/del/' + dbInboundId),
                 });
             },
             delClient(dbInboundId, client,confirmation = true) {
@@ -1271,10 +1271,10 @@
                         class: themeSwitcher.currentTheme,
                         okText: '{{ i18n "delete"}}',
                         cancelText: '{{ i18n "cancel"}}',
-                        onOk: () => this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`),
+                        onOk: () => this.submit(`/panel/api/inbounds/${dbInboundId}/delClient/${clientId}`),
                     });
                 } else {
-                    this.submit(`/panel/inbound/${dbInboundId}/delClient/${clientId}`);
+                    this.submit(`/panel/api/inbounds/${dbInboundId}/delClient/${clientId}`);
                 }
             },
             getSubGroupClients(dbInbounds, currentClient) {
@@ -1353,7 +1353,7 @@
             switchEnable(dbInboundId,state) {
               dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
               dbInbound.enable = state;
-              this.submit(`/panel/inbound/update/${dbInboundId}`, dbInbound);
+              this.submit(`/panel/api/inbounds/update/${dbInboundId}`, dbInbound);
             },
             async switchEnableClient(dbInboundId, client) {
                 this.loading()
@@ -1383,10 +1383,10 @@
                         class: themeSwitcher.currentTheme,
                         okText: '{{ i18n "reset"}}',
                         cancelText: '{{ i18n "cancel"}}',
-                        onOk: () => this.submit('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email),
+                        onOk: () => this.submit('/panel/api/inbounds/' + dbInboundId + '/resetClientTraffic/' + client.email),
                     })
                 } else {
-                    this.submit('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + client.email);
+                    this.submit('/panel/api/inbounds/' + dbInboundId + '/resetClientTraffic/' + client.email);
                 }
             },
             resetAllTraffic() {
@@ -1396,7 +1396,7 @@
                     class: themeSwitcher.currentTheme,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
-                    onOk: () => this.submit('/panel/inbound/resetAllTraffics'),
+                    onOk: () => this.submit('/panel/api/inbounds/resetAllTraffics'),
                 });
             },
             resetAllClientTraffics(dbInboundId) {
@@ -1406,7 +1406,7 @@
                     class: themeSwitcher.currentTheme,
                     okText: '{{ i18n "reset"}}',
                     cancelText: '{{ i18n "cancel"}}',
-                    onOk: () => this.submit('/panel/inbound/resetAllClientTraffics/' + dbInboundId),
+                    onOk: () => this.submit('/panel/api/inbounds/resetAllClientTraffics/' + dbInboundId),
                 })
             },
             delDepletedClients(dbInboundId) {
@@ -1416,7 +1416,7 @@
                     class: themeSwitcher.currentTheme,
                     okText: '{{ i18n "delete"}}',
                     cancelText: '{{ i18n "cancel"}}',
-                    onOk: () => this.submit('/panel/inbound/delDepletedClients/' + dbInboundId),
+                    onOk: () => this.submit('/panel/api/inbounds/delDepletedClients/' + dbInboundId),
                 })
             },
             isExpiry(dbInbound, index) {
@@ -1542,7 +1542,7 @@
                     value: '',
                     okText: '{{ i18n "pages.inbounds.import" }}',
                     confirm: async (dbInboundText) => {
-                        await this.submit('/panel/inbound/import', {data: dbInboundText}, promptModal);
+                        await this.submit('/panel/api/inbounds/import', {data: dbInboundText}, promptModal);
                     },
                 });
             },

+ 13 - 13
web/html/index.html

@@ -746,7 +746,7 @@ ${dateTime}
             },
             async getStatus() {
                 try {
-                    const msg = await HttpUtil.post('/server/status');
+                    const msg = await HttpUtil.get('/panel/api/server/status');
                     if (msg.success) {
                         if (!this.loadingStates.fetched) {
                             this.loadingStates.fetched = true;
@@ -763,7 +763,7 @@ ${dateTime}
             },
             async openSelectV2rayVersion() {
                 this.loading(true);
-                const msg = await HttpUtil.post('server/getXrayVersion');
+                const msg = await HttpUtil.get('/panel/api/server/getXrayVersion');
                 this.loading(false);
                 if (!msg.success) {
                     return;
@@ -780,7 +780,7 @@ ${dateTime}
                     onOk: async () => {
                         versionModal.hide();
                         this.loading(true, '{{ i18n "pages.index.dontRefresh"}}');
-                        await HttpUtil.post(`/server/installXray/${version}`);
+                        await HttpUtil.post(`/panel/api/server/installXray/${version}`);
                         this.loading(false);
                     },
                 });
@@ -798,9 +798,9 @@ ${dateTime}
                     onOk: async () => {
                         versionModal.hide();
                         this.loading(true, '{{ i18n "pages.index.dontRefresh"}}');
-                        const url = isSingleFile 
-                            ? `/server/updateGeofile/${fileName}` 
-                            : `/server/updateGeofile`;
+                        const url = isSingleFile
+                            ? `/panel/api/server/updateGeofile/${fileName}`
+                            : `/panel/api/server/updateGeofile`;
                         await HttpUtil.post(url);
                         this.loading(false);
                     },
@@ -808,7 +808,7 @@ ${dateTime}
             },
             async stopXrayService() {
                 this.loading(true);
-                const msg = await HttpUtil.post('server/stopXrayService');
+                const msg = await HttpUtil.post('/panel/api/server/stopXrayService');
                 this.loading(false);
                 if (!msg.success) {
                     return;
@@ -816,7 +816,7 @@ ${dateTime}
             },
             async restartXrayService() {
                 this.loading(true);
-                const msg = await HttpUtil.post('server/restartXrayService');
+                const msg = await HttpUtil.post('/panel/api/server/restartXrayService');
                 this.loading(false);
                 if (!msg.success) {
                     return;
@@ -824,7 +824,7 @@ ${dateTime}
             },
             async openLogs(){
                 logModal.loading = true;
-                const msg = await HttpUtil.post('server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog});
+                const msg = await HttpUtil.post('/panel/api/server/logs/'+logModal.rows,{level: logModal.level, syslog: logModal.syslog});
                 if (!msg.success) {
                     return;
                 }
@@ -834,7 +834,7 @@ ${dateTime}
             },
             async openXrayLogs(){
               xraylogModal.loading = true;
-                const msg = await HttpUtil.post('server/xraylogs/'+xraylogModal.rows,{filter: xraylogModal.filter, showDirect: xraylogModal.showDirect, showBlocked: xraylogModal.showBlocked, showProxy: xraylogModal.showProxy});
+                const msg = await HttpUtil.post('/panel/api/server/xraylogs/'+xraylogModal.rows,{filter: xraylogModal.filter, showDirect: xraylogModal.showDirect, showBlocked: xraylogModal.showBlocked, showProxy: xraylogModal.showProxy});
                 if (!msg.success) {
                     return;
                 }
@@ -844,7 +844,7 @@ ${dateTime}
             },
             async openConfig() {
                 this.loading(true);
-                const msg = await HttpUtil.post('server/getConfigJson');
+                const msg = await HttpUtil.get('/panel/api/server/getConfigJson');
                 this.loading(false);
                 if (!msg.success) {
                     return;
@@ -855,7 +855,7 @@ ${dateTime}
               backupModal.show();
             },
             exportDatabase() {
-                window.location = basePath + 'server/getDb';
+                window.location = basePath + 'panel/api/server/getDb';
             },
             importDatabase() {
                 const fileInput = document.createElement('input');
@@ -868,7 +868,7 @@ ${dateTime}
                         formData.append('db', dbFile);
                         backupModal.hide();
                         this.loading(true);
-                        const uploadMsg = await HttpUtil.post('server/importDB', formData, {
+                        const uploadMsg = await HttpUtil.post('/panel/api/server/importDB', formData, {
                             headers: {
                                 'Content-Type': 'multipart/form-data',
                             }

+ 3 - 3
web/html/modals/client_modal.html

@@ -121,7 +121,7 @@
         },
         methods: {
             async getDBClientIps(email) {
-                const msg = await HttpUtil.post(`/panel/inbound/clientIps/${email}`);
+                const msg = await HttpUtil.post(`/panel/api/inbounds/clientIps/${email}`);
                 if (!msg.success) {
                     document.getElementById("clientIPs").value = msg.obj;
                     return;
@@ -139,7 +139,7 @@
             },
             async clearDBClientIps(email) {
                 try {
-                    const msg = await HttpUtil.post(`/panel/inbound/clearClientIps/${email}`);
+                    const msg = await HttpUtil.post(`/panel/api/inbounds/clearClientIps/${email}`);
                     if (!msg.success) {
                         return;
                     }
@@ -156,7 +156,7 @@
                     cancelText: '{{ i18n "cancel"}}',
                     onOk: async () => {
                         iconElement.disabled = true;
-                        const msg = await HttpUtil.postWithModal('/panel/inbound/' + dbInboundId + '/resetClientTraffic/' + email);
+                        const msg = await HttpUtil.postWithModal('/panel/api/inbounds/' + dbInboundId + '/resetClientTraffic/' + email);
                         if (msg.success) {
                             this.clientModal.clientStats.up = 0;
                             this.clientModal.clientStats.down = 0;

+ 2 - 2
web/html/modals/inbound_info_modal.html

@@ -492,7 +492,7 @@
 </a-modal>
 <script>
   function refreshIPs(email) {
-    return HttpUtil.post(`/panel/inbound/clientIps/${email}`).then((msg) => {
+    return HttpUtil.post(`/panel/api/inbounds/clientIps/${email}`).then((msg) => {
       if (msg.success) {
         try {
           return JSON.parse(msg.obj).join(', ');
@@ -613,7 +613,7 @@
           });
       },
       clearClientIps() {
-        HttpUtil.post(`/panel/inbound/clearClientIps/${this.infoModal.clientStats.email}`)
+        HttpUtil.post(`/panel/api/inbounds/clearClientIps/${this.infoModal.clientStats.email}`)
           .then((msg) => {
             if (!msg.success) {
               return;

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

@@ -132,7 +132,7 @@
             },
             async getNewX25519Cert() {
                 inModal.loading(true);
-                const msg = await HttpUtil.post('/server/getNewX25519Cert');
+                const msg = await HttpUtil.get('/panel/api/server/getNewX25519Cert');
                 inModal.loading(false);
                 if (!msg.success) {
                     return;
@@ -140,9 +140,13 @@
                 inModal.inbound.stream.reality.privateKey = msg.obj.privateKey;
                 inModal.inbound.stream.reality.settings.publicKey = msg.obj.publicKey;
             },
+            clearX25519Cert() {
+                this.inbound.stream.reality.privateKey = '';
+                this.inbound.stream.reality.settings.publicKey = '';
+            },
             async getNewmldsa65() {
                 inModal.loading(true);
-                const msg = await HttpUtil.post('/server/getNewmldsa65');
+                const msg = await HttpUtil.get('/panel/api/server/getNewmldsa65');
                 inModal.loading(false);
                 if (!msg.success) {
                     return;
@@ -150,9 +154,13 @@
                 inModal.inbound.stream.reality.mldsa65Seed = msg.obj.seed;
                 inModal.inbound.stream.reality.settings.mldsa65Verify = msg.obj.verify;
             },
+            clearMldsa65() {
+                this.inbound.stream.reality.mldsa65Seed = '';
+                this.inbound.stream.reality.settings.mldsa65Verify = '';
+            },
             async getNewEchCert() {
                 inModal.loading(true);
-                const msg = await HttpUtil.post('/server/getNewEchCert', { sni: inModal.inbound.stream.tls.sni });
+                const msg = await HttpUtil.post('/panel/api/server/getNewEchCert', { sni: inModal.inbound.stream.tls.sni });
                 inModal.loading(false);
                 if (!msg.success) {
                     return;
@@ -160,9 +168,13 @@
                 inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys;
                 inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList;
             },
+            clearEchCert() {
+                this.inbound.stream.tls.echServerKeys = '';
+                this.inbound.stream.tls.settings.echConfigList = '';
+            },
             async getNewVlessEnc() {
                 inModal.loading(true);
-                const msg = await HttpUtil.post('/server/getNewVlessEnc');
+                const msg = await HttpUtil.get('/panel/api/server/getNewVlessEnc');
                 inModal.loading(false);
 
                 if (!msg.success) {
@@ -181,7 +193,7 @@
                 inModal.inbound.settings.decryption = block.decryption;
                 inModal.inbound.settings.encryption = block.encryption;
             },
-            clearKeys() {
+            clearVlessEnc() {
                 this.inbound.settings.decryption = 'none';
                 this.inbound.settings.encryption = 'none';
                 this.inbound.settings.selectedAuth = undefined;

+ 1 - 1
web/html/modals/qrcode_modal.html

@@ -151,7 +151,7 @@
     methods: {
       async getStatus() {
         try {
-          const msg = await HttpUtil.post('/server/status');
+          const msg = await HttpUtil.get('/panel/api/server/status');
           if (msg.success) {
             this.serverStatus = msg.obj;
           }

+ 1 - 1
web/html/xray.html

@@ -420,7 +420,7 @@
       },
       async restartXray() {
         this.loading(true);
-        const msg = await HttpUtil.post("server/restartXrayService");
+        const msg = await HttpUtil.post("/panel/api/server/restartXrayService");
         this.loading(false);
         if (msg.success) {
           await PromiseUtil.sleep(500);

+ 47 - 19
web/service/server.go

@@ -24,6 +24,7 @@ import (
 	"x-ui/util/sys"
 	"x-ui/xray"
 
+	"github.com/google/uuid"
 	"github.com/shirou/gopsutil/v4/cpu"
 	"github.com/shirou/gopsutil/v4/disk"
 	"github.com/shirou/gopsutil/v4/host"
@@ -872,12 +873,6 @@ func (s *ServerService) GetNewEchCert(sni string) (interface{}, error) {
 	}, nil
 }
 
-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
@@ -887,37 +882,70 @@ func (s *ServerService) GetNewVlessEnc() (any, error) {
 	}
 
 	lines := strings.Split(out.String(), "\n")
-
-	var blocks []AuthBlock
-	var current *AuthBlock
+	var auths []map[string]string
+	var current map[string]string
 
 	for _, line := range lines {
 		line = strings.TrimSpace(line)
 		if strings.HasPrefix(line, "Authentication:") {
 			if current != nil {
-				blocks = append(blocks, *current)
+				auths = append(auths, current)
+			}
+			current = map[string]string{
+				"label": strings.TrimSpace(strings.TrimPrefix(line, "Authentication:")),
 			}
-			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], `" `)
-				switch key {
-				case "decryption":
-					current.Decryption = val
-				case "encryption":
-					current.Encryption = val
-				}
+				current[key] = val
 			}
 		}
 	}
 
 	if current != nil {
-		blocks = append(blocks, *current)
+		auths = append(auths, current)
 	}
 
 	return map[string]any{
-		"auths": blocks,
+		"auths": auths,
+	}, nil
+}
+
+func (s *ServerService) GetNewUUID() (map[string]string, error) {
+	newUUID, err := uuid.NewRandom()
+	if err != nil {
+		return nil, fmt.Errorf("failed to generate UUID: %w", err)
+	}
+
+	return map[string]string{
+		"uuid": newUUID.String(),
 	}, nil
 }
+
+func (s *ServerService) GetNewmlkem768() (any, error) {
+	// Run the command
+	cmd := exec.Command(xray.GetBinaryPath(), "mlkem768")
+	var out bytes.Buffer
+	cmd.Stdout = &out
+	err := cmd.Run()
+	if err != nil {
+		return nil, err
+	}
+
+	lines := strings.Split(out.String(), "\n")
+
+	SeedLine := strings.Split(lines[0], ":")
+	ClientLine := strings.Split(lines[1], ":")
+
+	seed := strings.TrimSpace(SeedLine[1])
+	client := strings.TrimSpace(ClientLine[1])
+
+	keyPair := map[string]any{
+		"seed":   seed,
+		"client": client,
+	}
+
+	return keyPair, nil
+}

+ 1 - 1
web/web.go

@@ -176,7 +176,7 @@ func (s *Server) initRouter() (*gin.Engine, error) {
 	if err != nil {
 		return nil, err
 	}
-	engine.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{basePath + "panel/API/"})))
+	engine.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{basePath + "panel/api/"})))
 	assetsBasePath := basePath + "assets/"
 
 	store := cookie.NewStore(secret)