mirror of
https://github.com/alireza0/x-ui.git
synced 2026-03-18 14:55:49 +00:00
expand multiDomain to externalProxy #637
- Including non-tls inbounds - Support forcedTLS/none/same security - Change port is possible - Move it to standard streamSettings - Optimizing database
This commit is contained in:
@@ -136,14 +136,9 @@ class DBInbound {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
genLink(address=this.address, remark=this.remark, clientIndex=0) {
|
||||
const inbound = this.toInbound();
|
||||
return inbound.genLink(address, remark, clientIndex);
|
||||
}
|
||||
|
||||
get genInboundLinks() {
|
||||
const inbound = this.toInbound();
|
||||
return inbound.genInboundLinks(this.address, this.remark);
|
||||
return inbound.genInboundLinks(this.remark);
|
||||
}
|
||||
}
|
||||
@@ -567,14 +567,12 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
|
||||
this.allowInsecure = allowInsecure;
|
||||
this.fingerprint = fingerprint;
|
||||
this.serverName = serverName;
|
||||
this.domains = domains;
|
||||
}
|
||||
static fromJson(json = {}) {
|
||||
return new TlsStreamSettings.Settings(
|
||||
json.allowInsecure,
|
||||
json.fingerprint,
|
||||
json.serverName,
|
||||
json.domains,
|
||||
);
|
||||
}
|
||||
toJson() {
|
||||
@@ -582,7 +580,6 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
|
||||
allowInsecure: this.allowInsecure,
|
||||
fingerprint: this.fingerprint,
|
||||
serverName: this.serverName,
|
||||
domains: this.domains,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -700,6 +697,7 @@ class SockoptStreamSettings extends XrayCommonClass {
|
||||
class StreamSettings extends XrayCommonClass {
|
||||
constructor(network='tcp',
|
||||
security='none',
|
||||
externalProxy = [],
|
||||
tlsSettings=new TlsStreamSettings(),
|
||||
realitySettings = new RealityStreamSettings(),
|
||||
tcpSettings=new TcpStreamSettings(),
|
||||
@@ -713,6 +711,7 @@ class StreamSettings extends XrayCommonClass {
|
||||
super();
|
||||
this.network = network;
|
||||
this.security = security;
|
||||
this.externalProxy = externalProxy;
|
||||
this.tls = tlsSettings;
|
||||
this.reality = realitySettings;
|
||||
this.tcp = tcpSettings;
|
||||
@@ -757,10 +756,10 @@ class StreamSettings extends XrayCommonClass {
|
||||
}
|
||||
|
||||
static fromJson(json={}) {
|
||||
|
||||
return new StreamSettings(
|
||||
json.network,
|
||||
json.security,
|
||||
json.externalProxy,
|
||||
TlsStreamSettings.fromJson(json.tlsSettings),
|
||||
RealityStreamSettings.fromJson(json.realitySettings),
|
||||
TcpStreamSettings.fromJson(json.tcpSettings),
|
||||
@@ -778,6 +777,7 @@ class StreamSettings extends XrayCommonClass {
|
||||
return {
|
||||
network: network,
|
||||
security: this.security,
|
||||
externalProxy: this.externalProxy,
|
||||
tlsSettings: this.isTls ? this.tls.toJson() : undefined,
|
||||
realitySettings: this.isReality ? this.reality.toJson() : undefined,
|
||||
tcpSettings: network === 'tcp' ? this.tcp.toJson() : undefined,
|
||||
@@ -836,6 +836,16 @@ class Inbound extends XrayCommonClass {
|
||||
return this.clientStats;
|
||||
}
|
||||
|
||||
get clients() {
|
||||
switch (this.protocol) {
|
||||
case Protocols.VMESS: return this.settings.vmesses;
|
||||
case Protocols.VLESS: return this.settings.vlesses;
|
||||
case Protocols.TROJAN: return this.settings.trojans;
|
||||
case Protocols.SHADOWSOCKS: return this.isSSMultiUser ? this.settings.shadowsockses : null;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
get protocol() {
|
||||
return this._protocol;
|
||||
}
|
||||
@@ -844,7 +854,7 @@ class Inbound extends XrayCommonClass {
|
||||
this._protocol = protocol;
|
||||
this.settings = Inbound.Settings.getSettings(protocol);
|
||||
if (protocol === Protocols.TROJAN) {
|
||||
this.tls = true;
|
||||
this.stream.isTls = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,26 +960,8 @@ class Inbound extends XrayCommonClass {
|
||||
}
|
||||
|
||||
isExpiry(index) {
|
||||
switch (this.protocol) {
|
||||
case Protocols.VMESS:
|
||||
if(this.settings.vmesses[index].expiryTime > 0)
|
||||
return this.settings.vmesses[index].expiryTime < new Date().getTime();
|
||||
return false
|
||||
case Protocols.VLESS:
|
||||
if(this.settings.vlesses[index].expiryTime > 0)
|
||||
return this.settings.vlesses[index].expiryTime < new Date().getTime();
|
||||
return false
|
||||
case Protocols.TROJAN:
|
||||
if(this.settings.trojans[index].expiryTime > 0)
|
||||
return this.settings.trojans[index].expiryTime < new Date().getTime();
|
||||
return false
|
||||
case Protocols.SHADOWSOCKS:
|
||||
if(this.settings.shadowsockses.length > 0 && this.settings.shadowsockses[index].expiryTime > 0)
|
||||
return this.settings.shadowsockses[index].expiryTime < new Date().getTime();
|
||||
return false
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
let exp = this.clients[index].expiryTime;
|
||||
return exp > 0 ? exp < new Date().getTime() : false;
|
||||
}
|
||||
|
||||
canEnableTls() {
|
||||
@@ -1008,19 +1000,20 @@ class Inbound extends XrayCommonClass {
|
||||
this.sniffing = new Sniffing();
|
||||
}
|
||||
|
||||
genVmessLink(address='', remark='', clientIndex=0) {
|
||||
genVmessLink(address='', port=this.port, forceTls, remark='', clientId) {
|
||||
if (this.protocol !== Protocols.VMESS) {
|
||||
return '';
|
||||
}
|
||||
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||
let obj = {
|
||||
v: '2',
|
||||
ps: remark,
|
||||
add: address,
|
||||
port: this.port,
|
||||
id: this.settings.vmesses[clientIndex].id,
|
||||
port: port,
|
||||
id: clientId,
|
||||
net: this.stream.network,
|
||||
type: 'none',
|
||||
tls: this.stream.security,
|
||||
tls: security,
|
||||
};
|
||||
let network = this.stream.network;
|
||||
if (network === 'tcp') {
|
||||
@@ -1060,8 +1053,8 @@ class Inbound extends XrayCommonClass {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stream.security === 'tls') {
|
||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
||||
if (security === 'tls') {
|
||||
if (address == this.listen && port == this.port && !ObjectUtil.isEmpty(this.stream.tls.server)) {
|
||||
obj.add = this.stream.tls.server;
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.tls.settings.serverName)){
|
||||
@@ -1081,11 +1074,10 @@ class Inbound extends XrayCommonClass {
|
||||
return 'vmess://' + base64(JSON.stringify(obj, null, 2));
|
||||
}
|
||||
|
||||
genVLESSLink(address = '', remark='', clientIndex=0) {
|
||||
const settings = this.settings;
|
||||
const uuid = settings.vlesses[clientIndex].id;
|
||||
const port = this.port;
|
||||
genVLESSLink(address = '', port=this.port, forceTls, remark='', clientId, flow) {
|
||||
const uuid = clientId;
|
||||
const type = this.stream.network;
|
||||
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||
const params = new Map();
|
||||
params.set("type", this.stream.network);
|
||||
switch (type) {
|
||||
@@ -1136,25 +1128,27 @@ class Inbound extends XrayCommonClass {
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.tls) {
|
||||
if (security === 'tls') {
|
||||
params.set("security", "tls");
|
||||
params.set("fp" , this.stream.tls.settings.fingerprint);
|
||||
params.set("alpn", this.stream.tls.alpn);
|
||||
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.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.stream.isTls){
|
||||
params.set("fp" , this.stream.tls.settings.fingerprint);
|
||||
params.set("alpn", this.stream.tls.alpn);
|
||||
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.serverName !== ''){
|
||||
params.set("sni", this.stream.tls.settings.serverName);
|
||||
}
|
||||
if (type == "tcp" && !ObjectUtil.isEmpty(flow)) {
|
||||
params.set("flow", flow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.reality) {
|
||||
if (security === 'reality') {
|
||||
params.set("security", "reality");
|
||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||
@@ -1164,14 +1158,14 @@ class Inbound extends XrayCommonClass {
|
||||
if (this.stream.reality.shortIds.length > 0) {
|
||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||
if (address == this.listen && port == this.port && !ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||
address = this.stream.reality.settings.serverName;
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||
params.set("spx", this.stream.reality.settings.spiderX);
|
||||
}
|
||||
if (this.stream.network === 'tcp' && !ObjectUtil.isEmpty(this.settings.vlesses[clientIndex].flow)) {
|
||||
params.set("flow", this.settings.vlesses[clientIndex].flow);
|
||||
if (type == 'tcp' && !ObjectUtil.isEmpty(flow)) {
|
||||
params.set("flow", flow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1184,9 +1178,8 @@ class Inbound extends XrayCommonClass {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
genSSLink(address='', remark='', clientIndex = 0) {
|
||||
genSSLink(address='', port=this.port, remark='', clientPassword) {
|
||||
let settings = this.settings;
|
||||
const port = this.port;
|
||||
const type = this.stream.network;
|
||||
const params = new Map();
|
||||
params.set("type", this.stream.network);
|
||||
@@ -1240,9 +1233,9 @@ class Inbound extends XrayCommonClass {
|
||||
|
||||
let password = new Array();
|
||||
if (this.isSS2022) password.push(settings.password);
|
||||
if (this.isSSMultiUser) password.push(settings.shadowsockses[clientIndex].password);
|
||||
if (this.isSSMultiUser) password.push(clientPassword);
|
||||
|
||||
let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${this.port}`;
|
||||
let link = `ss://${safeBase64(settings.method + ':' + password.join(':'))}@${address}:${port}`;
|
||||
const url = new URL(link);
|
||||
for (const [key, value] of params) {
|
||||
url.searchParams.set(key, value)
|
||||
@@ -1251,9 +1244,8 @@ class Inbound extends XrayCommonClass {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
genTrojanLink(address = '', remark = '', clientIndex = 0) {
|
||||
let settings = this.settings;
|
||||
const port = this.port;
|
||||
genTrojanLink(address = '', port=this.port, forceTls, remark = '', clientPassword) {
|
||||
const security = forceTls == 'same' ? this.stream.security : forceTls;
|
||||
const type = this.stream.network;
|
||||
const params = new Map();
|
||||
params.set("type", this.stream.network);
|
||||
@@ -1305,22 +1297,24 @@ class Inbound extends XrayCommonClass {
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.tls) {
|
||||
if (security === 'tls') {
|
||||
params.set("security", "tls");
|
||||
params.set("fp" , this.stream.tls.settings.fingerprint);
|
||||
params.set("alpn", this.stream.tls.alpn);
|
||||
if(this.stream.tls.settings.allowInsecure){
|
||||
params.set("allowInsecure", "1");
|
||||
if (this.stream.isTls){
|
||||
params.set("fp" , this.stream.tls.settings.fingerprint);
|
||||
params.set("alpn", this.stream.tls.alpn);
|
||||
if(this.stream.tls.settings.allowInsecure){
|
||||
params.set("allowInsecure", "1");
|
||||
}
|
||||
if (address == this.listen && port == this.port && !ObjectUtil.isEmpty(this.stream.tls.server)) {
|
||||
address = this.stream.tls.server;
|
||||
}
|
||||
if (this.stream.tls.settings.serverName !== ''){
|
||||
params.set("sni", this.stream.tls.settings.serverName);
|
||||
}
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.tls.server)) {
|
||||
address = this.stream.tls.server;
|
||||
}
|
||||
if (this.stream.tls.settings.serverName !== ''){
|
||||
params.set("sni", this.stream.tls.settings.serverName);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.reality) {
|
||||
if (security === 'reality') {
|
||||
params.set("security", "reality");
|
||||
params.set("pbk", this.stream.reality.settings.publicKey);
|
||||
params.set("fp", this.stream.reality.settings.fingerprint);
|
||||
@@ -1330,7 +1324,7 @@ class Inbound extends XrayCommonClass {
|
||||
if (this.stream.reality.shortIds.length > 0) {
|
||||
params.set("sid", this.stream.reality.shortIds.split(",")[0]);
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||
if (address == this.listen && port == this.port && !ObjectUtil.isEmpty(this.stream.reality.settings.serverName)) {
|
||||
address = this.stream.reality.settings.serverName;
|
||||
}
|
||||
if (!ObjectUtil.isEmpty(this.stream.reality.settings.spiderX)) {
|
||||
@@ -1338,7 +1332,7 @@ class Inbound extends XrayCommonClass {
|
||||
}
|
||||
}
|
||||
|
||||
const link = `trojan://${settings.trojans[clientIndex].password}@${address}:${this.port}`;
|
||||
const link = `trojan://${clientPassword}@${address}:${port}`;
|
||||
const url = new URL(link);
|
||||
for (const [key, value] of params) {
|
||||
url.searchParams.set(key, value)
|
||||
@@ -1347,38 +1341,62 @@ class Inbound extends XrayCommonClass {
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
genLink(address='', remark='', clientIndex=0) {
|
||||
genLink(address='', port=this.port, forceTls='same', remark='', client) {
|
||||
switch (this.protocol) {
|
||||
case Protocols.VMESS:
|
||||
return this.genVmessLink(address, remark, clientIndex);
|
||||
return this.genVmessLink(address, port, forceTls, remark, client.id);
|
||||
case Protocols.VLESS:
|
||||
return this.genVLESSLink(address, remark, clientIndex);
|
||||
return this.genVLESSLink(address, port, forceTls, remark, client.id, client.flow);
|
||||
case Protocols.SHADOWSOCKS:
|
||||
return this.genSSLink(address, remark, clientIndex);
|
||||
return this.genSSLink(address, port, remark, this.isSSMultiUser ? client.password : '');
|
||||
case Protocols.TROJAN:
|
||||
return this.genTrojanLink(address, remark, clientIndex);
|
||||
return this.genTrojanLink(address, port, forceTls, remark, client.password);
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
genInboundLinks(address = '', remark = '') {
|
||||
let link = '';
|
||||
switch (this.protocol) {
|
||||
case Protocols.VMESS:
|
||||
case Protocols.VLESS:
|
||||
case Protocols.TROJAN:
|
||||
case Protocols.SHADOWSOCKS:
|
||||
JSON.parse(this.settings).clients.forEach((client,index) => {
|
||||
if(this.tls && !ObjectUtil.isArrEmpty(this.stream.tls.settings.domains)){
|
||||
this.stream.tls.settings.domains.forEach((domain) => {
|
||||
link += this.genLink(domain.domain, [remark, client.email, domain.remark].filter(x => x.length > 0).join('-'), index) + '\r\n';
|
||||
});
|
||||
} else {
|
||||
link += this.genLink(address, [remark, client.email].filter(x => x.length > 0).join('-'), index) + '\r\n';
|
||||
}
|
||||
genAllLinks(remark='', client){
|
||||
let result = [];
|
||||
let email = client ? client.email : '';
|
||||
let addr = this.listen;
|
||||
let port = this.port
|
||||
if(ObjectUtil.isArrEmpty(this.stream.externalProxy)){
|
||||
let r = [remark, email].filter(x => x.length > 0).join('-');
|
||||
result.push({
|
||||
remark: r,
|
||||
link: this.genLink(addr, port, 'same', r, client)
|
||||
});
|
||||
} else {
|
||||
this.stream.externalProxy.forEach((ep) => {
|
||||
let r = [remark, email, ep.remark].filter(x => x.length > 0).join('-')
|
||||
let dest = ep.dest.split(":");
|
||||
if(dest.length == 2) {
|
||||
addr = dest[0];
|
||||
port = dest[1];
|
||||
} else {
|
||||
addr = dest[0];
|
||||
}
|
||||
result.push({
|
||||
remark: r,
|
||||
link: this.genLink(addr, port, ep.forceTls, r, client)
|
||||
});
|
||||
return link;
|
||||
default: return '';
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
genInboundLinks(remark = '') {
|
||||
if(this.clients){
|
||||
let links = [];
|
||||
this.clients.forEach((client) => {
|
||||
genAllLinks(remark,client).forEach(l => {
|
||||
links.push(l.link);
|
||||
})
|
||||
});
|
||||
return links.join('\r\n');
|
||||
} else {
|
||||
if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, remark);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,39 +19,25 @@
|
||||
|
||||
const qrModal = {
|
||||
title: '',
|
||||
clientIndex: 0,
|
||||
inbound: new Inbound(),
|
||||
dbInbound: new DBInbound(),
|
||||
client: null,
|
||||
qrcodes: [],
|
||||
clipboard: null,
|
||||
visible: false,
|
||||
subId: '',
|
||||
show: function (title = '', dbInbound = new DBInbound(), clientIndex = 0) {
|
||||
show: function (title = '', dbInbound, client) {
|
||||
this.title = title;
|
||||
this.clientIndex = clientIndex;
|
||||
this.dbInbound = dbInbound;
|
||||
this.inbound = dbInbound.toInbound();
|
||||
settings = JSON.parse(this.inbound.settings);
|
||||
this.client = settings.clients[clientIndex];
|
||||
remark = [this.dbInbound.remark, ( this.client ? this.client.email : '')].filter(Boolean).join('-');
|
||||
address = this.dbInbound.address;
|
||||
this.client = client;
|
||||
this.subId = '';
|
||||
this.qrcodes = [];
|
||||
if (this.inbound.stream.isTls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
|
||||
this.inbound.stream.tls.settings.domains.forEach((domain) => {
|
||||
remarkText = [remark, domain.remark].filter(Boolean).join('-');
|
||||
this.qrcodes.push({
|
||||
remark: remarkText,
|
||||
link: this.inbound.genLink(domain.domain, remarkText, clientIndex)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.inbound.genAllLinks(this.dbInbound.remark, client).forEach(l => {
|
||||
this.qrcodes.push({
|
||||
remark: remark,
|
||||
link: this.inbound.genLink(address, remark, clientIndex)
|
||||
remark: l.remark,
|
||||
link: l.link
|
||||
});
|
||||
}
|
||||
});
|
||||
this.visible = true;
|
||||
},
|
||||
close: function () {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
this.isEdit = isEdit;
|
||||
this.dbInbound = new DBInbound(dbInbound);
|
||||
this.inbound = dbInbound.toInbound();
|
||||
this.clients = this.getClients(this.inbound.protocol, this.inbound.settings);
|
||||
this.clients = this.inbound.clients;
|
||||
this.index = index === null ? this.clients.length : index;
|
||||
this.delayedStart = false;
|
||||
if (isEdit){
|
||||
@@ -50,15 +50,6 @@
|
||||
this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
|
||||
this.confirm = confirm;
|
||||
},
|
||||
getClients(protocol, clientSettings) {
|
||||
switch(protocol){
|
||||
case Protocols.VMESS: return clientSettings.vmesses;
|
||||
case Protocols.VLESS: return clientSettings.vlesses;
|
||||
case Protocols.TROJAN: return clientSettings.trojans;
|
||||
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
|
||||
default: return null;
|
||||
}
|
||||
},
|
||||
getClientId(protocol, client) {
|
||||
switch(protocol){
|
||||
case Protocols.TROJAN: return client.password;
|
||||
|
||||
@@ -126,6 +126,7 @@
|
||||
<!-- stream settings -->
|
||||
<template v-if="inbound.canEnableStream()">
|
||||
{{template "form/streamSettings"}}
|
||||
{{template "form/externalProxy" }}
|
||||
</template>
|
||||
|
||||
<!-- tls settings -->
|
||||
|
||||
29
web/html/xui/form/stream/external_proxy.html
Normal file
29
web/html/xui/form/stream/external_proxy.html
Normal file
@@ -0,0 +1,29 @@
|
||||
{{define "form/externalProxy"}}
|
||||
<a-form layout="inline">
|
||||
<a-divider style="margin:0;"></a-divider>
|
||||
<a-form-item label="External Proxy">
|
||||
<a-switch v-model="externalProxy"></a-switch>
|
||||
<a-button v-if="externalProxy" type="primary" style="margin-left: 10px" size="small" @click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', remark: ''})">+</a-button>
|
||||
</a-form-item>
|
||||
<table width="100%" class="ant-table-tbody" v-if="externalProxy">
|
||||
<tr style="line-height: 40px;">
|
||||
<td width="100%">
|
||||
<a-input-group style="margin-top:5px;" compact v-for="(row, index) in inbound.stream.externalProxy">
|
||||
<template v-if="inbound.canEnableTls()">
|
||||
<a-tooltip title="Force TLS">
|
||||
<a-select v-model="row.forceTls" style="width:20%; margin: 0px" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="same">{{ i18n "pages.inbounds.same" }}</a-select-option>
|
||||
<a-select-option value="none">{{ i18n "none" }}</a-select-option>
|
||||
<a-select-option value="tls">TLS</a-select-option>
|
||||
</a-select>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input style="width: 40%" v-model.trim="row.dest" placeholder='{{ i18n "host" }}:{{ i18n "pages.inbounds.port" }}'></a-input>
|
||||
<a-input :style="inbound.canEnableTls() ? 'width: 30%' : 'width: 50%'" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'></a-input>
|
||||
<a-button style="width: 10%; margin: 0px" @click="inbound.stream.externalProxy.splice(index, 1)">-</a-button>
|
||||
</a-input-group>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -4,9 +4,8 @@
|
||||
<a-divider style="margin:0;"></a-divider>
|
||||
<table width="100%" class="ant-table-tbody">
|
||||
<tr>
|
||||
<td width="30%">{{ i18n "security" }}</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
<td colspan="2">
|
||||
<a-form-item label='{{ i18n "security" }}'>
|
||||
<a-radio-group v-model="inbound.stream.security" button-style="solid">
|
||||
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
|
||||
<a-radio-button value="tls">TLS</a-radio-button>
|
||||
@@ -64,26 +63,7 @@
|
||||
</a-form-item>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="line-height: 40px;">
|
||||
<td>Multi Domain</td>
|
||||
<td>
|
||||
<a-switch v-model="multiDomain"></a-switch>
|
||||
<a-button v-if="multiDomain" style="margin-left: 10px" size="small" @click="inbound.stream.tls.settings.domains.push({remark: '', domain: ''})">+</a-button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="multiDomain" style="line-height: 40px;">
|
||||
<td colspan="2" width="100%">
|
||||
<a-input-group style="margin-top:5px;" compact v-for="(row, index) in inbound.stream.tls.settings.domains">
|
||||
<a-input style="width: 50%" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'>
|
||||
<template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input style="width: 50%" v-model.trim="row.domain" placeholder='{{ i18n "host" }}'>
|
||||
<a-button slot="addonAfter" size="small" style="margin: 0px" @click="inbound.stream.tls.settings.domains.splice(index, 1)">-</a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-else>
|
||||
<tr>
|
||||
<td>{{ i18n "domainName" }}</td>
|
||||
<td>
|
||||
<a-form-item>
|
||||
|
||||
@@ -246,7 +246,6 @@
|
||||
visible: false,
|
||||
inbound: new Inbound(),
|
||||
dbInbound: new DBInbound(),
|
||||
settings: null,
|
||||
clientSettings: null,
|
||||
clientStats: [],
|
||||
upStats: 0,
|
||||
@@ -261,27 +260,10 @@
|
||||
this.index = index;
|
||||
this.inbound = dbInbound.toInbound();
|
||||
this.dbInbound = new DBInbound(dbInbound);
|
||||
this.settings = JSON.parse(this.inbound.settings);
|
||||
this.clientSettings = this.settings.clients ? Object.values(this.settings.clients)[index] : null;
|
||||
this.isExpired = this.inbound.isExpiry(index);
|
||||
this.clientStats = this.settings.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
||||
remark = [this.dbInbound.remark, ( this.clientSettings ? this.clientSettings.email : '')].filter(Boolean).join('-');
|
||||
address = this.dbInbound.address;
|
||||
this.links = [];
|
||||
if (this.inbound.stream.isTls && !ObjectUtil.isArrEmpty(this.inbound.stream.tls.settings.domains)) {
|
||||
this.inbound.stream.tls.settings.domains.forEach((domain) => {
|
||||
remarkText = [remark, domain.remark].filter(Boolean).join('-');
|
||||
this.links.push({
|
||||
remark: remarkText,
|
||||
link: this.inbound.genLink(domain.domain, remarkText, index)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.links.push({
|
||||
remark: remark,
|
||||
link: this.inbound.genLink(address, remark, index)
|
||||
});
|
||||
}
|
||||
this.clientSettings = this.inbound.clients ? this.inbound.clients[index] : null;
|
||||
this.isExpired = this.inbound.clients ? this.inbound.isExpiry(index): this.dbInbound.isExpiry;
|
||||
this.clientStats = this.inbound.clients ? this.dbInbound.clientStats.find(row => row.email === this.clientSettings.email) : [];
|
||||
this.links = this.inbound.genAllLinks(this.dbInbound.remark, this.clientSettings);
|
||||
if (this.clientSettings) {
|
||||
if (this.clientSettings.subId) {
|
||||
this.subLink = this.genSubLink(this.clientSettings.subId);
|
||||
|
||||
@@ -42,15 +42,6 @@
|
||||
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;
|
||||
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
|
||||
default: return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
new Vue({
|
||||
@@ -69,7 +60,7 @@
|
||||
return inModal.isEdit;
|
||||
},
|
||||
get client() {
|
||||
return inModal.getClients(this.inbound.protocol, this.inbound.settings)[0];
|
||||
return inModal.inbound.clients[0];
|
||||
},
|
||||
get delayedExpireDays() {
|
||||
return this.client && this.client.expiryTime < 0 ? this.client.expiryTime / -86400000 : 0;
|
||||
@@ -77,16 +68,18 @@
|
||||
set delayedExpireDays(days){
|
||||
this.client.expiryTime = -86400000 * days;
|
||||
},
|
||||
get multiDomain() {
|
||||
return this.inbound.stream.tls.settings.domains.length > 0;
|
||||
get externalProxy() {
|
||||
return this.inbound.stream.externalProxy.length > 0;
|
||||
},
|
||||
set multiDomain(value) {
|
||||
set externalProxy(value) {
|
||||
if (value) {
|
||||
inModal.inbound.stream.tls.server = "";
|
||||
inModal.inbound.stream.tls.settings.domains = [{ remark: "", domain: window.location.hostname }];
|
||||
inModal.inbound.stream.externalProxy = [{
|
||||
forceTls: "same",
|
||||
dest: window.location.hostname + ":" + inModal.inbound.port,
|
||||
remark: ""
|
||||
}];
|
||||
} else {
|
||||
inModal.inbound.stream.tls.server = "";
|
||||
inModal.inbound.stream.tls.settings.domains = [];
|
||||
inModal.inbound.stream.externalProxy = [];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -617,7 +617,7 @@
|
||||
},
|
||||
getClientCounts(dbInbound, inbound) {
|
||||
let clientCount = 0, active = [], deactive = [], depleted = [], expiring = [], online = [];
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
clients = inbound.clients;
|
||||
clientStats = dbInbound.clientStats
|
||||
now = new Date().getTime()
|
||||
if (clients) {
|
||||
@@ -967,15 +967,6 @@
|
||||
this.submit(`/xui/inbound/${dbInboundId}/delClient/${clientId}`);
|
||||
}
|
||||
},
|
||||
getClients(protocol, clientSettings) {
|
||||
switch (protocol) {
|
||||
case Protocols.VMESS: return clientSettings.vmesses;
|
||||
case Protocols.VLESS: return clientSettings.vlesses;
|
||||
case Protocols.TROJAN: return clientSettings.trojans;
|
||||
case Protocols.SHADOWSOCKS: return clientSettings.shadowsockses;
|
||||
default: return null;
|
||||
}
|
||||
},
|
||||
getClientId(protocol, client) {
|
||||
switch (protocol) {
|
||||
case Protocols.TROJAN: return client.password;
|
||||
@@ -995,7 +986,7 @@
|
||||
newDbInbound.listen = rootInbound.listen;
|
||||
newDbInbound.port = rootInbound.port;
|
||||
newInbound = newDbInbound.toInbound();
|
||||
newInbound.stream.security = 'tls';
|
||||
newInbound.stream.security = rootInbound.stream.security;
|
||||
newInbound.stream.tls = rootInbound.stream.tls;
|
||||
newDbInbound.streamSettings = newInbound.stream.toString();
|
||||
}
|
||||
@@ -1004,18 +995,15 @@
|
||||
},
|
||||
showQrcode(dbInboundId, client) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
inbound = dbInbound.toInbound();
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||
newDbInbound = this.checkFallback(dbInbound);
|
||||
qrModal.show('{{ i18n "qrCode"}}', newDbInbound, index);
|
||||
qrModal.show('{{ i18n "qrCode"}}', newDbInbound, client);
|
||||
},
|
||||
showInfo(dbInboundId, client) {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
index=0;
|
||||
if (dbInbound.isMultiUser()){
|
||||
inbound = dbInbound.toInbound();
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
clients = inbound.clients;
|
||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||
}
|
||||
newDbInbound = this.checkFallback(dbInbound);
|
||||
@@ -1029,7 +1017,7 @@
|
||||
this.loading()
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
inbound = dbInbound.toInbound();
|
||||
clients = this.getClients(dbInbound.protocol, inbound.settings);
|
||||
clients = inbound.clients;
|
||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||
clients[index].enable = !clients[index].enable;
|
||||
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
||||
@@ -1043,15 +1031,7 @@
|
||||
}
|
||||
},
|
||||
getInboundClients(dbInbound) {
|
||||
if (dbInbound.protocol == Protocols.VLESS) {
|
||||
return dbInbound.toInbound().settings.vlesses;
|
||||
} else if (dbInbound.protocol == Protocols.VMESS) {
|
||||
return dbInbound.toInbound().settings.vmesses;
|
||||
} else if (dbInbound.protocol == Protocols.TROJAN) {
|
||||
return dbInbound.toInbound().settings.trojans;
|
||||
} else if (dbInbound.protocol == Protocols.SHADOWSOCKS) {
|
||||
return dbInbound.toInbound().settings.shadowsockses;
|
||||
}
|
||||
return dbInbound.toInbound().clients;
|
||||
},
|
||||
resetClientTraffic(client, dbInboundId, confirmation = true) {
|
||||
if (confirmation){
|
||||
@@ -1181,11 +1161,11 @@
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', newDbInbound.genInboundLinks, newDbInbound.remark);
|
||||
},
|
||||
exportAllLinks() {
|
||||
let copyText = '';
|
||||
let copyText = [];
|
||||
for (const dbInbound of this.dbInbounds) {
|
||||
copyText += dbInbound.genInboundLinks;
|
||||
copyText.push(dbInbound.genInboundLinks);
|
||||
}
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText, 'All-Inbounds');
|
||||
txtModal.show('{{ i18n "pages.inbounds.export"}}', copyText.join('\r\n'), 'All-Inbounds');
|
||||
},
|
||||
async startDataRefreshLoop() {
|
||||
while (this.isRefreshEnabled) {
|
||||
|
||||
@@ -133,19 +133,24 @@ func (s *XrayService) GetXrayConfig() (*xray.Config, error) {
|
||||
inbound.Settings = string(modifiedSettings)
|
||||
}
|
||||
|
||||
// Unmarshal stream JSON
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
if len(inbound.StreamSettings) > 0 {
|
||||
// Unmarshal stream JSON
|
||||
var stream map[string]interface{}
|
||||
json.Unmarshal([]byte(inbound.StreamSettings), &stream)
|
||||
|
||||
// Remove the "settings" field under "tlsSettings" and "realitySettings"
|
||||
tlsSettings, ok1 := stream["tlsSettings"].(map[string]interface{})
|
||||
realitySettings, ok2 := stream["realitySettings"].(map[string]interface{})
|
||||
if ok1 || ok2 {
|
||||
if ok1 {
|
||||
delete(tlsSettings, "settings")
|
||||
} else if ok2 {
|
||||
delete(realitySettings, "settings")
|
||||
// Remove the "settings" field under "tlsSettings" and "realitySettings"
|
||||
tlsSettings, ok1 := stream["tlsSettings"].(map[string]interface{})
|
||||
realitySettings, ok2 := stream["realitySettings"].(map[string]interface{})
|
||||
if ok1 || ok2 {
|
||||
if ok1 {
|
||||
delete(tlsSettings, "settings")
|
||||
} else if ok2 {
|
||||
delete(realitySettings, "settings")
|
||||
}
|
||||
}
|
||||
|
||||
delete(stream, "externalProxy")
|
||||
|
||||
newStream, err := json.MarshalIndent(stream, "", " ")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -169,6 +169,7 @@
|
||||
"telegramDesc" = "Use Telegram ID without @ or chat IDs ( you can get it here @userinfobot or use '/id' command in bot )"
|
||||
"subscriptionDesc" = "You can find your sub link on Details, also you can use the same name for several configurations"
|
||||
"info" = "Info"
|
||||
"same" = "Same"
|
||||
|
||||
[pages.client]
|
||||
"add" = "Add Client"
|
||||
|
||||
@@ -168,6 +168,7 @@
|
||||
"telegramDesc" = "از آیدی تلگرام بدون @ یا آیدی چت استفاده کنید (می توانید آن را از اینجا دریافت کنید @userinfobot یا در ربات دستور '/id' را وارد کنید)"
|
||||
"subscriptionDesc" = "می توانید ساب لینک خود را در جزئیات پیدا کنید، همچنین می توانید از همین نام برای چندین کانفیگ استفاده کنید"
|
||||
"info" = "اطلاعات"
|
||||
"same" = "همسان"
|
||||
|
||||
[pages.client]
|
||||
"add" = "کاربر جدید"
|
||||
|
||||
@@ -169,6 +169,7 @@
|
||||
"telegramDesc" = "Используйте идентификатор Telegram без символа @ или идентификатора чата (можно получить его здесь @userinfobot или использовать команду '/id' в боте)"
|
||||
"subscriptionDesc" = "вы можете найти свою ссылку подписки в разделе «Подробнее», также вы можете использовать одно и то же имя для нескольких конфигов"
|
||||
"info" = "Информация"
|
||||
"same" = "Тот же"
|
||||
|
||||
[pages.client]
|
||||
"add" = "Добавить клиента"
|
||||
|
||||
@@ -169,6 +169,7 @@
|
||||
"telegramDesc" = "使用 Telegram ID,不包含 @ 符号或聊天 ID(可以在 @userinfobot 处获取,或在机器人中使用'/id'命令)"
|
||||
"subscriptionDesc" = "您可以在详细信息上找到您的子链接,也可以对多个配置使用相同的名称"
|
||||
"info" = "信息"
|
||||
"same" = "相同"
|
||||
|
||||
[pages.client]
|
||||
"add" = "添加客户端"
|
||||
|
||||
Reference in New Issue
Block a user