diff --git a/web/assets/css/custom.css b/web/assets/css/custom.css index 229d8500..741b01ae 100644 --- a/web/assets/css/custom.css +++ b/web/assets/css/custom.css @@ -246,6 +246,11 @@ background-color: #2e3b52; } +.ant-card-dark .ant-select-disabled .ant-select-selection { + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: #242c3a; +} + .ant-card-dark .ant-collapse-item { color: hsla(0,0%,100%,.65); background-color: #161b22; diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index f5c7da3f..3e3ff9cb 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -49,6 +49,7 @@ const XTLS_FLOW_CONTROL = { const TLS_FLOW_CONTROL = { VISION: "xtls-rprx-vision", + VISION_UDP443: "xtls-rprx-vision-udp443", }; const TLS_VERSION_OPTION = { @@ -91,9 +92,6 @@ const UTLS_FINGERPRINT = { UTLS_RANDOMIZED: "randomized", }; -const bytesToHex = e => Array.from(e).map(e => e.toString(16).padStart(2, 0)).join(''); -const hexToBytes = e => new Uint8Array(e.match(/[0-9a-f]{2}/gi).map(e => parseInt(e, 16))); - const ALPN_OPTION = { H3: "h3", H2: "h2", @@ -481,7 +479,7 @@ class TlsStreamSettings extends XrayCommonClass { cipherSuites = '', certificates=[new TlsStreamSettings.Cert()], alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1], - settings=[new TlsStreamSettings.Settings()]) { + settings=new TlsStreamSettings.Settings()) { super(); this.server = serverName; this.minVersion = minVersion; @@ -508,8 +506,7 @@ class TlsStreamSettings extends XrayCommonClass { } if (!ObjectUtil.isEmpty(json.settings)) { - let values = json.settings[0]; - settings = [new TlsStreamSettings.Settings(values.allowInsecure , values.fingerprint, values.serverName)]; + settings = new TlsStreamSettings.Settings(json.settings.allowInsecure , json.settings.fingerprint, json.settings.serverName); } return new TlsStreamSettings( json.serverName, @@ -530,7 +527,7 @@ class TlsStreamSettings extends XrayCommonClass { cipherSuites: this.cipherSuites, certificates: TlsStreamSettings.toJsonArray(this.certs), alpn: this.alpn, - settings: TlsStreamSettings.toJsonArray(this.settings), + settings: this.settings, }; } } @@ -598,71 +595,204 @@ TlsStreamSettings.Settings = class extends XrayCommonClass { }; } }; +class XtlsStreamSettings extends XrayCommonClass { + constructor(serverName='', + certificates=[new XtlsStreamSettings.Cert()], + alpn=[ALPN_OPTION.H2,ALPN_OPTION.HTTP1], + settings=new XtlsStreamSettings.Settings()) { + super(); + this.server = serverName; + this.certs = certificates; + this.alpn = alpn; + this.settings = settings; + } + + addCert(cert) { + this.certs.push(cert); + } + + removeCert(index) { + this.certs.splice(index, 1); + } + + static fromJson(json={}) { + let certs; + let settings; + if (!ObjectUtil.isEmpty(json.certificates)) { + certs = json.certificates.map(cert => XtlsStreamSettings.Cert.fromJson(cert)); + } + + if (!ObjectUtil.isEmpty(json.settings)) { + settings = new XtlsStreamSettings.Settings(json.settings.allowInsecure , json.settings.serverName); + } + return new XtlsStreamSettings( + json.serverName, + certs, + json.alpn, + settings, + ); + } + + toJson() { + return { + serverName: this.server, + certificates: XtlsStreamSettings.toJsonArray(this.certs), + alpn: this.alpn, + settings: this.settings, + }; + } +} + +XtlsStreamSettings.Cert = class extends XrayCommonClass { + constructor(useFile=true, certificateFile='', keyFile='', certificate='', key='') { + super(); + this.useFile = useFile; + this.certFile = certificateFile; + this.keyFile = keyFile; + this.cert = certificate instanceof Array ? certificate.join('\n') : certificate; + this.key = key instanceof Array ? key.join('\n') : key; + } + + static fromJson(json={}) { + if ('certificateFile' in json && 'keyFile' in json) { + return new XtlsStreamSettings.Cert( + true, + json.certificateFile, + json.keyFile, + ); + } else { + return new XtlsStreamSettings.Cert( + false, '', '', + json.certificate.join('\n'), + json.key.join('\n'), + ); + } + } + + toJson() { + if (this.useFile) { + return { + certificateFile: this.certFile, + keyFile: this.keyFile, + }; + } else { + return { + certificate: this.cert.split('\n'), + key: this.key.split('\n'), + }; + } + } +}; + +XtlsStreamSettings.Settings = class extends XrayCommonClass { + constructor(allowInsecure = false, serverName = '') { + super(); + this.allowInsecure = allowInsecure; + this.serverName = serverName; + } + static fromJson(json = {}) { + return new XtlsStreamSettings.Settings( + json.allowInsecure, + json.servername, + ); + } + toJson() { + return { + allowInsecure: this.allowInsecure, + serverName: this.serverName, + }; + } +}; class RealityStreamSettings extends XrayCommonClass { constructor( show = false,xver = 0, - fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, dest = 'yahoo.com:443', serverNames = 'yahoo.com,www.yahoo.com', - privateKey = RandomUtil.randomX25519PrivateKey(), - publicKey = '', + privateKey = '', minClient = '', maxClient = '', maxTimediff = 0, - shortIds = RandomUtil.randowShortId() - ) - { + shortIds = RandomUtil.randowShortId(), + settings= new RealityStreamSettings.Settings() + ){ super(); this.show = show; this.xver = xver; - this.fingerprint = fingerprint; this.dest = dest; this.serverNames = serverNames instanceof Array ? serverNames.join(",") : serverNames; this.privateKey = privateKey; - this.publicKey = RandomUtil.randomX25519PublicKey(this.privateKey); this.minClient = minClient; this.maxClient = maxClient; this.maxTimediff = maxTimediff; this.shortIds = shortIds instanceof Array ? shortIds.join(",") : shortIds; + this.settings = settings; + } + + static fromJson(json = {}) { + let settings; + if (!ObjectUtil.isEmpty(json.settings)) { + settings = new RealityStreamSettings.Settings(json.settings.publicKey , json.settings.fingerprint, json.settings.serverName); } - static fromJson(json = {}) { return new RealityStreamSettings( json.show, json.xver, - json.fingerprint, json.dest, json.serverNames, json.privateKey, - json.publicKey, json.minClient, json.maxClient, json.maxTimediff, - json.shortIds + json.shortIds, + json.settings, ); - } - toJson() { + + } + toJson() { return { show: this.show, xver: this.xver, - fingerprint: this.fingerprint, dest: this.dest, - serverNames: this.serverNames.split(/,|,|\s+/), + serverNames: this.serverNames.split(","), privateKey: this.privateKey, - publicKey: this.publicKey, minClient: this.minClient, maxClient: this.maxClient, maxTimediff: this.maxTimediff, - shortIds: this.shortIds.split(/,|,|\s+/) - }; - } + shortIds: this.shortIds.split(","), + settings: this.settings, + }; } +} + +RealityStreamSettings.Settings = class extends XrayCommonClass { + constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '') { + super(); + this.publicKey = publicKey; + this.fingerprint = fingerprint; + this.serverName = serverName; + } + static fromJson(json = {}) { + return new RealityStreamSettings.Settings( + json.publicKey, + json.fingerprint, + json.serverName, + ); + } + toJson() { + return { + publicKey: this.publicKey, + fingerprint: this.fingerprint, + serverName: this.serverName, + }; + } +}; class StreamSettings extends XrayCommonClass { constructor(network='tcp', security='none', tlsSettings=new TlsStreamSettings(), + xtlsSettings=new XtlsStreamSettings(), realitySettings = new RealityStreamSettings(), tcpSettings=new TcpStreamSettings(), kcpSettings=new KcpStreamSettings(), @@ -675,6 +805,7 @@ class StreamSettings extends XrayCommonClass { this.network = network; this.security = security; this.tls = tlsSettings; + this.xtls = xtlsSettings; this.reality = realitySettings; this.tcp = tcpSettings; this.kcp = kcpSettings; @@ -685,7 +816,7 @@ class StreamSettings extends XrayCommonClass { } get isTls() { - return this.security === 'tls'; + return this.security === "tls"; } set isTls(isTls) { @@ -696,12 +827,12 @@ class StreamSettings extends XrayCommonClass { } } - get isXTLS() { + get isXtls() { return this.security === "xtls"; } - set isXTLS(isXTLS) { - if (isXTLS) { + set isXtls(isXtls) { + if (isXtls) { this.security = 'xtls'; } else { this.security = 'none'; @@ -715,27 +846,19 @@ class StreamSettings extends XrayCommonClass { set isReality(isReality) { if (isReality) { - this.security = "reality"; + this.security = 'reality'; } else { - this.security = "none"; + this.security = 'none'; } } - - static fromJson(json = {}) { - let tls, reality; - if (json.security === "xtls") { - tls = TlsStreamSettings.fromJson(json.XTLSSettings); - } else if (json.security === "tls") { - tls = TlsStreamSettings.fromJson(json.tlsSettings); - } - if (json.security === "reality") { - reality = RealityStreamSettings.fromJson(json.realitySettings) - } + + static fromJson(json={}) { return new StreamSettings( json.network, json.security, - tls, - reality, + TlsStreamSettings.fromJson(json.tlsSettings), + XtlsStreamSettings.fromJson(json.xtlsSettings), + RealityStreamSettings.fromJson(json.realitySettings), TcpStreamSettings.fromJson(json.tcpSettings), KcpStreamSettings.fromJson(json.kcpSettings), WsStreamSettings.fromJson(json.wsSettings), @@ -751,9 +874,9 @@ class StreamSettings extends XrayCommonClass { network: network, security: this.security, tlsSettings: this.isTls ? this.tls.toJson() : undefined, - XTLSSettings: this.isXTLS ? this.tls.toJson() : undefined, - tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined, + xtlsSettings: this.isXtls ? this.xtls.toJson() : undefined, realitySettings: this.isReality ? this.reality.toJson() : undefined, + tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined, kcpSettings: network === 'kcp' ? this.kcp.toJson() : undefined, wsSettings: network === 'ws' ? this.ws.toJson() : undefined, httpSettings: network === 'http' ? this.http.toJson() : undefined, @@ -826,22 +949,18 @@ class Inbound extends XrayCommonClass { set tls(isTls) { if (isTls) { - this.xtls = false; - this.reality = false; this.stream.security = 'tls'; } else { this.stream.security = 'none'; } } - get XTLS() { + get xtls() { return this.stream.security === 'xtls'; } - set XTLS(isXTLS) { - if (isXTLS) { - this.xtls = false; - this.reality = false; + set xtls(isXtls) { + if (isXtls) { this.stream.security = 'xtls'; } else { this.stream.security = 'none'; @@ -850,19 +969,14 @@ class Inbound extends XrayCommonClass { //for Reality get reality() { - if (this.stream.security === "reality") { - return this.network === "tcp" || this.network === "grpc" || this.network === "http"; - } - return false; + return this.stream.security === 'reality'; } set reality(isReality) { if (isReality) { - this.tls = false; - this.xtls = false; - this.stream.security = "reality"; + this.stream.security = 'reality'; } else { - this.stream.security = "none"; + this.stream.security = 'none'; } } @@ -969,7 +1083,7 @@ class Inbound extends XrayCommonClass { } get serverName() { - if (this.stream.isTls || this.stream.isXTLS) { + if (this.stream.isTls || this.stream.isXtls || this.stream.isReality) { return this.stream.tls.server; } return ""; @@ -1070,7 +1184,14 @@ class Inbound extends XrayCommonClass { default: return false; } - return this.network === "tcp" || this.network === "grpc" || this.network === "http"; + switch (this.network) { + case "tcp": + case "http": + case "grpc": + return true; + default: + return false; + } } //this is used for xtls-rprx-vision @@ -1090,7 +1211,7 @@ class Inbound extends XrayCommonClass { return this.canEnableTls(); } - canEnableXTLS() { + canEnableXtls() { switch (this.protocol) { case Protocols.VLESS: case Protocols.TROJAN: @@ -1195,10 +1316,10 @@ class Inbound extends XrayCommonClass { host: host, path: path, tls: this.stream.security, - sni: this.stream.tls.settings[0]['serverName'], - fp: this.stream.tls.settings[0]['fingerprint'], + sni: this.stream.tls.settings.serverName, + fp: this.stream.tls.settings.fingerprint, alpn: this.stream.tls.alpn.join(','), - allowInsecure: this.stream.tls.settings[0].allowInsecure, + allowInsecure: this.stream.tls.settings.allowInsecure, }; return 'vmess://' + base64(JSON.stringify(obj, null, 2)); } @@ -1257,54 +1378,54 @@ class Inbound extends XrayCommonClass { if (this.tls) { params.set("security", "tls"); - params.set("fp" , this.stream.tls.settings[0]['fingerprint']); + params.set("fp" , this.stream.tls.settings.fingerprint); params.set("alpn", this.stream.tls.alpn); - if(this.stream.tls.settings[0].allowInsecure){ + if(this.stream.tls.settings.allowInsecure){ params.set("allowInsecure", "1"); } if (!ObjectUtil.isEmpty(this.stream.tls.server)) { address = this.stream.tls.server; } - if (this.stream.tls.settings[0]['serverName'] !== ''){ - params.set("sni", this.stream.tls.settings[0]['serverName']); + if (this.stream.tls.settings.serverName !== ''){ + params.set("sni", this.stream.tls.settings.serverName); } if (type === "tcp" && this.settings.vlesses[clientIndex].flow.length > 0) { params.set("flow", this.settings.vlesses[clientIndex].flow); } } - if (this.XTLS) { + if (this.xtls) { params.set("security", "xtls"); - params.set("alpn", this.stream.tls.alpn); - if(this.stream.tls.settings[0].allowInsecure){ + params.set("alpn", this.stream.xtls.alpn); + if(this.stream.xtls.settings.allowInsecure){ params.set("allowInsecure", "1"); } - if (!ObjectUtil.isEmpty(this.stream.tls.server)) { - address = this.stream.tls.server; - } + if (!ObjectUtil.isEmpty(this.stream.xtls.server)) { + address = this.stream.xtls.server; + } params.set("flow", this.settings.vlesses[clientIndex].flow); } if (this.reality) { params.set("security", "reality"); + params.set("pbk", this.stream.reality.settings.publicKey); if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { - params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); - } - if (this.stream.reality.publicKey != "") { - //params.set("pbk", Ed25519.getPublicKey(this.stream.reality.privateKey)); - params.set("pbk", this.stream.reality.publicKey); + params.set("sni", this.stream.reality.serverNames.split(",")[0]); } if (this.stream.network === 'tcp') { params.set("flow", this.settings.vlesses[clientIndex].flow); } if (this.stream.reality.shortIds != "") { - params.set("sid", this.stream.reality.shortIds); + params.set("sid", this.stream.reality.shortIds.split(",")[0]); } - if (this.stream.reality.fingerprint != "") { - params.set("fp", this.stream.reality.fingerprint); + if (this.stream.reality.settings.fingerprint != "") { + params.set("fp", this.stream.reality.settings.fingerprint); + } + if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) { + address = this.stream.reality.settings.serverName; } } - + const link = `vless://${uuid}@${address}:${port}`; const url = new URL(link); for (const [key, value] of params) { @@ -1376,47 +1497,47 @@ class Inbound extends XrayCommonClass { if (this.tls) { params.set("security", "tls"); - params.set("fp" , this.stream.tls.settings[0]['fingerprint']); + params.set("fp" , this.stream.tls.settings.fingerprint); params.set("alpn", this.stream.tls.alpn); - if(this.stream.tls.settings[0].allowInsecure){ + if(this.stream.tls.settings.allowInsecure){ params.set("allowInsecure", "1"); } if (!ObjectUtil.isEmpty(this.stream.tls.server)) { address = this.stream.tls.server; } - if (this.stream.tls.settings[0]['serverName'] !== ''){ - params.set("sni", this.stream.tls.settings[0]['serverName']); + if (this.stream.tls.settings.serverName !== ''){ + params.set("sni", this.stream.tls.settings.serverName); } } if (this.reality) { params.set("security", "reality"); + params.set("pbk", this.stream.reality.settings.publicKey); if (!ObjectUtil.isArrEmpty(this.stream.reality.serverNames)) { - params.set("sni", this.stream.reality.serverNames.split(/,|,|\s+/)[0]); + params.set("sni", this.stream.reality.serverNames.split(",")[0]); } - if (this.stream.reality.publicKey != "") { - //params.set("pbk", Ed25519.getPublicKey(this.stream.reality.privateKey)); - params.set("pbk", this.stream.reality.publicKey); - } - if (this.stream.network === 'tcp') { - params.set("flow", this.settings.trojans[clientIndex].flow); + if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) { + address = this.stream.reality.settings.serverName; } if (this.stream.reality.shortIds != "") { - params.set("sid", this.stream.reality.shortIds); + params.set("sid", this.stream.reality.shortIds.split(",")[0]); } - if (this.stream.reality.fingerprint != "") { - params.set("fp", this.stream.reality.fingerprint); + if (this.stream.reality.settings.fingerprint != "") { + params.set("fp", this.stream.reality.settings.fingerprint); + } + if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) { + address = this.stream.reality.settings.serverName; } } - if (this.XTLS) { + if (this.xtls) { params.set("security", "xtls"); - params.set("alpn", this.stream.tls.alpn); - if(this.stream.tls.settings[0].allowInsecure){ + params.set("alpn", this.stream.xtls.alpn); + if(this.stream.xtls.settings.allowInsecure){ params.set("allowInsecure", "1"); } if (!ObjectUtil.isEmpty(this.stream.tls.server)) { - address = this.stream.tls.server; + address = this.stream.xtls.server; } params.set("flow", this.settings.trojans[clientIndex].flow); } diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js index 405985da..451f63e9 100644 --- a/web/assets/js/util/utils.js +++ b/web/assets/js/util/utils.js @@ -94,26 +94,6 @@ const shortIdSeq = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ]; -const x25519Map = new Map( - [ - ['EH2FWe-Ij_FFAa2u9__-aiErLvVIneP601GOCdlyPWw', "goY3OtfaA4UYbiz7Hn0NysI5QJrK0VT_Chg6RLgUPQU"], - ['cKI_6DoMSP1IepeWWXrG3G9nkehl94KYBhagU50g2U0', "VigpKFbSLnHLzBWobZaS1IBmw--giJ51w92y723ajnU"], - ['qM2SNyK3NyHB6deWpEP3ITyCGKQFRTna_mlKP0w1QH0', "HYyIGuyNFslmcnNT7mrDdmuXwn4cm7smE_FZbYguKHQ"], - ['qCWg5GMEDFd3n1nxDswlIpOHoPUXMLuMOIiLUVzubkI', "rJFC3dUjJxMnVZiUGzmf_LFsJUwFWY-CU5RQgFOHCWM"], - ['4NOBxDrEsOhNI3Y3EnVIy_TN-uyBoAjQw6QM0YsOi0s', "CbcY9qc4YuMDJDyyL0OITlU824TBg1O84ClPy27e2RM"], - ['eBvFb0M4HpSOwWjtXV8zliiEs_hg56zX4a2LpuuqpEI', "CjulQ2qVIky7ImIfysgQhNX7s_drGLheCGSkVHcLZhc"], - ['yEpOzQV04NNcycWVeWtRNTzv5TS-ynTuKRacZCH-6U8', "O9RSr5gSdok2K_tobQnf_scyKVqnCx6C4Jrl7_rCZEQ"], - ['CNt6TAUVCwqM6xIBHyni0K3Zqbn2htKQLvLb6XDgh0s', "d9cGLVBrDFS02L2OvkqyqwFZ1Ux3AHs28ehl4Rwiyl0"], - ['EInKw-6Wr0rAHXlxxDuZU5mByIzcD3Z-_iWPzXlUL1k', "LlYD2nNVAvyjNvjZGZh4R8PkMIwkc6EycPTvR2LE0nQ"], - ['GKIKo7rcXVyle-EUHtGIDtYnDsI6osQmOUl3DTJRAGc', "VcqHivYGGoBkcxOI6cSSjQmneltstkb2OhvO53dyhEM"], - ['-FVDzv68IC17fJVlNDlhrrgX44WeBfbhwjWpCQVXGHE', "PGG2EYOvsFt2lAQTD7lqHeRxz2KxvllEDKcUrtizPBU"], - ['0H3OJEYEu6XW7woqy7cKh2vzg6YHkbF_xSDTHKyrsn4', "mzevpYbS8kXengBY5p7tt56QE4tS3lwlwRemmkcQeyc"], - ['8F8XywN6ci44ES6em2Z0fYYxyptB9uaXY9Hc1WSSPE4', "qCZUdWQZ2H33vWXnOkG8NpxBeq3qn5QWXlfCOWBNkkc"], - ['IN0dqfkC10dj-ifRHrg2PmmOrzYs697ajGMwcLbu-1g', "2UW_EO3r7uczPGUUlpJBnMDpDmWUHE2yDzCmXS4sckE"], - ['uIcmks5rAhvBe4dRaJOdeSqgxLGGMZhsGk4J4PEKL2s', "F9WJV_74IZp0Ide4hWjiJXk9FRtBUBkUr3mzU-q1lzk"], - ] -); - class RandomUtil { static randomIntRange(min, max) { @@ -170,26 +150,6 @@ class RandomUtil { }); } - static randowShortId() { - let str = ''; - str += this.randomShortIdSeq(8) - return str; - } - - static randomX25519PrivateKey() { - let num = x25519Map.size; - let index = this.randomInt(num); - let cntr = 0; - for (let key of x25519Map.keys()) { - if (cntr++ === index) { - return key; - } - } - } - - static randomX25519PublicKey(key) { - return x25519Map.get(key) - } static randomText() { var chars = 'abcdefghijklmnopqrstuvwxyz1234567890'; var string = ''; @@ -199,6 +159,12 @@ class RandomUtil { } return string; } + + static randowShortId() { + let str = ''; + str += this.randomShortIdSeq(8) + return str; + } } class ObjectUtil { diff --git a/web/controller/inbound.go b/web/controller/inbound.go index f7ea35eb..8e385248 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -33,7 +33,7 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) { g.POST("/update/:id", a.updateInbound) g.POST("/clientIps/:email", a.getClientIps) g.POST("/clearClientIps/:email", a.clearClientIps) - g.POST("/addClient/", a.addInboundClient) + g.POST("/addClient", a.addInboundClient) g.POST("/delClient/:email", a.delInboundClient) g.POST("/updateClient/:index", a.updateInboundClient) g.POST("/:id/resetClientTraffic/:email", a.resetClientTraffic) @@ -151,19 +151,19 @@ func (a *InboundController) clearClientIps(c *gin.Context) { jsonMsg(c, "Log Cleared", nil) } func (a *InboundController) addInboundClient(c *gin.Context) { - inbound := &model.Inbound{} - err := c.ShouldBind(inbound) + data := &model.Inbound{} + err := c.ShouldBind(data) if err != nil { jsonMsg(c, I18n(c, "pages.inbounds.revise"), err) return } - err = a.inboundService.AddInboundClient(inbound) + err = a.inboundService.AddInboundClient(data) if err != nil { jsonMsg(c, "something worng!", err) return } - jsonMsg(c, "Client added", nil) + jsonMsg(c, "Client(s) added", nil) if err == nil { a.xrayService.SetToNeedRestart() } diff --git a/web/controller/server.go b/web/controller/server.go index 24c3d623..c365ae4b 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -41,6 +41,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) { g.POST("/logs/:count", a.getLogs) g.POST("/getConfigJson", a.getConfigJson) g.GET("/getDb", a.getDb) + g.POST("/getNewX25519Cert", a.getNewX25519Cert) } func (a *ServerController) refreshStatus() { @@ -114,7 +115,7 @@ func (a *ServerController) getLogs(c *gin.Context) { count := c.Param("count") logs, err := a.serverService.GetLogs(count) if err != nil { - jsonMsg(c, I18n(c, "getLogs"), err) + jsonMsg(c, "getLogs", err) return } jsonObj(c, logs, nil) @@ -123,7 +124,7 @@ func (a *ServerController) getLogs(c *gin.Context) { func (a *ServerController) getConfigJson(c *gin.Context) { configJson, err := a.serverService.GetConfigJson() if err != nil { - jsonMsg(c, I18n(c, "getLogs"), err) + jsonMsg(c, "get config.json", err) return } jsonObj(c, configJson, nil) @@ -132,7 +133,7 @@ func (a *ServerController) getConfigJson(c *gin.Context) { func (a *ServerController) getDb(c *gin.Context) { db, err := a.serverService.GetDb() if err != nil { - jsonMsg(c, I18n(c, "getLogs"), err) + jsonMsg(c, "get Database", err) return } // Set the headers for the response @@ -142,3 +143,12 @@ func (a *ServerController) getDb(c *gin.Context) { // Write the file contents to the response c.Writer.Write(db) } + +func (a *ServerController) getNewX25519Cert(c *gin.Context) { + cert, err := a.serverService.GetNewX25519Cert() + if err != nil { + jsonMsg(c, "get x25519 certificate", err) + return + } + jsonObj(c, cert, nil) +} diff --git a/web/controller/sub.go b/web/controller/sub.go index 5695f032..9a8dfc19 100644 --- a/web/controller/sub.go +++ b/web/controller/sub.go @@ -29,14 +29,18 @@ func (a *SUBController) initRouter(g *gin.RouterGroup) { func (a *SUBController) subs(c *gin.Context) { subId := c.Param("subid") host := strings.Split(c.Request.Host, ":")[0] - subs, err := a.subService.GetSubs(subId, host) - if err != nil { + subs, header, err := a.subService.GetSubs(subId, host) + if err != nil || len(subs) == 0 { c.String(400, "Error!") } else { result := "" for _, sub := range subs { result += sub + "\n" } + + // Add subscription-userinfo + c.Writer.Header().Set("subscription-userinfo", header) + c.String(200, base64.StdEncoding.EncodeToString([]byte(result))) } } diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html index 4e282ccd..46bc6657 100644 --- a/web/html/xui/client_bulk_modal.html +++ b/web/html/xui/client_bulk_modal.html @@ -33,6 +33,30 @@ {{ i18n "pages.client.clientCount" }} + + + {{ i18n "pages.inbounds.IPLimit" }} + + + + + + + + + + {{ i18n "none" }} + [[ key ]] + + + + + {{ i18n "none" }} + [[ key ]] + + @@ -51,10 +75,10 @@ - + - + @@ -83,9 +107,9 @@ confirm: null, dbInbound: new DBInbound(), inbound: new Inbound(), - clients: [], quantity: 1, totalGB: 0, + limitIp: 0, expiryTime: '', emailMethod: 0, firstNum: 1, @@ -94,8 +118,10 @@ emailPostfix: "", subId: "", tgId: "", + flow: "", delayedStart: false, ok() { + clients = []; method=clientsBulkModal.emailMethod; if(method>1){ start=clientsBulkModal.firstNum; @@ -113,11 +139,18 @@ newClient.email += useNum ? prefix + i.toString() + postfix : prefix + postfix; newClient.subId = clientsBulkModal.subId; newClient.tgId = clientsBulkModal.tgId; + newClient.limitIp = clientsBulkModal.limitIp; newClient._totalGB = clientsBulkModal.totalGB; newClient._expiryTime = clientsBulkModal.expiryTime; - clientsBulkModal.clients.push(newClient); + if(clientsBulkModal.inbound.canEnableTlsFlow()){ + newClient.flow = clientsBulkModal.flow; + } + if(clientsBulkModal.inbound.xtls){ + newClient.flow = clientsBulkModal.flow; + } + clients.push(newClient); } - ObjectUtil.execute(clientsBulkModal.confirm, clientsBulkModal.inbound, clientsBulkModal.dbInbound); + ObjectUtil.execute(clientsBulkModal.confirm, clients, clientsBulkModal.dbInbound.id); }, show({ title='', okText='{{ i18n "sure" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) { this.visible = true; @@ -128,15 +161,16 @@ this.totalGB = 0; this.expiryTime = 0; this.emailMethod= 0; + this.limitIp= 0; this.firstNum= 1; this.lastNum= 1; this.emailPrefix= ""; this.emailPostfix= ""; this.subId= ""; this.tgId= ""; + this.flow= ""; this.dbInbound = new DBInbound(dbInbound); this.inbound = dbInbound.toInbound(); - this.clients = this.getClients(this.inbound.protocol, this.inbound.settings); this.delayedStart = false; }, getClients(protocol, clientSettings) { diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html index d1078f23..d90e6156 100644 --- a/web/html/xui/client_modal.html +++ b/web/html/xui/client_modal.html @@ -12,6 +12,7 @@ confirmLoading: false, title: '', okText: '', + isEdit: false, dbInbound: new DBInbound(), inbound: new Inbound(), clients: [], @@ -21,9 +22,13 @@ isExpired: false, delayedStart: false, ok() { - ObjectUtil.execute(clientModal.confirm, clientModal.inbound, clientModal.dbInbound, clientModal.index); + if(clientModal.isEdit){ + ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.index); + } else { + ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id); + } }, - show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=(index, dbInbound)=>{}, isEdit=false }) { + show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=()=>{}, isEdit=false }) { this.visible = true; this.title = title; this.okText = okText; diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html index 1de05ac9..2d8c5d2b 100644 --- a/web/html/xui/form/client.html +++ b/web/html/xui/form/client.html @@ -68,7 +68,7 @@ - + {{ i18n "none" }} [[ key ]] @@ -100,10 +100,10 @@ - + - + diff --git a/web/html/xui/form/protocol/trojan.html b/web/html/xui/form/protocol/trojan.html index 91fd7afd..9f5c120a 100644 --- a/web/html/xui/form/protocol/trojan.html +++ b/web/html/xui/form/protocol/trojan.html @@ -1,7 +1,7 @@ {{define "form/trojan"}} - + @@ -31,7 +31,7 @@ - + {{ i18n "none" }} [[ key ]] diff --git a/web/html/xui/form/protocol/vless.html b/web/html/xui/form/protocol/vless.html index 029f2c9e..68c719dd 100644 --- a/web/html/xui/form/protocol/vless.html +++ b/web/html/xui/form/protocol/vless.html @@ -1,7 +1,7 @@ {{define "form/vless"}} - + @@ -31,7 +31,7 @@ - + {{ i18n "none" }} [[ key ]] diff --git a/web/html/xui/form/protocol/vmess.html b/web/html/xui/form/protocol/vmess.html index ad2b3960..d19e5cb7 100644 --- a/web/html/xui/form/protocol/vmess.html +++ b/web/html/xui/form/protocol/vmess.html @@ -1,7 +1,7 @@ {{define "form/vmess"}} - + diff --git a/web/html/xui/form/tls_settings.html b/web/html/xui/form/tls_settings.html index f954b76b..dcef3990 100644 --- a/web/html/xui/form/tls_settings.html +++ b/web/html/xui/form/tls_settings.html @@ -17,7 +17,7 @@ - + XTLS @@ -27,14 +27,14 @@ - + - - - + + + @@ -52,22 +52,22 @@ [[ key ]] - - + + + + + None [[ key ]] - - - [[ key ]] - + @@ -93,33 +93,79 @@ + + + + + + + + + [[ key ]] + + + + + + + + {{ i18n "pages.inbounds.certificatePath" }} + {{ i18n "pages.inbounds.certificateContent" }} + + + + + + + - + - + - + [[ key ]] + + + - + - - + + - - - - - - - + + + + + + + + + Get New Key + {{end}} \ No newline at end of file diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html index 049d529e..4e8c7dae 100644 --- a/web/html/xui/inbound_info_modal.html +++ b/web/html/xui/inbound_info_modal.html @@ -49,10 +49,14 @@ tls: {{ i18n "enabled" }}
tls {{ i18n "domainName" }}: [[ inbound.serverName ? inbound.serverName : '' ]] - + xtls: {{ i18n "enabled" }}
xtls {{ i18n "domainName" }}: [[ inbound.serverName ? inbound.serverName : '' ]] + + reality: {{ i18n "enabled" }}
+ reality {{ i18n "domainName" }}: [[ inbound.serverName ? inbound.serverName : '' ]] + tls: {{ i18n "disabled" }} diff --git a/web/html/xui/inbound_modal.html b/web/html/xui/inbound_modal.html index 6b140abc..98cb188f 100644 --- a/web/html/xui/inbound_modal.html +++ b/web/html/xui/inbound_modal.html @@ -43,6 +43,14 @@ loading(loading) { inModal.confirmLoading = loading; }, + getClients(protocol, clientSettings) { + switch(protocol){ + case Protocols.VMESS: return clientSettings.vmesses; + case Protocols.VLESS: return clientSettings.vlesses; + case Protocols.TROJAN: return clientSettings.trojans; + default: return null; + } + }, }; const protocols = { @@ -62,6 +70,7 @@ inModal: inModal, Protocols: protocols, SSMethods: SSMethods, + delayedStart: false, get inbound() { return inModal.inbound; }, @@ -70,36 +79,40 @@ }, get isEdit() { return inModal.isEdit; - } + }, + get client() { + return inModal.getClients(this.inbound.protocol, this.inbound.settings)[0]; + }, + get delayedExpireDays() { + return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0; + }, + set delayedExpireDays(days){ + this.client.expiryTime = -86400000 * days; + }, }, methods: { - streamNetworkChange(oldValue) { - if (oldValue === 'kcp') { - this.inModal.inbound.tls = false; + streamNetworkChange() { + if (!inModal.inbound.canSetTls()) { + this.inModal.inbound.stream.security = 'none'; } - }, - addClient(protocol, clients) { - switch (protocol) { - case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess()); - case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS()); - case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan()); - default: return null; + if (!inModal.inbound.canEnableReality()) { + this.inModal.inbound.reality = false; } }, - removeClient(index, clients) { - clients.splice(index, 1); - }, - isExpiry(index) { - return this.inbound.isExpiry(index) - }, - isClientEnable(email) { - clientStats = this.dbInbound.clientStats ? this.dbInbound.clientStats.find(stats => stats.email === email) : null - return clientStats ? clientStats['enable'] : true - }, setDefaultCertData(){ inModal.inbound.stream.tls.certs[0].certFile = app.defaultCert; inModal.inbound.stream.tls.certs[0].keyFile = app.defaultKey; }, + async getNewX25519Cert(){ + inModal.loading(true); + const msg = await HttpUtil.post('/server/getNewX25519Cert'); + inModal.loading(false); + if (!msg.success) { + return; + } + inModal.inbound.stream.reality.privateKey = msg.obj.privateKey; + inModal.inbound.stream.reality.settings.publicKey = msg.obj.publicKey; + }, getNewEmail(client) { var chars = 'abcdefghijklmnopqrstuvwxyz1234567890'; var string = ''; diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html index b962efcb..15782b1d 100644 --- a/web/html/xui/inbounds.html +++ b/web/html/xui/inbounds.html @@ -133,26 +133,26 @@