Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2023-04-18 21:04:06 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2023-04-18 21:04:06 +0300
commit3e0faecaaebd8394a1a9d18ec11b5a60f210b654 (patch)
tree70226a9e7a73155d05410bda80a38673f8db2a83
parentdc7dbae14a37492ac3a7e3822b3e0b250e248173 (diff)
improve reality setting
split xtls from tls - remove iran warp - remove old setting reality from franzkafka (it was a messy code) -and other improvement Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
-rw-r--r--web/assets/css/custom.css5
-rw-r--r--web/assets/js/model/xray.js333
-rw-r--r--web/assets/js/util/utils.js46
-rw-r--r--web/controller/inbound.go10
-rw-r--r--web/controller/server.go16
-rw-r--r--web/controller/sub.go8
-rw-r--r--web/html/xui/client_bulk_modal.html46
-rw-r--r--web/html/xui/client_modal.html9
-rw-r--r--web/html/xui/form/client.html6
-rw-r--r--web/html/xui/form/protocol/trojan.html4
-rw-r--r--web/html/xui/form/protocol/vless.html4
-rw-r--r--web/html/xui/form/protocol/vmess.html2
-rw-r--r--web/html/xui/form/tls_settings.html92
-rw-r--r--web/html/xui/inbound_info_modal.html6
-rw-r--r--web/html/xui/inbound_modal.html55
-rw-r--r--web/html/xui/inbounds.html34
-rw-r--r--web/html/xui/setting.html18
-rw-r--r--web/service/inbound.go167
-rw-r--r--web/service/server.go26
-rw-r--r--web/service/sub.go70
20 files changed, 630 insertions, 327 deletions
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.settings.fingerprint != "") {
+ params.set("fp", this.stream.reality.settings.fingerprint);
}
- if (this.stream.reality.fingerprint != "") {
- params.set("fp", this.stream.reality.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.settings.fingerprint != "") {
+ params.set("fp", this.stream.reality.settings.fingerprint);
}
- if (this.stream.reality.fingerprint != "") {
- params.set("fp", this.stream.reality.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 @@
<span slot="label">{{ i18n "pages.client.clientCount" }}</span>
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
</a-form-item>
+ <a-form-item>
+ <span slot="label">
+ <span>{{ i18n "pages.inbounds.IPLimit" }}</span>
+ <a-tooltip>
+ <template slot="title">
+ <span>{{ i18n "pages.inbounds.IPLimitDesc" }}</span>
+ </template>
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </span>
+ <a-input type="number" v-model.number="clientsBulkModal.limitIp" min="0" style="width: 70px;" ></a-input>
+ </a-form-item>
+ <a-form-item v-if="clientsBulkModal.inbound.xtls" label="Flow">
+ <a-select v-model="clientsBulkModal.flow" style="width: 150px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+ <a-select-option value="">{{ i18n "none" }}</a-select-option>
+ <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item v-if="clientsBulkModal.inbound.canEnableTlsFlow()" label="Flow" layout="inline">
+ <a-select v-model="clientsBulkModal.flow" style="width: 150px">
+ <a-select-option value="" selected>{{ i18n "none" }}</a-select-option>
+ <a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
+ </a-select>
+ </a-form-item>
<a-form-item label="Subscription">
<a-input v-model.trim="clientsBulkModal.subId"></a-input>