mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-12 06:10:58 +00:00
TLS: Remove ECH Force Query
This commit is contained in:
@@ -1,58 +1,41 @@
|
||||
{{define "modals/clientsBulkModal"}}
|
||||
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible"
|
||||
:title="clientsBulkModal.title"
|
||||
@ok="clientsBulkModal.ok" :confirm-loading="clientsBulkModal.confirmLoading"
|
||||
:closable="true" :mask-closable="false"
|
||||
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }">
|
||||
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title"
|
||||
@ok="clientsBulkModal.ok" :confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.client.method" }}'>
|
||||
<a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="0">Random</a-select-option>
|
||||
<a-select-option :value="1">Random+Prefix</a-select-option>
|
||||
<a-select-option :value="2">Random+Prefix+Num</a-select-option>
|
||||
<a-select-option
|
||||
:value="3">Random+Prefix+Num+Postfix</a-select-option>
|
||||
<a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
|
||||
<a-select-option :value="4">Prefix+Num+Postfix</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.first" }}'
|
||||
v-if="clientsBulkModal.emailMethod>1">
|
||||
<a-input-number v-model.number="clientsBulkModal.firstNum"
|
||||
:min="1"></a-input-number>
|
||||
<a-form-item label='{{ i18n "pages.client.first" }}' v-if="clientsBulkModal.emailMethod>1">
|
||||
<a-input-number v-model.number="clientsBulkModal.firstNum" :min="1"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.last" }}'
|
||||
v-if="clientsBulkModal.emailMethod>1">
|
||||
<a-input-number v-model.number="clientsBulkModal.lastNum"
|
||||
:min="clientsBulkModal.firstNum"></a-input-number>
|
||||
<a-form-item label='{{ i18n "pages.client.last" }}' v-if="clientsBulkModal.emailMethod>1">
|
||||
<a-input-number v-model.number="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.prefix" }}'
|
||||
v-if="clientsBulkModal.emailMethod>0">
|
||||
<a-form-item label='{{ i18n "pages.client.prefix" }}' v-if="clientsBulkModal.emailMethod>0">
|
||||
<a-input v-model.trim="clientsBulkModal.emailPrefix"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.postfix" }}'
|
||||
v-if="clientsBulkModal.emailMethod>2">
|
||||
<a-form-item label='{{ i18n "pages.client.postfix" }}' v-if="clientsBulkModal.emailMethod>2">
|
||||
<a-input v-model.trim="clientsBulkModal.emailPostfix"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.clientCount" }}'
|
||||
v-if="clientsBulkModal.emailMethod < 2">
|
||||
<a-input-number v-model.number="clientsBulkModal.quantity" :min="1"
|
||||
:max="500"></a-input-number>
|
||||
<a-form-item label='{{ i18n "pages.client.clientCount" }}' v-if="clientsBulkModal.emailMethod < 2">
|
||||
<a-input-number v-model.number="clientsBulkModal.quantity" :min="1" :max="500"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "security" }}'
|
||||
v-if="inbound.protocol === Protocols.VMESS">
|
||||
<a-select v-model="clientsBulkModal.security"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-form-item label='{{ i18n "security" }}' v-if="inbound.protocol === Protocols.VMESS">
|
||||
<a-select v-model="clientsBulkModal.security" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in USERS_SECURITY" :value="key">[[
|
||||
key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Flow'
|
||||
v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
|
||||
<a-select v-model="clientsBulkModal.flow"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-form-item label='Flow' v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
|
||||
<a-select v-model="clientsBulkModal.flow" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value selected>{{ i18n "none"
|
||||
}}</a-select-option>
|
||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[
|
||||
@@ -67,9 +50,7 @@
|
||||
}}</span>
|
||||
</template>
|
||||
Subscription
|
||||
<a-icon
|
||||
@click="clientsBulkModal.subId = RandomUtil.randomLowerAndNum(16)"
|
||||
type="sync"></a-icon>
|
||||
<a-icon @click="clientsBulkModal.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="clientsBulkModal.subId"></a-input>
|
||||
@@ -84,8 +65,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number :style="{ width: '50%' }"
|
||||
v-model.number="clientsBulkModal.tgId" min="0"></a-input-number>
|
||||
<a-input-number :style="{ width: '50%' }" v-model.number="clientsBulkModal.tgId" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="app.ipLimitEnable">
|
||||
<template slot="label">
|
||||
@@ -97,8 +77,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="clientsBulkModal.limitIp"
|
||||
min="0"></a-input-number>
|
||||
<a-input-number v-model.number="clientsBulkModal.limitIp" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
@@ -110,17 +89,13 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="clientsBulkModal.totalGB"
|
||||
:min="0"></a-input-number>
|
||||
<a-input-number v-model.number="clientsBulkModal.totalGB" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
|
||||
<a-switch v-model="clientsBulkModal.delayedStart"
|
||||
@click="clientsBulkModal.expiryTime=0"></a-switch>
|
||||
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.expireDays" }}'
|
||||
v-if="clientsBulkModal.delayedStart">
|
||||
<a-input-number v-model.number="delayedExpireDays"
|
||||
:min="0"></a-input-number>
|
||||
<a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="clientsBulkModal.delayedStart">
|
||||
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-else>
|
||||
<template slot="label">
|
||||
@@ -133,15 +108,11 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-date-picker v-if="datepicker == 'gregorian'"
|
||||
:show-time="{ format: 'HH:mm:ss' }"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
<a-date-picker v-if="datepicker == 'gregorian'" :show-time="{ format: 'HH:mm:ss' }"
|
||||
format="YYYY-MM-DD HH:mm:ss" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
v-model="clientsBulkModal.expiryTime"></a-date-picker>
|
||||
<a-persian-datepicker v-else
|
||||
placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
||||
value="clientsBulkModal.expiryTime"
|
||||
v-model="clientsBulkModal.expiryTime">
|
||||
<a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
||||
value="clientsBulkModal.expiryTime" v-model="clientsBulkModal.expiryTime">
|
||||
</a-persian-datepicker>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="clientsBulkModal.expiryTime != 0">
|
||||
@@ -154,13 +125,11 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="clientsBulkModal.reset"
|
||||
:min="0"></a-input-number>
|
||||
<a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<script>
|
||||
|
||||
const clientsBulkModal = {
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
@@ -219,7 +188,7 @@
|
||||
title = '',
|
||||
okText = '{{ i18n "sure" }}',
|
||||
dbInbound = null,
|
||||
confirm = (inbound, dbInbound) => { }
|
||||
confirm = (inbound, dbInbound) => {}
|
||||
}) {
|
||||
this.visible = true;
|
||||
this.title = title;
|
||||
@@ -245,12 +214,19 @@
|
||||
},
|
||||
newClient(protocol) {
|
||||
switch (protocol) {
|
||||
case Protocols.VMESS: return new Inbound.VmessSettings.VMESS();
|
||||
case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS();
|
||||
case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan();
|
||||
case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings.Shadowsocks(clientsBulkModal.inbound.settings.shadowsockses[0].method);
|
||||
case Protocols.HYSTERIA: return new Inbound.HysteriaSettings.Hysteria();
|
||||
default: return null;
|
||||
case Protocols.VMESS:
|
||||
return new Inbound.VmessSettings.VMESS();
|
||||
case Protocols.VLESS:
|
||||
return new Inbound.VLESSSettings.VLESS();
|
||||
case Protocols.TROJAN:
|
||||
return new Inbound.TrojanSettings.Trojan();
|
||||
case Protocols.SHADOWSOCKS:
|
||||
return new Inbound.ShadowsocksSettings.Shadowsocks(clientsBulkModal.inbound.settings
|
||||
.shadowsockses[0].method);
|
||||
case Protocols.HYSTERIA:
|
||||
return new Inbound.HysteriaSettings.Hysteria();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
close() {
|
||||
@@ -271,7 +247,8 @@
|
||||
return this.clientsBulkModal.inbound;
|
||||
},
|
||||
get delayedExpireDays() {
|
||||
return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 : 0;
|
||||
return this.clientsBulkModal.expiryTime < 0 ? this.clientsBulkModal.expiryTime / -86400000 :
|
||||
0;
|
||||
},
|
||||
get datepicker() {
|
||||
return app.datepicker;
|
||||
@@ -281,6 +258,5 @@
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -1,10 +1,7 @@
|
||||
{{define "modals/clientsModal"}}
|
||||
<a-modal id="client-modal" v-model="clientModal.visible"
|
||||
:title="clientModal.title" @ok="clientModal.ok"
|
||||
:confirm-loading="clientModal.confirmLoading" :closable="true"
|
||||
:mask-closable="false"
|
||||
:class="themeSwitcher.currentTheme"
|
||||
:ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
|
||||
<a-modal id="client-modal" v-model="clientModal.visible" :title="clientModal.title" @ok="clientModal.ok"
|
||||
:confirm-loading="clientModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||
:class="themeSwitcher.currentTheme" :ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
|
||||
<template v-if="isEdit">
|
||||
<a-tag v-if="isExpiry || isTrafficExhausted" color="red"
|
||||
:style="{ marginBottom: '10px', display: 'block', textAlign: 'center' }">Account
|
||||
@@ -13,7 +10,6 @@
|
||||
{{template "form/client"}}
|
||||
</a-modal>
|
||||
<script>
|
||||
|
||||
const clientModal = {
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
@@ -30,12 +26,20 @@
|
||||
delayedStart: false,
|
||||
ok() {
|
||||
if (clientModal.isEdit) {
|
||||
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal.oldClientId);
|
||||
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id, clientModal
|
||||
.oldClientId);
|
||||
} else {
|
||||
ObjectUtil.execute(clientModal.confirm, clientModalApp.client, clientModal.dbInbound.id);
|
||||
}
|
||||
},
|
||||
show({ title = '', okText = '{{ i18n "sure" }}', index = null, dbInbound = null, confirm = () => { }, isEdit = false }) {
|
||||
show({
|
||||
title = '',
|
||||
okText = '{{ i18n "sure" }}',
|
||||
index = null,
|
||||
dbInbound = null,
|
||||
confirm = () => {},
|
||||
isEdit = false
|
||||
}) {
|
||||
this.visible = true;
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
@@ -55,30 +59,41 @@
|
||||
}
|
||||
this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
|
||||
this.confirm = confirm;
|
||||
},
|
||||
},
|
||||
getClientId(protocol, client) {
|
||||
switch (protocol) {
|
||||
case Protocols.TROJAN: return client.password;
|
||||
case Protocols.SHADOWSOCKS: return client.email;
|
||||
case Protocols.HYSTERIA: return client.auth;
|
||||
default: return client.id;
|
||||
case Protocols.TROJAN:
|
||||
return client.password;
|
||||
case Protocols.SHADOWSOCKS:
|
||||
return client.email;
|
||||
case Protocols.HYSTERIA:
|
||||
return client.auth;
|
||||
default:
|
||||
return client.id;
|
||||
}
|
||||
},
|
||||
addClient(inbound, clients) {
|
||||
switch (inbound.protocol) {
|
||||
case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.VMESS());
|
||||
case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
|
||||
case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
|
||||
case Protocols.SHADOWSOCKS: return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method, RandomUtil.randomShadowsocksPassword(inbound.settings.method)));
|
||||
case Protocols.HYSTERIA: return clients.push(new Inbound.HysteriaSettings.Hysteria());
|
||||
default: return null;
|
||||
case Protocols.VMESS:
|
||||
return clients.push(new Inbound.VmessSettings.VMESS());
|
||||
case Protocols.VLESS:
|
||||
return clients.push(new Inbound.VLESSSettings.VLESS());
|
||||
case Protocols.TROJAN:
|
||||
return clients.push(new Inbound.TrojanSettings.Trojan());
|
||||
case Protocols.SHADOWSOCKS:
|
||||
return clients.push(new Inbound.ShadowsocksSettings.Shadowsocks(clients[0].method, RandomUtil
|
||||
.randomShadowsocksPassword(inbound.settings.method)));
|
||||
case Protocols.HYSTERIA:
|
||||
return clients.push(new Inbound.HysteriaSettings.Hysteria());
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
close() {
|
||||
clientModal.visible = false;
|
||||
clientModal.loading(false);
|
||||
},
|
||||
loading(loading=true) {
|
||||
loading(loading = true) {
|
||||
clientModal.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
@@ -110,7 +125,8 @@
|
||||
return true
|
||||
},
|
||||
get isExpiry() {
|
||||
return this.clientModal.isEdit && this.client.expiryTime >0 ? (this.client.expiryTime < new Date().getTime()) : false;
|
||||
return this.clientModal.isEdit && this.client.expiryTime > 0 ? (this.client.expiryTime <
|
||||
new Date().getTime()) : false;
|
||||
},
|
||||
get delayedStart() {
|
||||
return this.clientModal.delayedStart;
|
||||
@@ -150,8 +166,7 @@
|
||||
return;
|
||||
}
|
||||
document.getElementById("clientIPs").value = "";
|
||||
} catch (error) {
|
||||
}
|
||||
} catch (error) {}
|
||||
},
|
||||
resetClientTraffic(email, dbInboundId, iconElement) {
|
||||
this.$confirm({
|
||||
@@ -162,7 +177,8 @@
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: async () => {
|
||||
iconElement.disabled = true;
|
||||
const msg = await HttpUtil.postWithModal('/panel/api/inbounds/' + dbInboundId + '/resetClientTraffic/' + email);
|
||||
const msg = await HttpUtil.postWithModal('/panel/api/inbounds/' +
|
||||
dbInboundId + '/resetClientTraffic/' + email);
|
||||
if (msg.success) {
|
||||
this.clientModal.clientStats.up = 0;
|
||||
this.clientModal.clientStats.down = 0;
|
||||
@@ -173,6 +189,5 @@
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -5,10 +5,12 @@
|
||||
<a-list-item v-for="dns in dnsPresetsDatabase" :style="{ padding: '12px 16px' }">
|
||||
<div class="ant-dns-presets-line">
|
||||
<a-space direction="horizontal" size="small" align="center">
|
||||
<a-tag :color="dns.family ? 'purple' : 'green'">[[ dns.family ? '{{ i18n "pages.xray.dns.dnsPresetFamily" }}' : 'DNS' ]]</a-tag>
|
||||
<a-tag :color="dns.family ? 'purple' : 'green'">[[ dns.family ? '{{ i18n "pages.xray.dns.dnsPresetFamily" }}'
|
||||
: 'DNS' ]]</a-tag>
|
||||
<span class="ant-dns-presets-list-name">[[ dns.name ]]</span>
|
||||
</a-space>
|
||||
<a-button class="ant-dns-presets-install" type="primary" @click="dnsPresetsModal.install(dns.data)">{{ i18n "install" }}</a-button>
|
||||
<a-button class="ant-dns-presets-install" type="primary"
|
||||
@click="dnsPresetsModal.install(dns.data)">{{ i18n "install" }}</a-button>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
@@ -36,8 +38,7 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const dnsPresetsDatabase = [
|
||||
{
|
||||
const dnsPresetsDatabase = [{
|
||||
name: 'Google DNS',
|
||||
family: false,
|
||||
data: [
|
||||
@@ -96,7 +97,11 @@
|
||||
install(selectedPreset) {
|
||||
return ObjectUtil.execute(dnsPresetsModal.selected, selectedPreset);
|
||||
},
|
||||
show({ title = '', selected = (selectedPreset) => { }, isEdit = false }) {
|
||||
show({
|
||||
title = '',
|
||||
selected = (selectedPreset) => {},
|
||||
isEdit = false
|
||||
}) {
|
||||
this.title = title;
|
||||
this.selected = selected;
|
||||
this.visible = true;
|
||||
|
||||
@@ -521,7 +521,8 @@
|
||||
@click="copy(infoModal.wireguardLinks[index])"></a-button>
|
||||
</a-tooltip>
|
||||
</tr-info-title>
|
||||
<code :style="{ display: 'block', whiteSpace: 'normal', wordBreak: 'break-all' }">[[ infoModal.wireguardLinks[index] ]]</code>
|
||||
<code :style="{ display: 'block', whiteSpace: 'normal', wordBreak: 'break-all' }">[[
|
||||
infoModal.wireguardLinks[index] ]]</code>
|
||||
</tr-info-row>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -534,7 +535,10 @@
|
||||
function refreshIPs(email) {
|
||||
return HttpUtil.post(`/panel/api/inbounds/clientIps/${email}`).then((msg) => {
|
||||
if (!msg.success) {
|
||||
return { text: 'No IP Record', array: [] };
|
||||
return {
|
||||
text: 'No IP Record',
|
||||
array: []
|
||||
};
|
||||
}
|
||||
|
||||
const formatIpRecord = (record) => {
|
||||
@@ -574,7 +578,10 @@
|
||||
try {
|
||||
ips = JSON.parse(ips);
|
||||
} catch (e) {
|
||||
return { text: String(ips), array: [String(ips)] };
|
||||
return {
|
||||
text: String(ips),
|
||||
array: [String(ips)]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,20 +593,32 @@
|
||||
// New format or object array
|
||||
if (Array.isArray(ips) && ips.length > 0 && typeof ips[0] === 'object') {
|
||||
const result = ips.map((item) => formatIpRecord(item)).filter(Boolean);
|
||||
return { text: result.join(' | '), array: result };
|
||||
return {
|
||||
text: result.join(' | '),
|
||||
array: result
|
||||
};
|
||||
}
|
||||
|
||||
// Old format - simple array of IPs
|
||||
if (Array.isArray(ips) && ips.length > 0) {
|
||||
const result = ips.map((ip) => String(ip));
|
||||
return { text: result.join(', '), array: result };
|
||||
return {
|
||||
text: result.join(', '),
|
||||
array: result
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback for any other format
|
||||
return { text: String(ips), array: [String(ips)] };
|
||||
return {
|
||||
text: String(ips),
|
||||
array: [String(ips)]
|
||||
};
|
||||
|
||||
} catch (e) {
|
||||
return { text: 'Error loading IPs', array: [] };
|
||||
return {
|
||||
text: 'Error loading IPs',
|
||||
array: []
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -626,7 +645,8 @@
|
||||
this.dbInbound = new DBInbound(dbInbound);
|
||||
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) || null) : null;
|
||||
this.clientStats = this.inbound.clients ? (this.dbInbound.clientStats.find(row => row.email === this
|
||||
.clientSettings.email) || null) : null;
|
||||
|
||||
if (
|
||||
[
|
||||
@@ -752,7 +772,8 @@
|
||||
return ColorUtils.usageColor(stats.up + stats.down, app.trafficDiff, stats.total);
|
||||
},
|
||||
getRemStats() {
|
||||
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats.down;
|
||||
remained = this.infoModal.clientStats.total - this.infoModal.clientStats.up - this.infoModal.clientStats
|
||||
.down;
|
||||
return remained > 0 ? SizeFormatter.sizeFormat(remained) : '-';
|
||||
},
|
||||
refreshIPs() {
|
||||
@@ -775,7 +796,7 @@
|
||||
this.infoModal.clientIps = 'No IP Record';
|
||||
this.infoModal.clientIpsArray = [];
|
||||
})
|
||||
.catch(() => { });
|
||||
.catch(() => {});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
okText = '{{ i18n "sure" }}',
|
||||
inbound = null,
|
||||
dbInbound = null,
|
||||
confirm = (inbound, dbInbound) => { },
|
||||
confirm = (inbound, dbInbound) => {},
|
||||
isEdit = false,
|
||||
}) {
|
||||
this.title = title;
|
||||
@@ -127,17 +127,17 @@
|
||||
get client() {
|
||||
return inModal.inbound &&
|
||||
inModal.inbound.clients &&
|
||||
inModal.inbound.clients.length > 0
|
||||
? inModal.inbound.clients[0]
|
||||
: null;
|
||||
inModal.inbound.clients.length > 0 ?
|
||||
inModal.inbound.clients[0] :
|
||||
null;
|
||||
},
|
||||
get datepicker() {
|
||||
return app.datepicker;
|
||||
},
|
||||
get delayedExpireDays() {
|
||||
return this.client && this.client.expiryTime < 0
|
||||
? this.client.expiryTime / -86400000
|
||||
: 0;
|
||||
return this.client && this.client.expiryTime < 0 ?
|
||||
this.client.expiryTime / -86400000 :
|
||||
0;
|
||||
},
|
||||
set delayedExpireDays(days) {
|
||||
this.client.expiryTime = -86400000 * days;
|
||||
@@ -147,14 +147,12 @@
|
||||
},
|
||||
set externalProxy(value) {
|
||||
if (value) {
|
||||
inModal.inbound.stream.externalProxy = [
|
||||
{
|
||||
forceTls: "same",
|
||||
dest: window.location.hostname,
|
||||
port: inModal.inbound.port,
|
||||
remark: "",
|
||||
},
|
||||
];
|
||||
inModal.inbound.stream.externalProxy = [{
|
||||
forceTls: "same",
|
||||
dest: window.location.hostname,
|
||||
port: inModal.inbound.port,
|
||||
remark: "",
|
||||
}, ];
|
||||
} else {
|
||||
inModal.inbound.stream.externalProxy = [];
|
||||
}
|
||||
@@ -182,8 +180,8 @@
|
||||
) {
|
||||
const hasVisionFlow = inModal.inbound.settings.vlesses.some(
|
||||
(c) =>
|
||||
c.flow === "xtls-rprx-vision" ||
|
||||
c.flow === "xtls-rprx-vision-udp443",
|
||||
c.flow === "xtls-rprx-vision" ||
|
||||
c.flow === "xtls-rprx-vision-udp443",
|
||||
);
|
||||
if (
|
||||
hasVisionFlow &&
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
{{define "modals/nordModal"}}
|
||||
<a-modal id="nord-modal" v-model="nordModal.visible" title="NordVPN NordLynx"
|
||||
:confirm-loading="nordModal.confirmLoading" :closable="true" :mask-closable="true"
|
||||
:footer="null" :class="themeSwitcher.currentTheme">
|
||||
:confirm-loading="nordModal.confirmLoading" :closable="true" :mask-closable="true" :footer="null"
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<template v-if="nordModal.nordData == null">
|
||||
<a-tabs default-active-key="token" :class="themeSwitcher.currentTheme">
|
||||
<a-tab-pane key="token" tab='{{ i18n "pages.xray.outbound.accessToken" }}'>
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:18} }" :style="{ marginTop: '20px' }">
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:18} }"
|
||||
:style="{ marginTop: '20px' }">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.accessToken" }}'>
|
||||
<a-input v-model="nordModal.token" placeholder='{{ i18n "pages.xray.outbound.accessToken" }}'></a-input>
|
||||
<a-input v-model="nordModal.token"
|
||||
placeholder='{{ i18n "pages.xray.outbound.accessToken" }}'></a-input>
|
||||
<div :style="{ marginTop: '10px' }">
|
||||
<a-button type="primary" icon="login" @click="login()" :loading="nordModal.confirmLoading">{{ i18n "login" }}</a-button>
|
||||
<a-button type="primary" icon="login" @click="login()"
|
||||
:loading="nordModal.confirmLoading">{{ i18n "login" }}</a-button>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="key" tab='{{ i18n "pages.xray.outbound.privateKey" }}'>
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:18} }" :style="{ marginTop: '20px' }">
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:18} }"
|
||||
:style="{ marginTop: '20px' }">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.privateKey" }}'>
|
||||
<a-input v-model="nordModal.manualKey" placeholder='{{ i18n "pages.xray.outbound.privateKey" }}'></a-input>
|
||||
<a-input v-model="nordModal.manualKey"
|
||||
placeholder='{{ i18n "pages.xray.outbound.privateKey" }}'></a-input>
|
||||
<div :style="{ marginTop: '10px' }">
|
||||
<a-button type="primary" icon="save" @click="saveKey()" :loading="nordModal.confirmLoading">{{ i18n "save" }}</a-button>
|
||||
<a-button type="primary" icon="save" @click="saveKey()"
|
||||
:loading="nordModal.confirmLoading">{{ i18n "save" }}</a-button>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@@ -39,7 +45,8 @@
|
||||
</table>
|
||||
<a-button @click="logout" :loading="nordModal.confirmLoading" type="danger">{{ i18n "logout" }}</a-button>
|
||||
<a-divider :style="{ margin: '0' }">{{ i18n "pages.xray.outbound.settings" }}</a-divider>
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:18} }" :style="{ marginTop: '10px' }">
|
||||
<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:18} }"
|
||||
:style="{ marginTop: '10px' }">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.country" }}'>
|
||||
<a-select v-model="nordModal.countryId" @change="fetchServers" show-search option-filter-prop="label">
|
||||
<a-select-option v-for="c in nordModal.countries" :key="c.id" :value="c.id" :label="c.name">
|
||||
@@ -69,11 +76,13 @@
|
||||
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<template v-if="nordOutboundIndex>=0">
|
||||
<a-tag color="green" :style="{ lineHeight: '31px' }">{{ i18n "enabled" }}</a-tag>
|
||||
<a-button @click="resetOutbound" :loading="nordModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
|
||||
<a-button @click="resetOutbound" :loading="nordModal.confirmLoading"
|
||||
type="danger">{{ i18n "reset" }}</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-tag color="orange" :style="{ lineHeight: '31px' }">{{ i18n "disabled" }}</a-tag>
|
||||
<a-button @click="addOutbound" :disabled="!nordModal.serverId" :loading="nordModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||
<a-button @click="addOutbound" :disabled="!nordModal.serverId" :loading="nordModal.confirmLoading"
|
||||
type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||
</template>
|
||||
</a-form>
|
||||
</template>
|
||||
@@ -115,7 +124,9 @@
|
||||
},
|
||||
async login() {
|
||||
this.loading(true);
|
||||
const msg = await HttpUtil.post('/panel/xray/nord/reg', { token: this.token });
|
||||
const msg = await HttpUtil.post('/panel/xray/nord/reg', {
|
||||
token: this.token
|
||||
});
|
||||
if (msg.success) {
|
||||
this.nordData = JSON.parse(msg.obj);
|
||||
await this.fetchCountries();
|
||||
@@ -124,7 +135,9 @@
|
||||
},
|
||||
async saveKey() {
|
||||
this.loading(true);
|
||||
const msg = await HttpUtil.post('/panel/xray/nord/setKey', { key: this.manualKey });
|
||||
const msg = await HttpUtil.post('/panel/xray/nord/setKey', {
|
||||
key: this.manualKey
|
||||
});
|
||||
if (msg.success) {
|
||||
this.nordData = JSON.parse(msg.obj);
|
||||
await this.fetchCountries();
|
||||
@@ -160,7 +173,9 @@
|
||||
this.cities = [];
|
||||
this.serverId = null;
|
||||
this.cityId = null;
|
||||
const msg = await HttpUtil.post('/panel/xray/nord/servers', { countryId: this.countryId });
|
||||
const msg = await HttpUtil.post('/panel/xray/nord/servers', {
|
||||
countryId: this.countryId
|
||||
});
|
||||
if (msg.success) {
|
||||
const data = JSON.parse(msg.obj);
|
||||
const locations = data.locations || [];
|
||||
@@ -173,7 +188,7 @@
|
||||
}
|
||||
});
|
||||
this.cities = Array.from(citiesMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
|
||||
this.servers = (data.servers || []).map(s => {
|
||||
const firstLocId = (s.location_ids || [])[0];
|
||||
const city = locToCity[firstLocId];
|
||||
@@ -195,7 +210,7 @@
|
||||
addOutbound() {
|
||||
const server = this.servers.find(s => s.id === this.serverId);
|
||||
if (!server) return;
|
||||
|
||||
|
||||
const tech = server.technologies.find(t => t.id === 35);
|
||||
const publicKey = tech.metadata.find(m => m.name === 'public_key').value;
|
||||
|
||||
@@ -221,7 +236,7 @@
|
||||
resetOutbound(index) {
|
||||
const server = this.servers.find(s => s.id === this.serverId);
|
||||
if (!server || index === -1) return;
|
||||
|
||||
|
||||
const tech = server.technologies.find(t => t.id === 35);
|
||||
const publicKey = tech.metadata.find(m => m.name === 'public_key').value;
|
||||
|
||||
@@ -242,7 +257,7 @@
|
||||
}
|
||||
};
|
||||
app.templateSettings.outbounds[index] = outbound;
|
||||
|
||||
|
||||
// Sync routing rules
|
||||
app.templateSettings.routing.rules.forEach(r => {
|
||||
if (r.outboundTag === oldTag) {
|
||||
@@ -262,7 +277,8 @@
|
||||
},
|
||||
delRouting() {
|
||||
if (app.templateSettings && app.templateSettings.routing) {
|
||||
app.templateSettings.routing.rules = app.templateSettings.routing.rules.filter(r => !r.outboundTag.startsWith("nord-"));
|
||||
app.templateSettings.routing.rules = app.templateSettings.routing.rules.filter(r => !r.outboundTag
|
||||
.startsWith("nord-"));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -276,10 +292,14 @@
|
||||
methods: {
|
||||
login: () => nordModal.login(),
|
||||
saveKey: () => nordModal.saveKey(),
|
||||
logout() { nordModal.logout(this.nordOutboundIndex) },
|
||||
logout() {
|
||||
nordModal.logout(this.nordOutboundIndex)
|
||||
},
|
||||
fetchServers: () => nordModal.fetchServers(),
|
||||
addOutbound: () => nordModal.addOutbound(),
|
||||
resetOutbound() { nordModal.resetOutbound(this.nordOutboundIndex) },
|
||||
resetOutbound() {
|
||||
nordModal.resetOutbound(this.nordOutboundIndex)
|
||||
},
|
||||
onCityChange() {
|
||||
if (this.filteredServers.length > 0) {
|
||||
this.nordModal.serverId = this.filteredServers[0].id;
|
||||
@@ -290,8 +310,9 @@
|
||||
},
|
||||
computed: {
|
||||
nordOutboundIndex: {
|
||||
get: function () {
|
||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag.startsWith("nord-")) : -1;
|
||||
get: function() {
|
||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag
|
||||
.startsWith("nord-")) : -1;
|
||||
}
|
||||
},
|
||||
filteredServers: function() {
|
||||
@@ -303,4 +324,4 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -1,17 +1,13 @@
|
||||
{{define "modals/promptModal"}}
|
||||
<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
|
||||
:closable="true" @ok="promptModal.ok" :mask-closable="false"
|
||||
:confirm-loading="promptModal.confirmLoading"
|
||||
:ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-input id="prompt-modal-input" :type="promptModal.type"
|
||||
v-model="promptModal.value"
|
||||
:autosize="{minRows: 10, maxRows: 20}"
|
||||
@keydown.enter.native="promptModal.keyEnter"
|
||||
@keydown.ctrl.83="promptModal.ctrlS"></a-input>
|
||||
<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title" :closable="true"
|
||||
@ok="promptModal.ok" :mask-closable="false" :confirm-loading="promptModal.confirmLoading"
|
||||
:ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-input id="prompt-modal-input" :type="promptModal.type" v-model="promptModal.value"
|
||||
:autosize="{minRows: 10, maxRows: 20}" @keydown.enter.native="promptModal.keyEnter"
|
||||
@keydown.ctrl.83="promptModal.ctrlS"></a-input>
|
||||
</a-modal>
|
||||
|
||||
<script>
|
||||
|
||||
const promptModal = {
|
||||
title: '',
|
||||
type: '',
|
||||
@@ -55,7 +51,7 @@
|
||||
close() {
|
||||
this.visible = false;
|
||||
},
|
||||
loading(loading=true) {
|
||||
loading(loading = true) {
|
||||
this.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
@@ -66,6 +62,5 @@
|
||||
promptModal: promptModal,
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -57,39 +57,44 @@
|
||||
border-radius: 1rem;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* QR code transition effects */
|
||||
.qr-cv {
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.qr-transition-enter-active, .qr-transition-leave-active {
|
||||
|
||||
.qr-transition-enter-active,
|
||||
.qr-transition-leave-active {
|
||||
transition: opacity 0.3s, transform 0.3s;
|
||||
}
|
||||
|
||||
.qr-transition-enter, .qr-transition-leave-to {
|
||||
|
||||
.qr-transition-enter,
|
||||
.qr-transition-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.qr-transition-enter-to, .qr-transition-leave {
|
||||
|
||||
.qr-transition-enter-to,
|
||||
.qr-transition-leave {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
|
||||
.qr-flash {
|
||||
animation: qr-flash-animation 0.6s;
|
||||
}
|
||||
|
||||
|
||||
@keyframes qr-flash-animation {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
@@ -105,7 +110,7 @@
|
||||
qrcodes: [],
|
||||
visible: false,
|
||||
subId: '',
|
||||
show: function (title = '', dbInbound, client) {
|
||||
show: function(title = '', dbInbound, client) {
|
||||
this.title = title;
|
||||
this.dbInbound = dbInbound;
|
||||
this.inbound = dbInbound.toInbound();
|
||||
@@ -135,7 +140,7 @@
|
||||
}
|
||||
this.visible = true;
|
||||
},
|
||||
close: function () {
|
||||
close: function() {
|
||||
this.visible = false;
|
||||
},
|
||||
};
|
||||
@@ -159,7 +164,7 @@
|
||||
console.error("Failed to get status:", e);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
toggleIPv4(index) {
|
||||
const row = qrModal.qrcodes[index];
|
||||
row.useIPv4 = !row.useIPv4;
|
||||
@@ -170,13 +175,13 @@
|
||||
if (!this.serverStatus || !this.serverStatus.publicIP) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (row.useIPv4 && this.serverStatus.publicIP.ipv4) {
|
||||
// Replace the hostname or IP in the link with the IPv4 address
|
||||
const originalLink = row.originalLink;
|
||||
const url = new URL(originalLink);
|
||||
const ipv4 = this.serverStatus.publicIP.ipv4;
|
||||
|
||||
|
||||
if (qrModal.inbound.protocol == Protocols.WIREGUARD) {
|
||||
// Special handling for WireGuard config
|
||||
const endpointRegex = /Endpoint = ([^:]+):(\d+)/;
|
||||
@@ -196,19 +201,19 @@
|
||||
// Restore original link
|
||||
row.link = row.originalLink;
|
||||
}
|
||||
|
||||
|
||||
// Update QR code with transition effect
|
||||
const canvasElement = document.querySelector('#qrCode-' + index);
|
||||
if (canvasElement) {
|
||||
// Add flash animation class
|
||||
canvasElement.classList.add('qr-flash');
|
||||
|
||||
|
||||
// Remove the class after animation completes
|
||||
setTimeout(() => {
|
||||
canvasElement.classList.remove('qr-flash');
|
||||
}, 600);
|
||||
}
|
||||
|
||||
|
||||
this.setQrCode("qrCode-" + index, row.link);
|
||||
},
|
||||
copy(content) {
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
fileName: '',
|
||||
qrcode: null,
|
||||
visible: false,
|
||||
show: function (title = '', content = '', fileName = '') {
|
||||
show: function(title = '', content = '', fileName = '') {
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
this.fileName = fileName;
|
||||
this.visible = true;
|
||||
},
|
||||
copy: function (content = '') {
|
||||
copy: function(content = '') {
|
||||
ClipboardManager
|
||||
.copyText(content)
|
||||
.then(() => {
|
||||
@@ -35,7 +35,7 @@
|
||||
this.close();
|
||||
})
|
||||
},
|
||||
close: function () {
|
||||
close: function() {
|
||||
this.visible = false;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -55,12 +55,12 @@
|
||||
|
||||
twoFactorModal.close()
|
||||
},
|
||||
show: function ({
|
||||
show: function({
|
||||
title = '',
|
||||
description = '',
|
||||
token = '',
|
||||
type = 'set',
|
||||
confirm = (success) => { }
|
||||
confirm = (success) => {}
|
||||
}) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
@@ -78,7 +78,7 @@
|
||||
secret: twoFactorModal.token,
|
||||
});
|
||||
},
|
||||
close: function () {
|
||||
close: function() {
|
||||
twoFactorModal.enteredCode = "";
|
||||
twoFactorModal.visible = false;
|
||||
},
|
||||
@@ -91,34 +91,34 @@
|
||||
twoFactorModal: twoFactorModal,
|
||||
},
|
||||
updated() {
|
||||
if (
|
||||
this.twoFactorModal.visible &&
|
||||
this.twoFactorModal.type === 'set' &&
|
||||
document.getElementById('twofactor-qrcode')
|
||||
) {
|
||||
this.setQrCode('twofactor-qrcode', this.twoFactorModal.totpObject.toString());
|
||||
}
|
||||
if (
|
||||
this.twoFactorModal.visible &&
|
||||
this.twoFactorModal.type === 'set' &&
|
||||
document.getElementById('twofactor-qrcode')
|
||||
) {
|
||||
this.setQrCode('twofactor-qrcode', this.twoFactorModal.totpObject.toString());
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setQrCode(elementId, content) {
|
||||
new QRious({
|
||||
element: document.getElementById(elementId),
|
||||
size: 200,
|
||||
value: content,
|
||||
background: 'white',
|
||||
backgroundAlpha: 0,
|
||||
foreground: 'black',
|
||||
padding: 2,
|
||||
level: 'L'
|
||||
});
|
||||
},
|
||||
copy(content) {
|
||||
ClipboardManager
|
||||
.copyText(content)
|
||||
.then(() => {
|
||||
app.$message.success('{{ i18n "copied" }}')
|
||||
})
|
||||
},
|
||||
setQrCode(elementId, content) {
|
||||
new QRious({
|
||||
element: document.getElementById(elementId),
|
||||
size: 200,
|
||||
value: content,
|
||||
background: 'white',
|
||||
backgroundAlpha: 0,
|
||||
foreground: 'black',
|
||||
padding: 2,
|
||||
level: 'L'
|
||||
});
|
||||
},
|
||||
copy(content) {
|
||||
ClipboardManager
|
||||
.copyText(content)
|
||||
.then(() => {
|
||||
app.$message.success('{{ i18n "copied" }}')
|
||||
})
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{{define "modals/warpModal"}}
|
||||
<a-modal id="warp-modal" v-model="warpModal.visible" title="Cloudflare WARP"
|
||||
:confirm-loading="warpModal.confirmLoading" :closable="true" :mask-closable="true"
|
||||
:footer="null" :class="themeSwitcher.currentTheme">
|
||||
<a-modal id="warp-modal" v-model="warpModal.visible" title="Cloudflare WARP" :confirm-loading="warpModal.confirmLoading"
|
||||
:closable="true" :mask-closable="true" :footer="null" :class="themeSwitcher.currentTheme">
|
||||
<template v-if="ObjectUtil.isEmpty(warpModal.warpData)">
|
||||
<a-button icon="api" @click="register" :loading="warpModal.confirmLoading">{{ i18n "create" }}</a-button>
|
||||
</template>
|
||||
@@ -81,11 +80,13 @@
|
||||
<a-form :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<template v-if="warpOutboundIndex>=0">
|
||||
<a-tag color="green" :style="{ lineHeight: '31px' }">{{ i18n "enabled" }}</a-tag>
|
||||
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading" type="danger">{{ i18n "reset" }}</a-button>
|
||||
<a-button @click="resetOutbound" :loading="warpModal.confirmLoading"
|
||||
type="danger">{{ i18n "reset" }}</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-tag color="orange" :style="{ lineHeight: '31px' }">{{ i18n "disabled" }}</a-tag>
|
||||
<a-button @click="addOutbound" :loading="warpModal.confirmLoading" type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||
<a-button @click="addOutbound" :loading="warpModal.confirmLoading"
|
||||
type="primary">{{ i18n "pages.xray.outbound.addOutbound" }}</a-button>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@@ -93,7 +94,6 @@
|
||||
</template>
|
||||
</a-modal>
|
||||
<script>
|
||||
|
||||
const warpModal = {
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
@@ -188,7 +188,9 @@
|
||||
},
|
||||
async updateLicense(l) {
|
||||
warpModal.loading(true);
|
||||
const msg = await HttpUtil.post('/panel/xray/warp/license', { license: l });
|
||||
const msg = await HttpUtil.post('/panel/xray/warp/license', {
|
||||
license: l
|
||||
});
|
||||
if (msg.success) {
|
||||
warpModal.warpData = JSON.parse(msg.obj);
|
||||
warpModal.warpConfig = null;
|
||||
@@ -235,12 +237,12 @@
|
||||
},
|
||||
computed: {
|
||||
warpOutboundIndex: {
|
||||
get: function () {
|
||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag == 'warp') : -1;
|
||||
get: function() {
|
||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag ==
|
||||
'warp') : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -1,15 +1,7 @@
|
||||
{{define "modals/balancerModal"}}
|
||||
<a-modal
|
||||
id="balancer-modal"
|
||||
v-model="balancerModal.visible"
|
||||
:title="balancerModal.title"
|
||||
@ok="balancerModal.ok"
|
||||
:confirm-loading="balancerModal.confirmLoading"
|
||||
:ok-button-props="{ props: { disabled: !balancerModal.isValid } }"
|
||||
:closable="true"
|
||||
:mask-closable="false"
|
||||
:ok-text="balancerModal.okText"
|
||||
cancel-text='{{ i18n "close" }}'
|
||||
<a-modal id="balancer-modal" v-model="balancerModal.visible" :title="balancerModal.title" @ok="balancerModal.ok"
|
||||
:confirm-loading="balancerModal.confirmLoading" :ok-button-props="{ props: { disabled: !balancerModal.isValid } }"
|
||||
:closable="true" :mask-closable="false" :ok-text="balancerModal.okText" cancel-text='{{ i18n "close" }}'
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.xray.balancer.tag" }}' has-feedback
|
||||
@@ -35,7 +27,8 @@
|
||||
<a-form-item label="Fallback">
|
||||
<a-select v-model="balancerModal.balancer.fallbackTag" clearable
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="tag in [ '', ...balancerModal.outboundTags]" :value="tag">[[ tag ]]</a-select-option>
|
||||
<a-select-option v-for="tag in [ '', ...balancerModal.outboundTags]" :value="tag">[[ tag
|
||||
]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</table>
|
||||
@@ -58,7 +51,7 @@
|
||||
fallbackTag: ''
|
||||
},
|
||||
outboundTags: [],
|
||||
balancerTags:[],
|
||||
balancerTags: [],
|
||||
ok() {
|
||||
if (balancerModal.balancer.selector.length == 0) {
|
||||
balancerModal.emptySelector = true;
|
||||
@@ -67,7 +60,14 @@
|
||||
balancerModal.emptySelector = false;
|
||||
ObjectUtil.execute(balancerModal.confirm, balancerModal.balancer);
|
||||
},
|
||||
show({ title = '', okText = '{{ i18n "sure" }}', balancerTags = [], balancer, confirm = (balancer) => { }, isEdit = false }) {
|
||||
show({
|
||||
title = '',
|
||||
okText = '{{ i18n "sure" }}',
|
||||
balancerTags = [],
|
||||
balancer,
|
||||
confirm = (balancer) => {},
|
||||
isEdit = false
|
||||
}) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
@@ -83,7 +83,8 @@
|
||||
};
|
||||
}
|
||||
this.balancerTags = balancerTags.filter((tag) => tag != balancer.tag);
|
||||
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag);
|
||||
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj =>
|
||||
obj.tag);
|
||||
this.isEdit = isEdit;
|
||||
this.check();
|
||||
this.checkSelector();
|
||||
@@ -92,7 +93,7 @@
|
||||
this.visible = false;
|
||||
this.loading(false);
|
||||
},
|
||||
loading(loading=true) {
|
||||
loading(loading = true) {
|
||||
this.confirmLoading = loading;
|
||||
},
|
||||
check() {
|
||||
@@ -115,9 +116,7 @@
|
||||
data: {
|
||||
balancerModal: balancerModal
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {}
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -75,15 +75,19 @@
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
dnsServer: { ...defaultDnsObject },
|
||||
dnsServer: {
|
||||
...defaultDnsObject
|
||||
},
|
||||
ok() {
|
||||
ObjectUtil.execute(dnsModal.confirm, { ...dnsModal.dnsServer });
|
||||
ObjectUtil.execute(dnsModal.confirm, {
|
||||
...dnsModal.dnsServer
|
||||
});
|
||||
},
|
||||
show({
|
||||
title = '',
|
||||
okText = '{{ i18n "confirm" }}',
|
||||
dnsServer,
|
||||
confirm = (dnsServer) => { },
|
||||
confirm = (dnsServer) => {},
|
||||
isEdit = false
|
||||
}) {
|
||||
this.title = title;
|
||||
@@ -95,7 +99,9 @@
|
||||
if (isEdit) {
|
||||
switch (typeof dnsServer) {
|
||||
case 'string':
|
||||
const dnsObj = { ...defaultDnsObject };
|
||||
const dnsObj = {
|
||||
...defaultDnsObject
|
||||
};
|
||||
|
||||
dnsObj.address = dnsServer;
|
||||
|
||||
@@ -106,7 +112,9 @@
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.dnsServer = { ...defaultDnsObject };
|
||||
this.dnsServer = {
|
||||
...defaultDnsObject
|
||||
};
|
||||
|
||||
this.dnsServer.domains = [];
|
||||
this.dnsServer.expectIPs = [];
|
||||
|
||||
@@ -23,11 +23,19 @@
|
||||
okText: '{{ i18n "confirm" }}',
|
||||
isEdit: false,
|
||||
confirm: null,
|
||||
fakeDns: { ...fakednsDefaultData },
|
||||
fakeDns: {
|
||||
...fakednsDefaultData
|
||||
},
|
||||
ok() {
|
||||
ObjectUtil.execute(fakednsModal.confirm, fakednsModal.fakeDns);
|
||||
},
|
||||
show({ title = '', okText = '{{ i18n "confirm" }}', fakeDns, confirm = (fakeDns) => { }, isEdit = false }) {
|
||||
show({
|
||||
title = '',
|
||||
okText = '{{ i18n "confirm" }}',
|
||||
fakeDns,
|
||||
confirm = (fakeDns) => {},
|
||||
isEdit = false
|
||||
}) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
@@ -35,7 +43,9 @@
|
||||
if (isEdit) {
|
||||
this.fakeDns = fakeDns;
|
||||
} else {
|
||||
this.fakeDns = { ...fakednsDefaultData }
|
||||
this.fakeDns = {
|
||||
...fakednsDefaultData
|
||||
}
|
||||
}
|
||||
this.isEdit = isEdit;
|
||||
},
|
||||
@@ -51,6 +61,5 @@
|
||||
fakednsModal: fakednsModal,
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -1,12 +1,11 @@
|
||||
{{define "modals/outModal"}}
|
||||
<a-modal id="out-modal" v-model="outModal.visible" :title="outModal.title" @ok="outModal.ok"
|
||||
:confirm-loading="outModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||
:ok-button-props="{ props: { disabled: !outModal.isValid } }" :style="{ overflow: 'hidden' }"
|
||||
:ok-text="outModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
{{template "form/outbound"}}
|
||||
:confirm-loading="outModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||
:ok-button-props="{ props: { disabled: !outModal.isValid } }" :style="{ overflow: 'hidden' }"
|
||||
:ok-text="outModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
{{template "form/outbound"}}
|
||||
</a-modal>
|
||||
<script>
|
||||
|
||||
const outModal = {
|
||||
title: '',
|
||||
visible: false,
|
||||
@@ -25,7 +24,14 @@
|
||||
ok() {
|
||||
ObjectUtil.execute(outModal.confirm, outModal.outbound.toJson());
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', outbound, confirm=(outbound)=>{}, isEdit=false, tags=[] }) {
|
||||
show({
|
||||
title = '',
|
||||
okText = '{{ i18n "sure" }}',
|
||||
outbound,
|
||||
confirm = (outbound) => {},
|
||||
isEdit = false,
|
||||
tags = []
|
||||
}) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
@@ -42,11 +48,11 @@
|
||||
outModal.visible = false;
|
||||
outModal.loading(false);
|
||||
},
|
||||
loading(loading=true) {
|
||||
loading(loading = true) {
|
||||
outModal.confirmLoading = loading;
|
||||
},
|
||||
check(){
|
||||
if(outModal.outbound.tag == '' || outModal.tags.includes(outModal.outbound.tag)){
|
||||
check() {
|
||||
if (outModal.outbound.tag == '' || outModal.tags.includes(outModal.outbound.tag)) {
|
||||
this.duplicateTag = true;
|
||||
this.isValid = false;
|
||||
} else {
|
||||
@@ -56,25 +62,25 @@
|
||||
},
|
||||
toggleJson(jsonTab) {
|
||||
textAreaObj = document.getElementById('outboundJson');
|
||||
if(jsonTab){
|
||||
if(this.cm != null) {
|
||||
this.cm.toTextArea();
|
||||
this.cm=null;
|
||||
if (jsonTab) {
|
||||
if (this.cm != null) {
|
||||
this.cm.toTextArea();
|
||||
this.cm = null;
|
||||
}
|
||||
textAreaObj.value = JSON.stringify(this.outbound.toJson(), null, 2);
|
||||
this.cm = CodeMirror.fromTextArea(textAreaObj, app.cmOptions);
|
||||
this.cm.on('change',editor => {
|
||||
this.cm.on('change', editor => {
|
||||
value = editor.getValue();
|
||||
if(this.isJsonString(value)){
|
||||
if (this.isJsonString(value)) {
|
||||
this.outbound = Outbound.fromJson(JSON.parse(value));
|
||||
this.check();
|
||||
}
|
||||
});
|
||||
this.activeKey = '2';
|
||||
} else {
|
||||
if(this.cm != null) {
|
||||
this.cm.toTextArea();
|
||||
this.cm=null;
|
||||
if (this.cm != null) {
|
||||
this.cm.toTextArea();
|
||||
this.cm = null;
|
||||
}
|
||||
this.activeKey = '1';
|
||||
}
|
||||
@@ -100,20 +106,21 @@
|
||||
},
|
||||
methods: {
|
||||
streamNetworkChange() {
|
||||
if (this.outModal.outbound.protocol == Protocols.VLESS && !outModal.outbound.canEnableTlsFlow()) {
|
||||
if (this.outModal.outbound.protocol == Protocols.VLESS && !outModal.outbound
|
||||
.canEnableTlsFlow()) {
|
||||
delete this.outModal.outbound.settings.flow;
|
||||
}
|
||||
},
|
||||
canEnableTls() {
|
||||
return this.outModal.outbound.canEnableTls();
|
||||
},
|
||||
convertLink(){
|
||||
convertLink() {
|
||||
newOutbound = Outbound.fromLink(outModal.link);
|
||||
if(newOutbound){
|
||||
if (newOutbound) {
|
||||
this.outModal.outbound = newOutbound;
|
||||
this.outModal.toggleJson(true);
|
||||
this.outModal.check();
|
||||
this.$message.success('Link imported successfully...');
|
||||
this.$message.success('Link imported successfully...');
|
||||
outModal.link = '';
|
||||
} else {
|
||||
this.$message.error('Wrong Link!');
|
||||
@@ -122,6 +129,5 @@
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -1,7 +1,7 @@
|
||||
{{define "modals/reverseModal"}}
|
||||
<a-modal id="reverse-modal" v-model="reverseModal.visible" :title="reverseModal.title" @ok="reverseModal.ok"
|
||||
:confirm-loading="reverseModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||
:ok-text="reverseModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
:confirm-loading="reverseModal.confirmLoading" :closable="true" :mask-closable="false"
|
||||
:ok-text="reverseModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
<a-select v-model="reverseModal.reverse.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
@@ -15,26 +15,24 @@
|
||||
<a-input v-model.trim="reverseModal.reverse.domain"></a-input>
|
||||
</a-form-item>
|
||||
<template v-if="reverseModal.reverse.type=='bridge'">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.intercon" }}'>
|
||||
<a-select v-model="reverseModal.rules[0].outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.outbound" }}'>
|
||||
<a-select v-model="reverseModal.rules[1].outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.intercon" }}'>
|
||||
<a-select v-model="reverseModal.rules[0].outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.outbound" }}'>
|
||||
<a-select v-model="reverseModal.rules[1].outboundTag" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="x in reverseModal.outboundTags" :value="x">[[ x ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.intercon" }}'>
|
||||
<a-checkbox-group
|
||||
v-model="reverseModal.rules[0].inboundTag"
|
||||
<a-checkbox-group v-model="reverseModal.rules[0].inboundTag"
|
||||
:options="reverseModal.inboundTags"></a-checkbox-group>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.xray.rules.inbound" }}'>
|
||||
<a-checkbox-group
|
||||
v-model="reverseModal.rules[1].inboundTag"
|
||||
<a-checkbox-group v-model="reverseModal.rules[1].inboundTag"
|
||||
:options="reverseModal.inboundTags"></a-checkbox-group>
|
||||
</a-form-item>
|
||||
</template>
|
||||
@@ -53,9 +51,14 @@
|
||||
type: "",
|
||||
domain: ""
|
||||
},
|
||||
rules: [
|
||||
{ outboundTag: '', inboundTag: []},
|
||||
{ outboundTag: '', inboundTag: []}
|
||||
rules: [{
|
||||
outboundTag: '',
|
||||
inboundTag: []
|
||||
},
|
||||
{
|
||||
outboundTag: '',
|
||||
inboundTag: []
|
||||
}
|
||||
],
|
||||
inboundTags: [],
|
||||
outboundTags: [],
|
||||
@@ -64,7 +67,7 @@
|
||||
reverseModal.rules[0].type = 'field';
|
||||
reverseModal.rules[1].type = 'field';
|
||||
|
||||
if(reverseModal.reverse.type == 'bridge'){
|
||||
if (reverseModal.reverse.type == 'bridge') {
|
||||
reverseModal.rules[0].inboundTag = [reverseModal.reverse.tag];
|
||||
reverseModal.rules[1].inboundTag = [reverseModal.reverse.tag];
|
||||
} else {
|
||||
@@ -73,22 +76,36 @@
|
||||
}
|
||||
ObjectUtil.execute(reverseModal.confirm, reverseModal.reverse, reverseModal.rules);
|
||||
},
|
||||
show({ title='', okText='{{ i18n "sure" }}', reverse, rules, confirm=(reverse, rules)=>{}, isEdit=false }) {
|
||||
show({
|
||||
title = '',
|
||||
okText = '{{ i18n "sure" }}',
|
||||
reverse,
|
||||
rules,
|
||||
confirm = (reverse, rules) => {},
|
||||
isEdit = false
|
||||
}) {
|
||||
this.title = title;
|
||||
this.okText = okText;
|
||||
this.confirm = confirm;
|
||||
this.visible = true;
|
||||
if(isEdit) {
|
||||
if (isEdit) {
|
||||
this.reverse = {
|
||||
tag: reverse.tag,
|
||||
type: reverse.type,
|
||||
domain: reverse.domain,
|
||||
};
|
||||
reverse;
|
||||
reverse;
|
||||
rules0 = rules.filter(r => r.domain != null);
|
||||
if(rules0.length == 0) rules0 = [{ outboundTag: '', domain: ["full:" + this.reverse.domain], inboundTag: []}];
|
||||
if (rules0.length == 0) rules0 = [{
|
||||
outboundTag: '',
|
||||
domain: ["full:" + this.reverse.domain],
|
||||
inboundTag: []
|
||||
}];
|
||||
rules1 = rules.filter(r => r.domain == null);
|
||||
if(rules1.length == 0) rules1 = [{ outboundTag: '', inboundTag: []}];
|
||||
if (rules1.length == 0) rules1 = [{
|
||||
outboundTag: '',
|
||||
inboundTag: []
|
||||
}];
|
||||
this.rules = [];
|
||||
this.rules.push({
|
||||
domain: rules0[0].domain,
|
||||
@@ -105,22 +122,29 @@
|
||||
type: "bridge",
|
||||
domain: "reverse.xui"
|
||||
}
|
||||
this.rules = [
|
||||
{ outboundTag: '', inboundTag: []},
|
||||
{ outboundTag: '', inboundTag: []}
|
||||
this.rules = [{
|
||||
outboundTag: '',
|
||||
inboundTag: []
|
||||
},
|
||||
{
|
||||
outboundTag: '',
|
||||
inboundTag: []
|
||||
}
|
||||
]
|
||||
}
|
||||
this.isEdit = isEdit;
|
||||
this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag);
|
||||
this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj =>
|
||||
obj.tag);
|
||||
this.inboundTags.push(...app.inboundTags);
|
||||
if (app.enableDNS && !ObjectUtil.isEmpty(app.dnsTag)) this.inboundTags.push(app.dnsTag)
|
||||
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag);
|
||||
this.outboundTags = app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj =>
|
||||
obj.tag);
|
||||
},
|
||||
close() {
|
||||
reverseModal.visible = false;
|
||||
reverseModal.loading(false);
|
||||
},
|
||||
loading(loading=true) {
|
||||
loading(loading = true) {
|
||||
reverseModal.confirmLoading = loading;
|
||||
},
|
||||
};
|
||||
@@ -130,9 +154,11 @@
|
||||
el: '#reverse-modal',
|
||||
data: {
|
||||
reverseModal: reverseModal,
|
||||
reverseTypes: { bridge: '{{ i18n "pages.xray.outbound.bridge" }}', portal:'{{ i18n "pages.xray.outbound.portal" }}'},
|
||||
reverseTypes: {
|
||||
bridge: '{{ i18n "pages.xray.outbound.bridge" }}',
|
||||
portal: '{{ i18n "pages.xray.outbound.portal" }}'
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -1,5 +1,7 @@
|
||||
{{define "modals/ruleModal"}}
|
||||
<a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok" :confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false" :ok-text="ruleModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-modal id="rule-modal" v-model="ruleModal.visible" :title="ruleModal.title" @ok="ruleModal.ok"
|
||||
:confirm-loading="ruleModal.confirmLoading" :closable="true" :mask-closable="false" :ok-text="ruleModal.okText"
|
||||
cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
@@ -42,15 +44,19 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='Attributes'>
|
||||
<a-button icon="plus" size="small" :style="{ marginLeft: '10px' }" @click="ruleModal.rule.attrs.push(['', ''])"></a-button>
|
||||
<a-button icon="plus" size="small" :style="{ marginLeft: '10px' }"
|
||||
@click="ruleModal.rule.attrs.push(['', ''])"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span: 24}">
|
||||
<a-input-group compact v-for="(attr,index) in ruleModal.rule.attrs">
|
||||
<a-input :style="{ width: '50%' }" v-model="attr[0]" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||
<a-input :style="{ width: '50%' }" v-model="attr[0]"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input :style="{ width: '50%' }" v-model="attr[1]" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button icon="minus" slot="addonAfter" size="small" @click="ruleModal.rule.attrs.splice(index,1)"></a-button>
|
||||
<a-input :style="{ width: '50%' }" v-model="attr[1]"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button icon="minus" slot="addonAfter" size="small"
|
||||
@click="ruleModal.rule.attrs.splice(index,1)"></a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
@@ -196,16 +202,20 @@
|
||||
this.inboundTags = app.templateSettings.inbounds.filter((i) => !ObjectUtil.isEmpty(i.tag)).map(obj => obj.tag);
|
||||
this.inboundTags.push(...app.inboundTags);
|
||||
if (app.enableDNS && !ObjectUtil.isEmpty(app.dnsTag)) this.inboundTags.push(app.dnsTag)
|
||||
this.outboundTags = ["", ...app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag)];
|
||||
this.outboundTags = ["", ...app.templateSettings.outbounds.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj =>
|
||||
obj.tag)];
|
||||
if (app.templateSettings.reverse) {
|
||||
if (app.templateSettings.reverse.bridges) {
|
||||
this.inboundTags.push(...app.templateSettings.reverse.bridges.map(b => b.tag));
|
||||
}
|
||||
if (app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(b => b.tag));
|
||||
if (app.templateSettings.reverse.portals) this.outboundTags.push(...app.templateSettings.reverse.portals.map(
|
||||
b => b.tag));
|
||||
}
|
||||
this.balancerTags = [""];
|
||||
if (app.templateSettings.routing && app.templateSettings.routing.balancers) {
|
||||
this.balancerTags = ["", ...app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag)).map(obj => obj.tag)];
|
||||
this.balancerTags = ["", ...app.templateSettings.routing.balancers.filter((o) => !ObjectUtil.isEmpty(o.tag))
|
||||
.map(obj => obj.tag)
|
||||
];
|
||||
}
|
||||
},
|
||||
close() {
|
||||
@@ -234,7 +244,8 @@
|
||||
rule.outboundTag = value.outboundTag == "" ? undefined : value.outboundTag;
|
||||
rule.balancerTag = value.balancerTag == "" ? undefined : value.balancerTag;
|
||||
for (const [key, value] of Object.entries(rule)) {
|
||||
if (value !== null && value !== undefined && !(Array.isArray(value) && value.length === 0) && !(typeof value === 'object' && Object.keys(value).length === 0) && value !== '') {
|
||||
if (value !== null && value !== undefined && !(Array.isArray(value) && value.length === 0) && !(
|
||||
typeof value === 'object' && Object.keys(value).length === 0) && value !== '') {
|
||||
newRule[key] = value;
|
||||
}
|
||||
}
|
||||
@@ -249,4 +260,4 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user