From 08d30a44559bc41bccab7ea8466f086811a36c66 Mon Sep 17 00:00:00 2001 From: Niduka Akalanka <97357554+NidukaAkalanka@users.noreply.github.com> Date: Sun, 20 Nov 2022 20:12:05 +0530 Subject: [PATCH] Update to 0.2.1 --- web/assets/js/langs.js | 79 ++ web/assets/js/model/models.js | 4 +- web/assets/js/model/xray.js | 447 ++++++----- web/assets/js/util/common.js | 8 +- web/controller/api.go | 48 ++ web/controller/base.go | 12 +- web/controller/inbound.go | 58 +- web/controller/index.go | 12 +- web/controller/server.go | 7 +- web/controller/setting.go | 16 +- web/controller/util.go | 6 +- web/controller/xui.go | 6 +- web/entity/entity.go | 1 - web/html/common/head.html | 2 +- web/html/common/js.html | 1 + web/html/common/prompt_modal.html | 6 +- web/html/common/qrcode_modal.html | 65 +- web/html/login.html | 152 ++-- web/html/xui/common_sider.html | 23 +- web/html/xui/component/inbound_info.html | 100 +-- web/html/xui/component/setting.html | 5 +- web/html/xui/form/inbound.html | 18 +- web/html/xui/form/protocol/dokodemo.html | 6 +- web/html/xui/form/protocol/http.html | 4 +- web/html/xui/form/protocol/shadowsocks.html | 14 +- web/html/xui/form/protocol/socks.html | 14 +- web/html/xui/form/protocol/trojan.html | 34 +- web/html/xui/form/protocol/vless.html | 154 +++- web/html/xui/form/protocol/vmess.html | 145 +++- web/html/xui/form/sniffing.html | 18 +- web/html/xui/form/stream/stream_http.html | 4 +- web/html/xui/form/stream/stream_kcp.html | 4 +- web/html/xui/form/stream/stream_quic.html | 6 +- web/html/xui/form/stream/stream_settings.html | 2 +- web/html/xui/form/stream/stream_tcp.html | 50 +- web/html/xui/form/stream/stream_ws.html | 20 +- web/html/xui/form/tls_settings.html | 38 +- web/html/xui/inbound_info_modal.html | 7 +- web/html/xui/inbound_modal.html | 104 ++- web/html/xui/inbounds.html | 691 +++++++++--------- web/html/xui/index.html | 580 ++++++++------- web/html/xui/setting.html | 317 ++++---- web/job/check_clinet_ip_job.go | 351 +++++++++ web/job/check_inbound_job.go | 10 +- web/job/stats_notify_job.go | 182 ++--- web/job/xray_traffic_job.go | 10 +- web/service/config.json | 55 +- web/service/inbound.go | 202 +++-- web/service/server.go | 19 +- web/service/setting.go | 2 +- web/service/telegram.go | 30 +- web/service/xray.go | 44 +- web/session/session.go | 3 +- web/translation/translate.en_US.toml | 201 ++++- web/translation/translate.pr.toml | 189 +++++ web/web.go | 75 +- 56 files changed, 3005 insertions(+), 1656 deletions(-) create mode 100644 web/assets/js/langs.js create mode 100644 web/controller/api.go create mode 100644 web/job/check_clinet_ip_job.go create mode 100644 web/translation/translate.pr.toml diff --git a/web/assets/js/langs.js b/web/assets/js/langs.js new file mode 100644 index 0000000..7601854 --- /dev/null +++ b/web/assets/js/langs.js @@ -0,0 +1,79 @@ +supportLangs = [ + { + name : "English", + value : "en-US", + icon : "🇺🇸" + }, + { + name : "Perisan", + value : "pr", + icon : "pr" + }, +] + +function getLang(){ + let lang = getCookie('lang') + + if (! lang){ + if (window.navigator){ + lang = window.navigator.language || window.navigator.userLanguage; + + if (isSupportLang(lang)){ + setCookie('lang' , lang , 150) + }else{ + setCookie('lang' , 'en-US' , 150) + window.location.reload(); + } + }else{ + setCookie('lang' , 'en-US' , 150) + window.location.reload(); + } + } + + return lang; +} + +function setLang(lang){ + + if (!isSupportLang(lang)){ + lang = 'en-US'; + } + + setCookie('lang' , lang , 150) + window.location.reload(); +} + +function isSupportLang(lang){ + for (l of supportLangs){ + if (l.value === lang){ + return true; + } + } + + return false; +} + + + +function getCookie(cname) { + let name = cname + "="; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(';'); + for(let i = 0; i { let values = v2Headers[key]; - if (typeof (values) === 'string') { + if (typeof(values) === 'string') { newHeaders.push({ name: key, value: values }); } else { for (let i = 0; i < values.length; ++i) { @@ -90,7 +87,7 @@ class XrayCommonClass { return newHeaders; } - static toV2Headers(headers, arr = true) { + static toV2Headers(headers, arr=true) { let v2Headers = {}; for (let i = 0; i < headers.length; ++i) { let name = headers[i].name; @@ -113,11 +110,11 @@ class XrayCommonClass { } class TcpStreamSettings extends XrayCommonClass { - constructor(acceptProxyProtocol = false, - type = 'none', - request = new TcpStreamSettings.TcpRequest(), - response = new TcpStreamSettings.TcpResponse(), - ) { + constructor(acceptProxyProtocol=false, + type='none', + request=new TcpStreamSettings.TcpRequest(), + response=new TcpStreamSettings.TcpResponse(), + ) { super(); this.acceptProxyProtocol = acceptProxyProtocol; this.type = type; @@ -125,7 +122,7 @@ class TcpStreamSettings extends XrayCommonClass { this.response = response; } - static fromJson(json = {}) { + static fromJson(json={}) { let header = json.header; if (!header) { header = {}; @@ -150,10 +147,10 @@ class TcpStreamSettings extends XrayCommonClass { } TcpStreamSettings.TcpRequest = class extends XrayCommonClass { - constructor(version = '1.1', - method = 'GET', - path = ['/'], - headers = [], + constructor(version='1.1', + method='GET', + path=['/'], + headers=[], ) { super(); this.version = version; @@ -187,7 +184,7 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass { this.headers.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { return new TcpStreamSettings.TcpRequest( json.version, json.method, @@ -206,10 +203,10 @@ TcpStreamSettings.TcpRequest = class extends XrayCommonClass { }; TcpStreamSettings.TcpResponse = class extends XrayCommonClass { - constructor(version = '1.1', - status = '200', - reason = 'OK', - headers = [], + constructor(version='1.1', + status='200', + reason='OK', + headers=[], ) { super(); this.version = version; @@ -226,7 +223,7 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass { this.headers.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { return new TcpStreamSettings.TcpResponse( json.version, json.status, @@ -246,15 +243,15 @@ TcpStreamSettings.TcpResponse = class extends XrayCommonClass { }; class KcpStreamSettings extends XrayCommonClass { - constructor(mtu = 1350, tti = 20, - uplinkCapacity = 5, - downlinkCapacity = 20, - congestion = false, - readBufferSize = 2, - writeBufferSize = 2, - type = 'none', - seed = RandomUtil.randomSeq(32), - ) { + constructor(mtu=1350, tti=20, + uplinkCapacity=5, + downlinkCapacity=20, + congestion=false, + readBufferSize=2, + writeBufferSize=2, + type='none', + seed=RandomUtil.randomSeq(10), + ) { super(); this.mtu = mtu; this.tti = tti; @@ -267,7 +264,7 @@ class KcpStreamSettings extends XrayCommonClass { this.seed = seed; } - static fromJson(json = {}) { + static fromJson(json={}) { return new KcpStreamSettings( json.mtu, json.tti, @@ -299,7 +296,7 @@ class KcpStreamSettings extends XrayCommonClass { } class WsStreamSettings extends XrayCommonClass { - constructor(acceptProxyProtocol = false, path = '/', headers = []) { + constructor(acceptProxyProtocol=false, path='/', headers=[]) { super(); this.acceptProxyProtocol = acceptProxyProtocol; this.path = path; @@ -323,7 +320,7 @@ class WsStreamSettings extends XrayCommonClass { this.headers.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { return new WsStreamSettings( json.acceptProxyProtocol, json.path, @@ -341,7 +338,7 @@ class WsStreamSettings extends XrayCommonClass { } class HttpStreamSettings extends XrayCommonClass { - constructor(path = '/', host = ['']) { + constructor(path='/', host=['']) { super(); this.path = path; this.host = host.length === 0 ? [''] : host; @@ -355,7 +352,7 @@ class HttpStreamSettings extends XrayCommonClass { this.host.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { return new HttpStreamSettings(json.path, json.host); } @@ -374,15 +371,15 @@ class HttpStreamSettings extends XrayCommonClass { } class QuicStreamSettings extends XrayCommonClass { - constructor(security = VmessMethods.NONE, - key = '', type = 'none') { + constructor(security=VmessMethods.NONE, + key='', type='none') { super(); this.security = security; this.key = key; this.type = type; } - static fromJson(json = {}) { + static fromJson(json={}) { return new QuicStreamSettings( json.security, json.key, @@ -402,12 +399,12 @@ class QuicStreamSettings extends XrayCommonClass { } class GrpcStreamSettings extends XrayCommonClass { - constructor(serviceName = "") { + constructor(serviceName="") { super(); this.serviceName = serviceName; } - static fromJson(json = {}) { + static fromJson(json={}) { return new GrpcStreamSettings(json.serviceName); } @@ -419,8 +416,8 @@ class GrpcStreamSettings extends XrayCommonClass { } class TlsStreamSettings extends XrayCommonClass { - constructor(serverName = '', - certificates = [new TlsStreamSettings.Cert()], alpn = []) { + constructor(serverName='', + certificates=[new TlsStreamSettings.Cert()], alpn=[]) { super(); this.server = serverName; this.certs = certificates; @@ -435,11 +432,12 @@ class TlsStreamSettings extends XrayCommonClass { this.certs.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { let certs; if (!ObjectUtil.isEmpty(json.certificates)) { certs = json.certificates.map(cert => TlsStreamSettings.Cert.fromJson(cert)); } + return new TlsStreamSettings( json.serverName, certs, @@ -457,7 +455,7 @@ class TlsStreamSettings extends XrayCommonClass { } TlsStreamSettings.Cert = class extends XrayCommonClass { - constructor(useFile = true, certificateFile = '', keyFile = '', certificate = '', key = '') { + constructor(useFile=true, certificateFile='', keyFile='', certificate='', key='') { super(); this.useFile = useFile; this.certFile = certificateFile; @@ -466,7 +464,7 @@ TlsStreamSettings.Cert = class extends XrayCommonClass { this.key = key instanceof Array ? key.join('\n') : key; } - static fromJson(json = {}) { + static fromJson(json={}) { if ('certificateFile' in json && 'keyFile' in json) { return new TlsStreamSettings.Cert( true, @@ -498,16 +496,16 @@ TlsStreamSettings.Cert = class extends XrayCommonClass { }; class StreamSettings extends XrayCommonClass { - constructor(network = 'tcp', - security = 'none', - tlsSettings = new TlsStreamSettings(), - tcpSettings = new TcpStreamSettings(), - kcpSettings = new KcpStreamSettings(), - wsSettings = new WsStreamSettings(), - httpSettings = new HttpStreamSettings(), - quicSettings = new QuicStreamSettings(), - grpcSettings = new GrpcStreamSettings(), - ) { + constructor(network='tcp', + security='none', + tlsSettings=new TlsStreamSettings(), + tcpSettings=new TcpStreamSettings(), + kcpSettings=new KcpStreamSettings(), + wsSettings=new WsStreamSettings(), + httpSettings=new HttpStreamSettings(), + quicSettings=new QuicStreamSettings(), + grpcSettings=new GrpcStreamSettings(), + ) { super(); this.network = network; this.security = security; @@ -544,7 +542,7 @@ class StreamSettings extends XrayCommonClass { } } - static fromJson(json = {}) { + static fromJson(json={}) { let tls; if (json.security === "xtls") { tls = TlsStreamSettings.fromJson(json.xtlsSettings); @@ -582,13 +580,13 @@ class StreamSettings extends XrayCommonClass { } class Sniffing extends XrayCommonClass { - constructor(enabled = true, destOverride = ['http', 'tls']) { + constructor(enabled=true, destOverride=['http', 'tls']) { super(); this.enabled = enabled; this.destOverride = destOverride; } - static fromJson(json = {}) { + static fromJson(json={}) { let destOverride = ObjectUtil.clone(json.destOverride); if (!ObjectUtil.isEmpty(destOverride) && !ObjectUtil.isArrEmpty(destOverride)) { if (ObjectUtil.isEmpty(destOverride[0])) { @@ -603,14 +601,15 @@ class Sniffing extends XrayCommonClass { } class Inbound extends XrayCommonClass { - constructor(port = RandomUtil.randomIntRange(10000, 60000), - listen = '', - protocol = Protocols.VMESS, - settings = null, - streamSettings = new StreamSettings(), - tag = '', - sniffing = new Sniffing(), - ) { + constructor(port=RandomUtil.randomIntRange(10000, 60000), + listen='', + protocol=Protocols.VMESS, + settings=null, + streamSettings=new StreamSettings(), + tag='', + sniffing=new Sniffing(), + clientStats='', + ) { super(); this.port = port; this.listen = listen; @@ -619,6 +618,10 @@ class Inbound extends XrayCommonClass { this.stream = streamSettings; this.tag = tag; this.sniffing = sniffing; + this.clientStats = clientStats; + } + getClientStats() { + return this.clientStats; } get protocol() { @@ -641,11 +644,7 @@ class Inbound extends XrayCommonClass { if (isTls) { this.stream.security = 'tls'; } else { - if (this.protocol === Protocols.TROJAN) { - this.xtls = true; - } else { - this.stream.security = 'none'; - } + this.stream.security = 'none'; } } @@ -657,11 +656,7 @@ class Inbound extends XrayCommonClass { if (isXTls) { this.stream.security = 'xtls'; } else { - if (this.protocol === Protocols.TROJAN) { - this.tls = true; - } else { - this.stream.security = 'none'; - } + this.stream.security = 'none'; } } @@ -820,6 +815,21 @@ class Inbound extends XrayCommonClass { return this.stream.grpc.serviceName; } + isExpiry(index) { + switch (this.protocol) { + case Protocols.VMESS: + if(this.settings.vmesses[index]._expiryTime != null) + return this.settings.vmesses[index]._expiryTime < new Date().getTime(); + return false + case Protocols.VLESS: + if(this.settings.vlesses[index]._expiryTime != null) + return this.settings.vlesses[index]._expiryTime < new Date().getTime(); + return false + default: + return false; + } + } + canEnableTls() { switch (this.protocol) { case Protocols.VMESS: @@ -863,7 +873,6 @@ class Inbound extends XrayCommonClass { case Protocols.VMESS: case Protocols.VLESS: case Protocols.SHADOWSOCKS: - case Protocols.TROJAN: return true; default: return false; @@ -892,7 +901,7 @@ class Inbound extends XrayCommonClass { this.sniffing = new Sniffing(); } - genVmessLink(address = '', remark = '') { + genVmessLink(address='', remark='', clientIndex=0) { if (this.protocol !== Protocols.VMESS) { return ''; } @@ -945,8 +954,8 @@ class Inbound extends XrayCommonClass { ps: remark, add: address, port: this.port, - id: this.settings.vmesses[0].id, - aid: this.settings.vmesses[0].alterId, + id: this.settings.vmesses[clientIndex].id, + aid: this.settings.vmesses[clientIndex].alterId, net: network, type: type, host: host, @@ -956,9 +965,9 @@ class Inbound extends XrayCommonClass { return 'vmess://' + base64(JSON.stringify(obj, null, 2)); } - genVLESSLink(address = '', remark = '') { + genVLESSLink(address = '', remark='', clientIndex=0) { const settings = this.settings; - const uuid = settings.vlesses[0].id; + const uuid = settings.vlesses[clientIndex].id; const port = this.port; const type = this.stream.network; const params = new Map(); @@ -1020,7 +1029,7 @@ class Inbound extends XrayCommonClass { } if (this.xtls) { - params.set("flow", this.settings.vlesses[0].flow); + params.set("flow", this.settings.vlesses[clientIndex].flow); } const link = `vless://${uuid}@${address}:${port}`; @@ -1032,104 +1041,32 @@ class Inbound extends XrayCommonClass { return url.toString(); } - genSSLink(address = '', remark = '') { + genSSLink(address='', remark='') { let settings = this.settings; const server = this.stream.tls.server; if (!ObjectUtil.isEmpty(server)) { address = server; } - if (settings.method == SSMethods.SS_2022_BLAKE3_AES_128_GCM || settings.method == SSMethods.SS_2022_BLAKE3_AES_256_GCM || settings.method == SSMethods.SS_2022_BLAKE3_CHACHA20_POLY1305) { - return `ss://${settings.method}:${settings.password}@${address}:${this.port}#${encodeURIComponent(remark)}`; - } else { - return 'ss://' + safeBase64(settings.method + ':' + settings.password + '@' + address + ':' + this.port) - + '#' + encodeURIComponent(remark); - } + return 'ss://' + safeBase64(settings.method + ':' + settings.password + '@' + address + ':' + this.port) + + '#' + encodeURIComponent(remark); } - genTrojanLink(address = '', remark = '') { + genTrojanLink(address='', remark='') { let settings = this.settings; - const port = this.port; - const type = this.stream.network; - const params = new Map(); - params.set("type", this.stream.network); - if (this.xtls) { - params.set("security", "xtls"); - } else { - params.set("security", this.stream.security); - } - switch (type) { - case "tcp": - const tcp = this.stream.tcp; - if (tcp.type === 'http') { - const request = tcp.request; - params.set("path", request.path.join(',')); - const index = request.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = request.headers[index].value; - params.set("host", host); - } - } - break; - case "kcp": - const kcp = this.stream.kcp; - params.set("headerType", kcp.type); - params.set("seed", kcp.seed); - break; - case "ws": - const ws = this.stream.ws; - params.set("path", ws.path); - const index = ws.headers.findIndex(header => header.name.toLowerCase() === 'host'); - if (index >= 0) { - const host = ws.headers[index].value; - params.set("host", host); - } - break; - case "http": - const http = this.stream.http; - params.set("path", http.path); - params.set("host", http.host); - break; - case "quic": - const quic = this.stream.quic; - params.set("quicSecurity", quic.security); - params.set("key", quic.key); - params.set("headerType", quic.type); - break; - case "grpc": - const grpc = this.stream.grpc; - params.set("serviceName", grpc.serviceName); - break; - } - - if (this.stream.security === 'tls') { - if (!ObjectUtil.isEmpty(this.stream.tls.server)) { - address = this.stream.tls.server; - params.set("sni", address); - } - } - if (this.xtls) { - params.set("flow", this.settings.clients[0].flow); - } - const link = `trojan://${settings.clients[0].password}@${address}:${port}`; - const url = new URL(link); - for (const [key, value] of params) { - url.searchParams.set(key, value) - } - url.hash = encodeURIComponent(remark); - return url.toString(); + return `trojan://${settings.clients[0].password}@${address}:${this.port}#${encodeURIComponent(remark)}`; } - genLink(address = '', remark = '') { + genLink(address='', remark='', clientIndex=0) { switch (this.protocol) { - case Protocols.VMESS: return this.genVmessLink(address, remark); - case Protocols.VLESS: return this.genVLESSLink(address, remark); + case Protocols.VMESS: return this.genVmessLink(address, remark, clientIndex); + case Protocols.VLESS: return this.genVLESSLink(address, remark, clientIndex); case Protocols.SHADOWSOCKS: return this.genSSLink(address, remark); case Protocols.TROJAN: return this.genTrojanLink(address, remark); default: return ''; } } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound( json.port, json.listen, @@ -1138,6 +1075,7 @@ class Inbound extends XrayCommonClass { StreamSettings.fromJson(json.streamSettings), json.tag, Sniffing.fromJson(json.sniffing), + json.clientStats ) } @@ -1154,6 +1092,7 @@ class Inbound extends XrayCommonClass { streamSettings: streamSettings, tag: this.tag, sniffing: this.sniffing.toJson(), + clientStats: this.clientStats }; } } @@ -1199,22 +1138,32 @@ Inbound.Settings = class extends XrayCommonClass { Inbound.VmessSettings = class extends Inbound.Settings { constructor(protocol, - vmesses = [new Inbound.VmessSettings.Vmess()], - disableInsecureEncryption = false) { + vmesses=[new Inbound.VmessSettings.Vmess()], + disableInsecureEncryption=false) { super(protocol); this.vmesses = vmesses; this.disableInsecure = disableInsecureEncryption; } - addVmess() { - this.vmesses.push(new Inbound.VmessSettings.Vmess()); + indexOfVmessById(id) { + return this.vmesses.findIndex(vmess => vmess.id === id); } - delVmess(index) { - this.vmesses.splice(index, 1); + addVmess(vmess) { + if (this.indexOfVmessById(vmess.id) >= 0) { + return false; + } + this.vmesses.push(vmess); } - static fromJson(json = {}) { + delVmess(vmess) { + const i = this.indexOfVmessById(vmess.id); + if (i >= 0) { + this.vmesses.splice(i, 1); + } + } + + static fromJson(json={}) { return new Inbound.VmessSettings( Protocols.VMESS, json.clients.map(client => Inbound.VmessSettings.Vmess.fromJson(client)), @@ -1230,39 +1179,62 @@ Inbound.VmessSettings = class extends Inbound.Settings { } }; Inbound.VmessSettings.Vmess = class extends XrayCommonClass { - constructor(id = RandomUtil.randomUUID(), alterId = 0) { + constructor(id=RandomUtil.randomUUID(), alterId=0, email='', limitIp=0, totalGB=0, expiryTime='') { super(); this.id = id; this.alterId = alterId; + this.email = email; + this.limitIp = limitIp; + this.totalGB = totalGB; + this.expiryTime = expiryTime; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.VmessSettings.Vmess( json.id, json.alterId, + json.email, + json.limitIp, + json.totalGB, + json.expiryTime, + ); } + get _expiryTime() { + if (this.expiryTime === 0 || this.expiryTime === "") { + return null; + } + return moment(this.expiryTime); + } + + set _expiryTime(t) { + if (t == null || t === "") { + this.expiryTime = 0; + } else { + this.expiryTime = t.valueOf(); + } + } + get _totalGB() { + return toFixed(this.totalGB / ONE_GB, 2); + } + + set _totalGB(gb) { + this.totalGB = toFixed(gb * ONE_GB, 0); + } + }; Inbound.VLESSSettings = class extends Inbound.Settings { constructor(protocol, - vlesses = [new Inbound.VLESSSettings.VLESS()], - decryption = 'none', - fallbacks = [],) { + vlesses=[new Inbound.VLESSSettings.VLESS()], + decryption='none', + fallbacks=[],) { super(protocol); this.vlesses = vlesses; this.decryption = decryption; this.fallbacks = fallbacks; } - - addVLESS() { - this.vlesses.push(new Inbound.VLESSSettings.VLESS()); - } - delVLESS(index) { - this.vlesses.splice(index, 1); - } - addFallback() { this.fallbacks.push(new Inbound.VLESSSettings.Fallback()); } @@ -1271,7 +1243,7 @@ Inbound.VLESSSettings = class extends Inbound.Settings { this.fallbacks.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.VLESSSettings( Protocols.VLESS, json.clients.map(client => Inbound.VLESSSettings.VLESS.fromJson(client)), @@ -1287,24 +1259,57 @@ Inbound.VLESSSettings = class extends Inbound.Settings { fallbacks: Inbound.VLESSSettings.toJsonArray(this.fallbacks), }; } + }; Inbound.VLESSSettings.VLESS = class extends XrayCommonClass { - constructor(id = RandomUtil.randomUUID(), flow = FLOW_CONTROL.DIRECT) { + constructor(id=RandomUtil.randomUUID(), flow=FLOW_CONTROL.DIRECT, email='', limitIp=0, totalGB=0, expiryTime='') { super(); this.id = id; this.flow = flow; + this.email = email; + this.limitIp = limitIp; + this.totalGB = totalGB; + this.expiryTime = expiryTime; + } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.VLESSSettings.VLESS( json.id, json.flow, + json.email, + json.limitIp, + json.totalGB, + json.expiryTime, + ); } + + get _expiryTime() { + if (this.expiryTime === 0 || this.expiryTime === "") { + return null; + } + return moment(this.expiryTime); + } + + set _expiryTime(t) { + if (t == null || t === "") { + this.expiryTime = 0; + } else { + this.expiryTime = t.valueOf(); + } + } + get _totalGB() { + return toFixed(this.totalGB / ONE_GB, 2); + } + + set _totalGB(gb) { + this.totalGB = toFixed(gb * ONE_GB, 0); + } }; Inbound.VLESSSettings.Fallback = class extends XrayCommonClass { - constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { + constructor(name="", alpn='', path='', dest='', xver=0) { super(); this.name = name; this.alpn = alpn; @@ -1327,7 +1332,7 @@ Inbound.VLESSSettings.Fallback = class extends XrayCommonClass { } } - static fromJson(json = []) { + static fromJson(json=[]) { const fallbacks = []; for (let fallback of json) { fallbacks.push(new Inbound.VLESSSettings.Fallback( @@ -1344,21 +1349,13 @@ Inbound.VLESSSettings.Fallback = class extends XrayCommonClass { Inbound.TrojanSettings = class extends Inbound.Settings { constructor(protocol, - clients = [new Inbound.TrojanSettings.Client()], - fallbacks = [],) { + clients=[new Inbound.TrojanSettings.Client()], + fallbacks=[],) { super(protocol); this.clients = clients; this.fallbacks = fallbacks; } - addTrojan() { - this.clients.push(new Inbound.TrojanSettings.Client()); - } - - delTrojan(index) { - this.clients.splice(index, 1); - } - addTrojanFallback() { this.fallbacks.push(new Inbound.TrojanSettings.Fallback()); } @@ -1374,7 +1371,7 @@ Inbound.TrojanSettings = class extends Inbound.Settings { }; } - static fromJson(json = {}) { + static fromJson(json={}) { const clients = []; for (const c of json.clients) { clients.push(Inbound.TrojanSettings.Client.fromJson(c)); @@ -1382,11 +1379,11 @@ Inbound.TrojanSettings = class extends Inbound.Settings { return new Inbound.TrojanSettings( Protocols.TROJAN, clients, - Inbound.TrojanSettings.Fallback.fromJson(json.fallbacks)); + Inbound.TrojanSettings.Fallback.fromJson(json.fallbacks),); } }; Inbound.TrojanSettings.Client = class extends XrayCommonClass { - constructor(password = RandomUtil.randomSeq(32), flow = FLOW_CONTROL.DIRECT) { + constructor(password=RandomUtil.randomSeq(10), flow=FLOW_CONTROL.DIRECT) { super(); this.password = password; this.flow = flow; @@ -1399,7 +1396,7 @@ Inbound.TrojanSettings.Client = class extends XrayCommonClass { }; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.TrojanSettings.Client( json.password, json.flow, @@ -1409,7 +1406,7 @@ Inbound.TrojanSettings.Client = class extends XrayCommonClass { }; Inbound.TrojanSettings.Fallback = class extends XrayCommonClass { - constructor(name = "", alpn = '', path = '', dest = '', xver = 0) { + constructor(name="", alpn='', path='', dest='', xver=0) { super(); this.name = name; this.alpn = alpn; @@ -1432,7 +1429,7 @@ Inbound.TrojanSettings.Fallback = class extends XrayCommonClass { } } - static fromJson(json = []) { + static fromJson(json=[]) { const fallbacks = []; for (let fallback of json) { fallbacks.push(new Inbound.TrojanSettings.Fallback( @@ -1449,9 +1446,9 @@ Inbound.TrojanSettings.Fallback = class extends XrayCommonClass { Inbound.ShadowsocksSettings = class extends Inbound.Settings { constructor(protocol, - method = SSMethods.AES_256_GCM, - password = btoa(RandomUtil.randomSeq(32)), - network = 'tcp,udp' + method=SSMethods.AES_256_GCM, + password=RandomUtil.randomSeq(10), + network='tcp,udp' ) { super(protocol); this.method = method; @@ -1459,7 +1456,7 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings { this.network = network; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.ShadowsocksSettings( Protocols.SHADOWSOCKS, json.method, @@ -1478,14 +1475,14 @@ Inbound.ShadowsocksSettings = class extends Inbound.Settings { }; Inbound.DokodemoSettings = class extends Inbound.Settings { - constructor(protocol, address, port, network = 'tcp,udp') { + constructor(protocol, address, port, network='tcp,udp') { super(protocol); this.address = address; this.port = port; this.network = network; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.DokodemoSettings( Protocols.DOKODEMO, json.address, @@ -1504,12 +1501,12 @@ Inbound.DokodemoSettings = class extends Inbound.Settings { }; Inbound.MtprotoSettings = class extends Inbound.Settings { - constructor(protocol, users = [new Inbound.MtprotoSettings.MtUser()]) { + constructor(protocol, users=[new Inbound.MtprotoSettings.MtUser()]) { super(protocol); this.users = users; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.MtprotoSettings( Protocols.MTPROTO, json.users.map(user => Inbound.MtprotoSettings.MtUser.fromJson(user)), @@ -1523,18 +1520,18 @@ Inbound.MtprotoSettings = class extends Inbound.Settings { } }; Inbound.MtprotoSettings.MtUser = class extends XrayCommonClass { - constructor(secret = RandomUtil.randomMTSecret()) { + constructor(secret=RandomUtil.randomMTSecret()) { super(); this.secret = secret; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.MtprotoSettings.MtUser(json.secret); } }; Inbound.SocksSettings = class extends Inbound.Settings { - constructor(protocol, auth = 'password', accounts = [new Inbound.SocksSettings.SocksAccount()], udp = false, ip = '127.0.0.1') { + constructor(protocol, auth='password', accounts=[new Inbound.SocksSettings.SocksAccount()], udp=false, ip='127.0.0.1') { super(protocol); this.auth = auth; this.accounts = accounts; @@ -1550,7 +1547,7 @@ Inbound.SocksSettings = class extends Inbound.Settings { this.accounts.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { let accounts; if (json.auth === 'password') { accounts = json.accounts.map( @@ -1576,19 +1573,19 @@ Inbound.SocksSettings = class extends Inbound.Settings { } }; Inbound.SocksSettings.SocksAccount = class extends XrayCommonClass { - constructor(user = RandomUtil.randomSeq(32), pass = RandomUtil.randomSeq(32)) { + constructor(user=RandomUtil.randomSeq(10), pass=RandomUtil.randomSeq(10)) { super(); this.user = user; this.pass = pass; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.SocksSettings.SocksAccount(json.user, json.pass); } }; Inbound.HttpSettings = class extends Inbound.Settings { - constructor(protocol, accounts = [new Inbound.HttpSettings.HttpAccount()]) { + constructor(protocol, accounts=[new Inbound.HttpSettings.HttpAccount()]) { super(protocol); this.accounts = accounts; } @@ -1601,7 +1598,7 @@ Inbound.HttpSettings = class extends Inbound.Settings { this.accounts.splice(index, 1); } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.HttpSettings( Protocols.HTTP, json.accounts.map(account => Inbound.HttpSettings.HttpAccount.fromJson(account)), @@ -1616,13 +1613,13 @@ Inbound.HttpSettings = class extends Inbound.Settings { }; Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { - constructor(user = RandomUtil.randomSeq(32), pass = RandomUtil.randomSeq(32)) { + constructor(user=RandomUtil.randomSeq(10), pass=RandomUtil.randomSeq(10)) { super(); this.user = user; this.pass = pass; } - static fromJson(json = {}) { + static fromJson(json={}) { return new Inbound.HttpSettings.HttpAccount(json.user, json.pass); } -}; \ No newline at end of file +}; diff --git a/web/assets/js/util/common.js b/web/assets/js/util/common.js index 39c964d..7225e4b 100644 --- a/web/assets/js/util/common.js +++ b/web/assets/js/util/common.js @@ -33,13 +33,13 @@ function safeBase64(str) { function formatSecond(second) { if (second < 60) { - return second.toFixed(0) + ' 秒'; + return second.toFixed(0) + ' s'; } else if (second < 3600) { - return (second / 60).toFixed(0) + ' 分钟'; + return (second / 60).toFixed(0) + ' m'; } else if (second < 3600 * 24) { - return (second / 3600).toFixed(0) + ' 小时'; + return (second / 3600).toFixed(0) + ' h'; } else { - return (second / 3600 / 24).toFixed(0) + ' 天'; + return (second / 3600 / 24).toFixed(0) + ' d'; } } diff --git a/web/controller/api.go b/web/controller/api.go new file mode 100644 index 0000000..84ac9c2 --- /dev/null +++ b/web/controller/api.go @@ -0,0 +1,48 @@ +package controller + +import ( + "github.com/gin-gonic/gin" +) +type APIController struct { + BaseController + + inboundController *InboundController + settingController *SettingController +} + +func NewAPIController(g *gin.RouterGroup) *APIController { + a := &APIController{} + a.initRouter(g) + return a +} + +func (a *APIController) initRouter(g *gin.RouterGroup) { + g = g.Group("/xui/API/inbounds") + g.Use(a.checkLogin) + + g.GET("/", a.inbounds) + g.GET("/get/:id", a.inbound) + g.POST("/add", a.addInbound) + g.POST("/del/:id", a.delInbound) + g.POST("/update/:id", a.updateInbound) + + + a.inboundController = NewInboundController(g) +} + + +func (a *APIController) inbounds(c *gin.Context) { + a.inboundController.getInbounds(c) +} +func (a *APIController) inbound(c *gin.Context) { + a.inboundController.getInbound(c) +} +func (a *APIController) addInbound(c *gin.Context) { + a.inboundController.addInbound(c) +} +func (a *APIController) delInbound(c *gin.Context) { + a.inboundController.delInbound(c) +} +func (a *APIController) updateInbound(c *gin.Context) { + a.inboundController.updateInbound(c) +} diff --git a/web/controller/base.go b/web/controller/base.go index 5515607..f0cbd1a 100644 --- a/web/controller/base.go +++ b/web/controller/base.go @@ -12,7 +12,7 @@ type BaseController struct { func (a *BaseController) checkLogin(c *gin.Context) { if !session.IsLogin(c) { if isAjax(c) { - pureJsonMsg(c, false, "Session expired. Please log in again") + pureJsonMsg(c, false, I18n(c , "pages.login.loginAgain")) } else { c.Redirect(http.StatusTemporaryRedirect, c.GetString("base_path")) } @@ -21,3 +21,13 @@ func (a *BaseController) checkLogin(c *gin.Context) { c.Next() } } + + +func I18n(c *gin.Context , name string, data ...string) string{ + anyfunc, _ := c.Get("I18n") + i18n, _ := anyfunc.(func(key string, params ...string) (string, error)) + + message, _ := i18n(name) + + return message; +} diff --git a/web/controller/inbound.go b/web/controller/inbound.go index d28a527..0aeffec 100644 --- a/web/controller/inbound.go +++ b/web/controller/inbound.go @@ -30,6 +30,11 @@ func (a *InboundController) initRouter(g *gin.RouterGroup) { g.POST("/add", a.addInbound) g.POST("/del/:id", a.delInbound) g.POST("/update/:id", a.updateInbound) + + g.POST("/clientIps/:email", a.getClientIps) + g.POST("/clearClientIps/:email", a.clearClientIps) + + } func (a *InboundController) startTask() { @@ -49,25 +54,38 @@ func (a *InboundController) getInbounds(c *gin.Context) { user := session.GetLoginUser(c) inbounds, err := a.inboundService.GetInbounds(user.Id) if err != nil { - jsonMsg(c, "Obtain", err) + jsonMsg(c, I18n(c , "pages.inbounds.toasts.obtain"), err) return } jsonObj(c, inbounds, nil) } +func (a *InboundController) getInbound(c *gin.Context) { + id, err := strconv.Atoi(c.Param("id")) + if err != nil { + jsonMsg(c, I18n(c , "get"), err) + return + } + inbound, err := a.inboundService.GetInbound(id) + if err != nil { + jsonMsg(c, I18n(c , "pages.inbounds.toasts.obtain"), err) + return + } + jsonObj(c, inbound, nil) +} func (a *InboundController) addInbound(c *gin.Context) { inbound := &model.Inbound{} err := c.ShouldBind(inbound) if err != nil { - jsonMsg(c, "Add to", err) + jsonMsg(c, I18n(c , "pages.inbounds.addTo"), err) return } user := session.GetLoginUser(c) inbound.UserId = user.Id inbound.Enable = true inbound.Tag = fmt.Sprintf("inbound-%v", inbound.Port) - err = a.inboundService.AddInbound(inbound) - jsonMsg(c, "Add to", err) + inbound, err = a.inboundService.AddInbound(inbound) + jsonMsgObj(c, I18n(c , "pages.inbounds.addTo"), inbound, err) if err == nil { a.xrayService.SetToNeedRestart() } @@ -76,11 +94,11 @@ func (a *InboundController) addInbound(c *gin.Context) { func (a *InboundController) delInbound(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { - jsonMsg(c, "Delete", err) + jsonMsg(c, I18n(c , "delete"), err) return } err = a.inboundService.DelInbound(id) - jsonMsg(c, "Delete", err) + jsonMsgObj(c, I18n(c , "delete"), id, err) if err == nil { a.xrayService.SetToNeedRestart() } @@ -89,7 +107,7 @@ func (a *InboundController) delInbound(c *gin.Context) { func (a *InboundController) updateInbound(c *gin.Context) { id, err := strconv.Atoi(c.Param("id")) if err != nil { - jsonMsg(c, "Update", err) + jsonMsg(c, I18n(c , "pages.inbounds.revise"), err) return } inbound := &model.Inbound{ @@ -97,12 +115,32 @@ func (a *InboundController) updateInbound(c *gin.Context) { } err = c.ShouldBind(inbound) if err != nil { - jsonMsg(c, "Update", err) + jsonMsg(c, I18n(c , "pages.inbounds.revise"), err) return } - err = a.inboundService.UpdateInbound(inbound) - jsonMsg(c, "Update", err) + inbound, err = a.inboundService.UpdateInbound(inbound) + jsonMsgObj(c, I18n(c , "pages.inbounds.revise"), inbound, err) if err == nil { a.xrayService.SetToNeedRestart() } } +func (a *InboundController) getClientIps(c *gin.Context) { + email := c.Param("email") + + ips , err := a.inboundService.GetInboundClientIps(email) + if err != nil { + jsonObj(c, "No IP Record", nil) + return + } + jsonObj(c, ips, nil) +} +func (a *InboundController) clearClientIps(c *gin.Context) { + email := c.Param("email") + + err := a.inboundService.ClearClientIps(email) + if err != nil { + jsonMsg(c, "Revise", err) + return + } + jsonMsg(c, "Log Cleared", nil) +} \ No newline at end of file diff --git a/web/controller/index.go b/web/controller/index.go index 1819e25..e0be607 100644 --- a/web/controller/index.go +++ b/web/controller/index.go @@ -39,22 +39,22 @@ func (a *IndexController) index(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, "xui/") return } - html(c, "login.html", "X -UILog in", nil) + html(c, "login.html", "pages.login.title", nil) } func (a *IndexController) login(c *gin.Context) { var form LoginForm err := c.ShouldBind(&form) if err != nil { - pureJsonMsg(c, false, "Data format error") + pureJsonMsg(c, false, I18n(c , "pages.login.toasts.invalidFormData")) return } if form.Username == "" { - pureJsonMsg(c, false, "Please enter the username") + pureJsonMsg(c, false, I18n(c, "pages.login.toasts.emptyUsername")) return } if form.Password == "" { - pureJsonMsg(c, false, "Please enter the password") + pureJsonMsg(c, false, I18n(c , "pages.login.toasts.emptyPassword")) return } user := a.userService.CheckUser(form.Username, form.Password) @@ -62,7 +62,7 @@ func (a *IndexController) login(c *gin.Context) { if user == nil { job.NewStatsNotifyJob().UserLoginNotify(form.Username, getRemoteIp(c), timeStr, 0) logger.Infof("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password) - pureJsonMsg(c, false, "Wrong username or password!") + pureJsonMsg(c, false, I18n(c , "pages.login.toasts.wrongUsernameOrPassword")) return } else { logger.Infof("%s login success,Ip Address:%s\n", form.Username, getRemoteIp(c)) @@ -71,7 +71,7 @@ func (a *IndexController) login(c *gin.Context) { err = session.SetLoginUser(c, user) logger.Info("user", user.Id, "login success") - jsonMsg(c, "Log in", err) + jsonMsg(c, I18n(c , "pages.login.toasts.successLogin"), err) } func (a *IndexController) logout(c *gin.Context) { diff --git a/web/controller/server.go b/web/controller/server.go index 44f37f4..d59b340 100644 --- a/web/controller/server.go +++ b/web/controller/server.go @@ -1,11 +1,10 @@ package controller import ( + "github.com/gin-gonic/gin" "time" "x-ui/web/global" "x-ui/web/service" - - "github.com/gin-gonic/gin" ) type ServerController struct { @@ -69,7 +68,7 @@ func (a *ServerController) getXrayVersion(c *gin.Context) { versions, err := a.serverService.GetXrayVersions() if err != nil { - jsonMsg(c, "Get XRAY version", err) + jsonMsg(c, I18n(c , "getVersion"), err) return } @@ -82,5 +81,5 @@ func (a *ServerController) getXrayVersion(c *gin.Context) { func (a *ServerController) installXray(c *gin.Context) { version := c.Param("version") err := a.serverService.UpdateXray(version) - jsonMsg(c, "Install XRAY", err) + jsonMsg(c, I18n(c , "install") + " xray", err) } diff --git a/web/controller/setting.go b/web/controller/setting.go index 093aa6f..f500c76 100644 --- a/web/controller/setting.go +++ b/web/controller/setting.go @@ -40,7 +40,7 @@ func (a *SettingController) initRouter(g *gin.RouterGroup) { func (a *SettingController) getAllSetting(c *gin.Context) { allSetting, err := a.settingService.GetAllSetting() if err != nil { - jsonMsg(c, "Get settings", err) + jsonMsg(c, I18n(c , "pages.setting.toasts.getSetting"), err) return } jsonObj(c, allSetting, nil) @@ -50,27 +50,27 @@ func (a *SettingController) updateSetting(c *gin.Context) { allSetting := &entity.AllSetting{} err := c.ShouldBind(allSetting) if err != nil { - jsonMsg(c, "Modify settings", err) + jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err) return } err = a.settingService.UpdateAllSetting(allSetting) - jsonMsg(c, "Modify settings", err) + jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err) } func (a *SettingController) updateUser(c *gin.Context) { form := &updateUserForm{} err := c.ShouldBind(form) if err != nil { - jsonMsg(c, "Modify user", err) + jsonMsg(c, I18n(c , "pages.setting.toasts.modifySetting"), err) return } user := session.GetLoginUser(c) if user.Username != form.OldUsername || user.Password != form.OldPassword { - jsonMsg(c, "Modify user", errors.New("The original user name or original password is incorrect")) + jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.originalUserPassIncorrect"))) return } if form.NewUsername == "" || form.NewPassword == "" { - jsonMsg(c, "Modify user", errors.New("New username and new password cannot be empty")) + jsonMsg(c,I18n(c , "pages.setting.toasts.modifyUser"), errors.New(I18n(c , "pages.setting.toasts.userPassMustBeNotEmpty"))) return } err = a.userService.UpdateUser(user.Id, form.NewUsername, form.NewPassword) @@ -79,10 +79,10 @@ func (a *SettingController) updateUser(c *gin.Context) { user.Password = form.NewPassword session.SetLoginUser(c, user) } - jsonMsg(c, "Modify user", err) + jsonMsg(c, I18n(c , "pages.setting.toasts.modifyUser"), err) } func (a *SettingController) restartPanel(c *gin.Context) { err := a.panelService.RestartPanel(time.Second * 3) - jsonMsg(c, "Restart panel", err) + jsonMsg(c, I18n(c , "pages.setting.restartPanel"), err) } diff --git a/web/controller/util.go b/web/controller/util.go index c1a855c..2bd2fa5 100644 --- a/web/controller/util.go +++ b/web/controller/util.go @@ -46,12 +46,12 @@ func jsonMsgObj(c *gin.Context, msg string, obj interface{}, err error) { if err == nil { m.Success = true if msg != "" { - m.Msg = msg + "success" + m.Msg = msg + I18n(c , "success") } } else { m.Success = false - m.Msg = msg + "failed: " + err.Error() - logger.Warning(msg+"failed: ", err) + m.Msg = msg + I18n(c , "fail") + ": " + err.Error() + logger.Warning(msg + I18n(c , "fail") + ": ", err) } c.JSON(http.StatusOK, m) } diff --git a/web/controller/xui.go b/web/controller/xui.go index 45f926f..5832be8 100644 --- a/web/controller/xui.go +++ b/web/controller/xui.go @@ -30,13 +30,13 @@ func (a *XUIController) initRouter(g *gin.RouterGroup) { } func (a *XUIController) index(c *gin.Context) { - html(c, "index.html", "System Status", nil) + html(c, "index.html", "pages.index.title", nil) } func (a *XUIController) inbounds(c *gin.Context) { - html(c, "inbounds.html", "Inbound List", nil) + html(c, "inbounds.html", "pages.inbounds.title", nil) } func (a *XUIController) setting(c *gin.Context) { - html(c, "setting.html", "Settings", nil) + html(c, "setting.html", "pages.setting.title", nil) } diff --git a/web/entity/entity.go b/web/entity/entity.go index d71fd5d..f8572f6 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -60,7 +60,6 @@ func (s *AllSetting) CheckValid() error { } } - if !strings.HasPrefix(s.WebBasePath, "/") { s.WebBasePath = "/" + s.WebBasePath } diff --git a/web/html/common/head.html b/web/html/common/head.html index 819f436..f34ce62 100644 --- a/web/html/common/head.html +++ b/web/html/common/head.html @@ -12,6 +12,6 @@ display: none; } - {{.title}} + {{ i18n .title}} {{end}} \ No newline at end of file diff --git a/web/html/common/js.html b/web/html/common/js.html index b8ee57b..d400196 100644 --- a/web/html/common/js.html +++ b/web/html/common/js.html @@ -14,6 +14,7 @@ + diff --git a/web/html/login.html b/web/html/login.html index ed7fcdb..37b3fbf 100644 --- a/web/html/login.html +++ b/web/html/login.html @@ -2,6 +2,7 @@ {{template "head" .}} - - - - - -

{{ .title }}

-
-
- - - - - - - - - - - - - - - {{ i18n "login" }} - - - - -
-
-
- {{template "js" .}} - + } + }); + \ No newline at end of file diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html index 2483625..d0b1bf0 100644 --- a/web/html/xui/common_sider.html +++ b/web/html/xui/common_sider.html @@ -1,33 +1,33 @@ {{define "menuItems"}} - System Status + {{ i18n "menu.dashboard"}} - Inbound List + {{ i18n "menu.inbounds"}} - Panel Settings + {{ i18n "menu.setting"}} - + - + Github - Sign Out + {{ i18n "menu.logout"}} {{end}} @@ -35,12 +35,13 @@ {{define "commonSider"}} + @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key"> {{template "menuItems" .}} - +
@@ -65,4 +66,4 @@ }; -{{end}} \ No newline at end of file +{{end}} diff --git a/web/html/xui/component/inbound_info.html b/web/html/xui/component/inbound_info.html index 5bc2d10..cbf156d 100644 --- a/web/html/xui/component/inbound_info.html +++ b/web/html/xui/component/inbound_info.html @@ -1,108 +1,80 @@ {{define "inboundInfoStream"}} -

transmission: [[ inbound.network ]] -

+

{{ i18n "transmission" }}: [[ inbound.network ]]

- TLS domain name: [[ inbound.serverName ? inbound.serverName - : "none" ]] + tls {{ i18n "domainName" }}: [[ inbound.serverName ? inbound.serverName : '' ]]

- XTLS domain name: [[ inbound.serverName ? inbound.serverName - : "none" ]] + xtls {{ i18n "domainName" }}: [[ inbound.serverName ? inbound.serverName : '' ]]

{{end}} {{define "component/inboundInfoComponent"}}
-

protocol: [[ dbInbound.protocol ]] -

-

address: [[ dbInbound.address ]] -

-

port: [[ dbInbound.port ]] -

- -