mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-05-08 14:36:13 +00:00
TLS: Remove ECH Force Query
This commit is contained in:
@@ -697,7 +697,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
certificates = [new TlsStreamSettings.Cert()],
|
||||
alpn = [ALPN_OPTION.H2, ALPN_OPTION.HTTP1],
|
||||
echServerKeys = '',
|
||||
echForceQuery = 'none',
|
||||
settings = new TlsStreamSettings.Settings()
|
||||
) {
|
||||
super();
|
||||
@@ -711,7 +710,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
this.certs = certificates;
|
||||
this.alpn = alpn;
|
||||
this.echServerKeys = echServerKeys;
|
||||
this.echForceQuery = echForceQuery;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@@ -744,7 +742,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
certs,
|
||||
json.alpn,
|
||||
json.echServerKeys,
|
||||
json.echForceQuery,
|
||||
settings,
|
||||
);
|
||||
}
|
||||
@@ -761,7 +758,6 @@ class TlsStreamSettings extends XrayCommonClass {
|
||||
certificates: TlsStreamSettings.toJsonArray(this.certs),
|
||||
alpn: this.alpn,
|
||||
echServerKeys: this.echServerKeys,
|
||||
echForceQuery: this.echForceQuery,
|
||||
settings: this.settings,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{{ define "page/head_start" }}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
@@ -12,6 +13,7 @@
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* vazirmatn-regular - arabic_latin_latin-ext */
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
@@ -21,6 +23,7 @@
|
||||
src: url('{{ .base_path }}assets/Vazirmatn-UI-NL-Regular.woff2') format('woff2');
|
||||
unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE80-FEFC, U+0030-0039;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Vazirmatn', 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
@@ -34,26 +37,34 @@
|
||||
overscroll-behavior-x: contain;
|
||||
white-space: nowrap;
|
||||
max-width: 100%;
|
||||
padding: 0 !important; /* Remove padding for arrows */
|
||||
padding: 0 !important;
|
||||
/* Remove padding for arrows */
|
||||
}
|
||||
|
||||
.ant-tabs-nav-wrap {
|
||||
overflow: visible !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.ant-tabs-nav-scroll {
|
||||
overflow: visible !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.ant-tabs-nav {
|
||||
display: flex !important;
|
||||
transform: none !important; /* Disable JS transform */
|
||||
transform: none !important;
|
||||
/* Disable JS transform */
|
||||
width: auto !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-prev,
|
||||
.ant-tabs-tab-next {
|
||||
display: none !important; /* Hide arrows */
|
||||
display: none !important;
|
||||
/* Hide arrows */
|
||||
}
|
||||
|
||||
.ant-tabs-nav-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
@@ -67,6 +78,7 @@
|
||||
{{ end }}
|
||||
|
||||
{{ define "page/body_start" }}
|
||||
|
||||
<body>
|
||||
<div id="message"></div>
|
||||
{{ end }}
|
||||
@@ -88,5 +100,6 @@
|
||||
|
||||
{{ define "page/body_end" }}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
{{ end }}
|
||||
@@ -2,30 +2,39 @@
|
||||
<template slot="actions" slot-scope="text, client, index">
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "qrCode" }}</template>
|
||||
<a-icon :style="{ fontSize: '22px', marginInlineStart: '14px' }" class="normal-icon" type="qrcode" v-if="record.hasLink()" @click="showQrcode(record.id,client);"></a-icon>
|
||||
<a-icon :style="{ fontSize: '22px', marginInlineStart: '14px' }" class="normal-icon" type="qrcode"
|
||||
v-if="record.hasLink()" @click="showQrcode(record.id,client);"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.client.edit" }}</template>
|
||||
<a-icon :style="{ fontSize: '22px' }" class="normal-icon" type="edit" @click="openEditClient(record.id,client);"></a-icon>
|
||||
<a-icon :style="{ fontSize: '22px' }" class="normal-icon" type="edit"
|
||||
@click="openEditClient(record.id,client);"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "info" }}</template>
|
||||
<a-icon :style="{ fontSize: '22px' }" class="normal-icon" type="info-circle" @click="showInfo(record.id,client);"></a-icon>
|
||||
<a-icon :style="{ fontSize: '22px' }" class="normal-icon" type="info-circle"
|
||||
@click="showInfo(record.id,client);"></a-icon>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||
<a-popconfirm @confirm="resetClientTraffic(client,record.id,false)" title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
|
||||
<a-popconfirm @confirm="resetClientTraffic(client,record.id,false)"
|
||||
title='{{ i18n "pages.inbounds.resetTrafficContent"}}' :overlay-class-name="themeSwitcher.currentTheme"
|
||||
ok-text='{{ i18n "reset"}}' cancel-text='{{ i18n "cancel"}}'>
|
||||
<a-icon slot="icon" type="question-circle-o" :style="{ color: 'var(--color-primary-100)'}"></a-icon>
|
||||
<a-icon :style="{ fontSize: '22px', cursor: 'pointer' }" class="normal-icon" type="retweet" v-if="client.email.length > 0"></a-icon>
|
||||
<a-icon :style="{ fontSize: '22px', cursor: 'pointer' }" class="normal-icon" type="retweet"
|
||||
v-if="client.email.length > 0"></a-icon>
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span :style="{ color: '#FF4D4F' }"> {{ i18n "delete"}}</span>
|
||||
</template>
|
||||
<a-popconfirm @confirm="delClient(record.id,client,false)" title='{{ i18n "pages.inbounds.deleteClientContent"}}' :overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "delete"}}' ok-type="danger" cancel-text='{{ i18n "cancel"}}'>
|
||||
<a-popconfirm @confirm="delClient(record.id,client,false)" title='{{ i18n "pages.inbounds.deleteClientContent"}}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "delete"}}' ok-type="danger"
|
||||
cancel-text='{{ i18n "cancel"}}'>
|
||||
<a-icon slot="icon" type="question-circle-o" :style="{ color: '#e04141' }"></a-icon>
|
||||
<a-icon :style="{ fontSize: '22px', cursor: 'pointer' }" class="delete-icon" type="delete" v-if="isRemovable(record.id)"></a-icon>
|
||||
<a-icon :style="{ fontSize: '22px', cursor: 'pointer' }" class="delete-icon" type="delete"
|
||||
v-if="isRemovable(record.id)"></a-icon>
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@@ -53,7 +62,8 @@
|
||||
<template v-else-if="!client.enable">{{ i18n "disabled" }}</template>
|
||||
<template v-else-if="client.enable && isClientOnline(client.email)">{{ i18n "online" }}</template>
|
||||
</template>
|
||||
<a-badge :class="isClientOnline(client.email)? 'online-animation' : ''" :color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
|
||||
<a-badge :class="isClientOnline(client.email)? 'online-animation' : ''"
|
||||
:color="client.enable ? statsExpColor(record, client.email) : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"></a-badge>
|
||||
</a-tooltip>
|
||||
<a-space direction="vertical" :size="2">
|
||||
<span class="client-email">[[ client.email ]]</span>
|
||||
@@ -87,10 +97,13 @@
|
||||
<tr class="tr-table-box">
|
||||
<td class="tr-table-rt"> [[ SizeFormatter.sizeFormat(getSumStats(record, client.email)) ]] </td>
|
||||
<td class="tr-table-bar" v-if="!client.enable">
|
||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false"
|
||||
:percent="statsProgress(record, client.email)" />
|
||||
</td>
|
||||
<td class="tr-table-bar" v-else-if="client.totalGB > 0">
|
||||
<a-progress :stroke-color="clientStatsColor(record, client.email)" :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="statsProgress(record, client.email)" />
|
||||
<a-progress :stroke-color="clientStatsColor(record, client.email)" :show-info="false"
|
||||
:status="isClientDepleted(record, client.email)? 'exception' : ''"
|
||||
:percent="statsProgress(record, client.email)" />
|
||||
</td>
|
||||
<td v-else class="infinite-bar tr-table-bar">
|
||||
<a-progress :show-info="false" :percent="100"></a-progress>
|
||||
@@ -118,7 +131,8 @@
|
||||
<tr class="tr-table-box">
|
||||
<td class="tr-table-rt"> [[ IntlUtil.formatRelativeTime(client.expiryTime) ]] </td>
|
||||
<td class="infinite-bar tr-table-bar">
|
||||
<a-progress :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="expireProgress(client.expiryTime, client.reset)" />
|
||||
<a-progress :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''"
|
||||
:percent="expireProgress(client.expiryTime, client.reset)" />
|
||||
</td>
|
||||
<td class="tr-table-lt">[[ client.reset + "d" ]]</td>
|
||||
</tr>
|
||||
@@ -131,11 +145,16 @@
|
||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}</span>
|
||||
<span v-else>[[ IntlUtil.formatDate(client.expiryTime) ]]</span>
|
||||
</template>
|
||||
<a-tag :style="{ minWidth: '50px', border: 'none' }" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ IntlUtil.formatRelativeTime(client.expiryTime) ]] </a-tag>
|
||||
<a-tag :style="{ minWidth: '50px', border: 'none' }"
|
||||
:color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[
|
||||
IntlUtil.formatRelativeTime(client.expiryTime) ]] </a-tag>
|
||||
</a-popover>
|
||||
<a-tag v-else :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)" :style="{ border: 'none' }" class="infinite-tag">
|
||||
<a-tag v-else :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"
|
||||
:style="{ border: 'none' }" class="infinite-tag">
|
||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
||||
<path
|
||||
d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
</a-tag>
|
||||
</template>
|
||||
@@ -165,7 +184,8 @@
|
||||
<span :style="{ color: '#FF4D4F' }"> {{ i18n "delete"}}</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-switch v-model="client.enable" size="small" @change="switchEnableClient(record.id, client, $event)"></a-switch>
|
||||
<a-switch v-model="client.enable" size="small"
|
||||
@change="switchEnableClient(record.id, client, $event)"></a-switch>
|
||||
{{ i18n "enable"}}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
@@ -179,9 +199,11 @@
|
||||
<td colspan="3" :style="{ textAlign: 'center' }">{{ i18n "pages.inbounds.traffic" }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[ SizeFormatter.sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
|
||||
<td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[
|
||||
SizeFormatter.sizeFormat(getUpStats(record, client.email) + getDownStats(record, client.email)) ]] </td>
|
||||
<td width="120px" v-if="!client.enable">
|
||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false" :percent="statsProgress(record, client.email)" />
|
||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? 'rgb(72 84 105)' : '#bcbcbc'" :show-info="false"
|
||||
:percent="statsProgress(record, client.email)" />
|
||||
</td>
|
||||
<td width="120px" v-else-if="client.totalGB > 0">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||
@@ -197,11 +219,14 @@
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
<a-progress :stroke-color="clientStatsColor(record, client.email)" :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="statsProgress(record, client.email)" />
|
||||
<a-progress :stroke-color="clientStatsColor(record, client.email)" :show-info="false"
|
||||
:status="isClientDepleted(record, client.email)? 'exception' : ''"
|
||||
:percent="statsProgress(record, client.email)" />
|
||||
</a-popover>
|
||||
</td>
|
||||
<td width="120px" v-else class="infinite-bar">
|
||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? '#2c1e32':'#F2EAF1'" :show-info="false" :percent="100"></a-progress>
|
||||
<a-progress :stroke-color="themeSwitcher.isDarkTheme ? '#2c1e32':'#F2EAF1'" :show-info="false"
|
||||
:percent="100"></a-progress>
|
||||
</td>
|
||||
<td width="80px">
|
||||
<template v-if="client.totalGB > 0">[[ client._totalGB + "GB" ]]</template>
|
||||
@@ -216,14 +241,16 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<template v-if="client.expiryTime !=0 && client.reset >0">
|
||||
<td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[ IntlUtil.formatRelativeTime(client.expiryTime) ]] </td>
|
||||
<td width="80px" :style="{ margin: '0', textAlign: 'right', fontSize: '1em' }"> [[
|
||||
IntlUtil.formatRelativeTime(client.expiryTime) ]] </td>
|
||||
<td width="120px" class="infinite-bar">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}</span>
|
||||
<span v-else>[[ IntlUtil.formatDate(client.expiryTime) ]]</span>
|
||||
</template>
|
||||
<a-progress :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''" :percent="expireProgress(client.expiryTime, client.reset)" />
|
||||
<a-progress :show-info="false" :status="isClientDepleted(record, client.email)? 'exception' : ''"
|
||||
:percent="expireProgress(client.expiryTime, client.reset)" />
|
||||
</a-popover>
|
||||
</td>
|
||||
<td width="60px">[[ client.reset + "d" ]]</td>
|
||||
@@ -235,11 +262,16 @@
|
||||
<span v-if="client.expiryTime < 0">{{ i18n "pages.client.delayedStart" }}</span>
|
||||
<span v-else>[[ IntlUtil.formatDate(client.expiryTime) ]]</span>
|
||||
</template>
|
||||
<a-tag :style="{ minWidth: '50px', border: 'none' }" :color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[ IntlUtil.formatRelativeTime(client.expiryTime) ]] </a-tag>
|
||||
<a-tag :style="{ minWidth: '50px', border: 'none' }"
|
||||
:color="ColorUtils.userExpiryColor(app.expireDiff, client, themeSwitcher.isDarkTheme)"> [[
|
||||
IntlUtil.formatRelativeTime(client.expiryTime) ]] </a-tag>
|
||||
</a-popover>
|
||||
<a-tag v-else :color="client.enable ? 'purple' : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'" class="infinite-tag">
|
||||
<a-tag v-else :color="client.enable ? 'purple' : themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc'"
|
||||
class="infinite-tag">
|
||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||
<path d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z" fill="currentColor"></path>
|
||||
<path
|
||||
d="M484.4 96C407 96 349.2 164.1 320 208.5C290.8 164.1 233 96 155.6 96C69.75 96 0 167.8 0 256s69.75 160 155.6 160C233.1 416 290.8 347.9 320 303.5C349.2 347.9 407 416 484.4 416C570.3 416 640 344.2 640 256S570.3 96 484.4 96zM155.6 368C96.25 368 48 317.8 48 256s48.25-112 107.6-112c67.75 0 120.5 82.25 137.1 112C276 285.8 223.4 368 155.6 368zM484.4 368c-67.75 0-120.5-82.25-137.1-112C364 226.2 416.6 144 484.4 144C543.8 144 592 194.2 592 256S543.8 368 484.4 368z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
</a-tag>
|
||||
</template>
|
||||
@@ -248,7 +280,8 @@
|
||||
</table>
|
||||
</template>
|
||||
<a-badge>
|
||||
<a-icon v-if="!client.enable" slot="count" type="pause-circle" theme="filled" :style="{ color: themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc' }"></a-icon>
|
||||
<a-icon v-if="!client.enable" slot="count" type="pause-circle" theme="filled"
|
||||
:style="{ color: themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc' }"></a-icon>
|
||||
<a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
|
||||
<a-icon type="solution"></a-icon>
|
||||
</a-button>
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
.dark .ant-statistic-content {
|
||||
color: var(--dark-color-text-primary)
|
||||
}
|
||||
|
||||
.dark .ant-statistic-title {
|
||||
color: rgba(255, 255, 255, 0.55)
|
||||
}
|
||||
|
||||
.ant-statistic-content {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@
|
||||
},
|
||||
methods: {
|
||||
convertToGregorian(date) {
|
||||
return date ? moment(moment(date, 'jYYYY/jMM/jDD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss')) : null
|
||||
return date ? moment(moment(date, 'jYYYY/jMM/jDD HH:mm:ss').format('YYYY-MM-DD HH:mm:ss')) :
|
||||
null
|
||||
},
|
||||
convertToJalalian(date) {
|
||||
return date && moment.isMoment(date) ? date.format('jYYYY/jMM/jDD HH:mm:ss') : null
|
||||
|
||||
@@ -43,8 +43,7 @@
|
||||
Vue.component('a-sidebar', {
|
||||
data() {
|
||||
return {
|
||||
tabs: [
|
||||
{
|
||||
tabs: [{
|
||||
key: '{{ .base_path }}panel/',
|
||||
icon: 'dashboard',
|
||||
title: '{{ i18n "menu.dashboard"}}'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{define "component/sortableTableTrigger"}}
|
||||
<a-icon type="drag" class="sortable-icon" :style="{ cursor: 'move' }" @mouseup="mouseUpHandler" @mousedown="mouseDownHandler"
|
||||
@click="clickHandler" />
|
||||
<a-icon type="drag" class="sortable-icon" :style="{ cursor: 'move' }" @mouseup="mouseUpHandler"
|
||||
@mousedown="mouseDownHandler" @click="clickHandler" />
|
||||
{{end}}
|
||||
|
||||
{{define "component/aTableSortable"}}
|
||||
@@ -148,7 +148,8 @@
|
||||
class: {
|
||||
...(parentMethodResult?.class || {}),
|
||||
[DRAGGABLE_ROW_CLASS]: true,
|
||||
['dragging']: this.isDragging() ? (newIndex === null ? index === currentIndex : index === newIndex) : false,
|
||||
['dragging']: this.isDragging() ? (newIndex === null ? index === currentIndex : index === newIndex) :
|
||||
false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,9 +24,11 @@
|
||||
|
||||
{{define "component/themeSwitchTemplateLogin"}}
|
||||
<template>
|
||||
<a-space @mousedown="themeSwitcher.animationsOff()" id="change-theme" direction="vertical" :size="10" :style="{ width: '100%' }">
|
||||
<a-space @mousedown="themeSwitcher.animationsOff()" id="change-theme" direction="vertical" :size="10"
|
||||
:style="{ width: '100%' }">
|
||||
<a-space direction="horizontal" size="small">
|
||||
<a-switch size="small" :default-checked="themeSwitcher.isDarkTheme" @change="themeSwitcher.toggleTheme()"></a-switch>
|
||||
<a-switch size="small" :default-checked="themeSwitcher.isDarkTheme"
|
||||
@change="themeSwitcher.toggleTheme()"></a-switch>
|
||||
<span>{{ i18n "menu.dark" }}</span>
|
||||
</a-space>
|
||||
<a-space v-if="themeSwitcher.isDarkTheme" direction="horizontal" size="small">
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
{{define "form/client"}}
|
||||
<a-form
|
||||
layout="horizontal"
|
||||
v-if="client"
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form layout="horizontal" v-if="client" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.enable" }}'>
|
||||
<a-switch v-model="client.enable"></a-switch>
|
||||
</a-form-item>
|
||||
@@ -16,33 +10,22 @@
|
||||
<span>{{ i18n "pages.inbounds.emailDesc" }}</span>
|
||||
</template>
|
||||
{{ i18n "pages.inbounds.email" }}
|
||||
<a-icon
|
||||
type="sync"
|
||||
@click="client.email = RandomUtil.randomLowerAndNum(9)"
|
||||
></a-icon>
|
||||
<a-icon type="sync" @click="client.email = RandomUtil.randomLowerAndNum(9)"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="client.email"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS"
|
||||
>
|
||||
<a-form-item v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS">
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
{{ i18n "password" }}
|
||||
<a-icon
|
||||
v-if="inbound.protocol === Protocols.SHADOWSOCKS"
|
||||
@click="client.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)"
|
||||
type="sync"
|
||||
></a-icon>
|
||||
<a-icon
|
||||
v-if="inbound.protocol === Protocols.TROJAN"
|
||||
@click="client.password = RandomUtil.randomSeq(10)"
|
||||
type="sync"
|
||||
>
|
||||
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS"
|
||||
@click="client.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)" type="sync"></a-icon>
|
||||
<a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)"
|
||||
type="sync">
|
||||
</a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@@ -55,42 +38,26 @@
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
Auth Password
|
||||
<a-icon
|
||||
@click="client.auth = RandomUtil.randomSeq(10)"
|
||||
type="sync"
|
||||
></a-icon>
|
||||
<a-icon @click="client.auth = RandomUtil.randomSeq(10)" type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="client.auth"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS"
|
||||
>
|
||||
<a-form-item v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
ID
|
||||
<a-icon
|
||||
@click="client.id = RandomUtil.randomUUID()"
|
||||
type="sync"
|
||||
></a-icon>
|
||||
<a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="client.id"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="inbound.protocol === Protocols.VMESS"
|
||||
label='{{ i18n "security" }}'
|
||||
>
|
||||
<a-select
|
||||
v-model="client.security"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select-option v-for="key in USERS_SECURITY" :value="key"
|
||||
>[[ key ]]</a-select-option
|
||||
>
|
||||
<a-form-item v-if="inbound.protocol === Protocols.VMESS" label='{{ i18n "security" }}'>
|
||||
<a-select v-model="client.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 v-if="client.email && app.subSettings?.enable">
|
||||
@@ -100,10 +67,7 @@
|
||||
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
|
||||
</template>
|
||||
Subscription
|
||||
<a-icon
|
||||
@click="client.subId = RandomUtil.randomLowerAndNum(16)"
|
||||
type="sync"
|
||||
></a-icon>
|
||||
<a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="client.subId"></a-input>
|
||||
@@ -118,11 +82,7 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number
|
||||
:style="{ width: '50%' }"
|
||||
v-model.number="client.tgId"
|
||||
min="0"
|
||||
></a-input-number>
|
||||
<a-input-number :style="{ width: '50%' }" v-model.number="client.tgId" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="client.email" label='{{ i18n "comment" }}'>
|
||||
<a-input v-model.trim="client.comment"></a-input>
|
||||
@@ -139,9 +99,7 @@
|
||||
</template>
|
||||
<a-input-number v-model.number="client.limitIp" min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
v-if="app.ipLimitEnable && client.limitIp > 0 && client.email && isEdit"
|
||||
>
|
||||
<a-form-item v-if="app.ipLimitEnable && client.limitIp > 0 && client.email && isEdit">
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
@@ -160,25 +118,15 @@
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<a-form layout="block">
|
||||
<a-textarea
|
||||
id="clientIPs"
|
||||
readonly
|
||||
@click="getDBClientIps(client.email)"
|
||||
placeholder="Click To Get IPs"
|
||||
:auto-size="{ minRows: 5, maxRows: 10 }"
|
||||
>
|
||||
<a-textarea id="clientIPs" readonly @click="getDBClientIps(client.email)" placeholder="Click To Get IPs"
|
||||
:auto-size="{ minRows: 5, maxRows: 10 }">
|
||||
</a-textarea>
|
||||
</a-form>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.canEnableTlsFlow()" label="Flow">
|
||||
<a-select
|
||||
v-model="client.flow"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="client.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"
|
||||
>[[ key ]]</a-select-option
|
||||
>
|
||||
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
@@ -201,45 +149,28 @@
|
||||
</a-tag>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||
<a-icon
|
||||
type="retweet"
|
||||
@click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)"
|
||||
v-if="client.email.length > 0"
|
||||
></a-icon>
|
||||
<a-icon type="retweet" @click="resetClientTraffic(client.email,clientStats.inboundId,$event.target)"
|
||||
v-if="client.email.length > 0"></a-icon>
|
||||
</a-tooltip>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
|
||||
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="delayedStart" label='{{ i18n "pages.client.expireDays" }}'>
|
||||
<a-input-number
|
||||
v-model.number="delayedExpireDays"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-else>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title"
|
||||
>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</template
|
||||
>
|
||||
<template slot="title">{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</template>
|
||||
{{ i18n "pages.inbounds.expireDate" }}
|
||||
<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"
|
||||
v-model="client._expiryTime"
|
||||
></a-date-picker>
|
||||
<a-persian-datepicker
|
||||
v-else
|
||||
placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
||||
value="client._expiryTime"
|
||||
v-model="client._expiryTime"
|
||||
></a-persian-datepicker>
|
||||
<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="client._expiryTime"></a-date-picker>
|
||||
<a-persian-datepicker v-else placeholder='{{ i18n "pages.settings.datepickerPlaceholder" }}'
|
||||
value="client._expiryTime" v-model="client._expiryTime"></a-persian-datepicker>
|
||||
<a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="client.expiryTime != 0">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,8 @@
|
||||
<td width="45%">{{ i18n "username" }}</td>
|
||||
<td width="45%">{{ i18n "password" }}</td>
|
||||
<td>
|
||||
<a-button icon="plus" size="small" @click="inbound.settings.addAccount(new Inbound.HttpSettings.HttpAccount())"></a-button>
|
||||
<a-button icon="plus" size="small"
|
||||
@click="inbound.settings.addAccount(new Inbound.HttpSettings.HttpAccount())"></a-button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{{define "form/hysteria"}}
|
||||
<a-collapse activeKey="0"
|
||||
v-for="(client, index) in inbound.settings.hysterias.slice(0,1)"
|
||||
v-if="!isEdit">
|
||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.hysterias.slice(0,1)" v-if="!isEdit">
|
||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||
{{template "form/client"}}
|
||||
</a-collapse-panel>
|
||||
@@ -22,11 +20,9 @@
|
||||
</table>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }">
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item :label="'{{ i18n "pages.inbounds.stream.tcp.version" }}'">
|
||||
<a-input-number v-model.number="inbound.settings.version" :min="2"
|
||||
:max="2" disabled></a-input-number>
|
||||
<a-input-number v-model.number="inbound.settings.version" :min="2" :max="2" disabled></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -1,10 +1,6 @@
|
||||
{{define "form/shadowsocks"}}
|
||||
<template v-if="inbound.isSSMultiUser">
|
||||
<a-collapse
|
||||
activeKey="0"
|
||||
v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)"
|
||||
v-if="!isEdit"
|
||||
>
|
||||
<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.shadowsockses.slice(0,1)" v-if="!isEdit">
|
||||
<a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
|
||||
{{template "form/client"}}
|
||||
</a-collapse-panel>
|
||||
@@ -16,10 +12,8 @@
|
||||
<th>{{ i18n "pages.inbounds.email" }}</th>
|
||||
<th>Password</th>
|
||||
</tr>
|
||||
<tr
|
||||
v-for="(client, index) in inbound.settings.shadowsockses"
|
||||
:class="index % 2 == 1 ? ' client-table-odd-row' : ''"
|
||||
>
|
||||
<tr v-for="(client, index) in inbound.settings.shadowsockses"
|
||||
:class="index % 2 == 1 ? ' client-table-odd-row' : ''">
|
||||
<td>[[ client.email ]]</td>
|
||||
<td>[[ client.password ]]</td>
|
||||
</tr>
|
||||
@@ -27,20 +21,11 @@
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</template>
|
||||
<a-form
|
||||
:colon=" false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon=" false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "encryption" }}'>
|
||||
<a-select
|
||||
v-model="inbound.settings.method"
|
||||
@change="SSMethodChange"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select-option v-for="(method,method_name) in SSMethods" :value="method"
|
||||
>[[ method_name ]]</a-select-option
|
||||
>
|
||||
<a-select v-model="inbound.settings.method" @change="SSMethodChange"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="(method,method_name) in SSMethods" :value="method">[[ method_name ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="inbound.isSS2022">
|
||||
@@ -50,20 +35,15 @@
|
||||
<span>{{ i18n "reset" }}</span>
|
||||
</template>
|
||||
Password
|
||||
<a-icon
|
||||
@click="inbound.settings.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)"
|
||||
type="sync"
|
||||
></a-icon>
|
||||
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword(inbound.settings.method)"
|
||||
type="sync"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input v-model.trim="inbound.settings.password"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
|
||||
<a-select
|
||||
v-model="inbound.settings.network"
|
||||
:style="{ width: '100px' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.settings.network" :style="{ width: '100px' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="tcp,udp">TCP,UDP</a-select-option>
|
||||
<a-select-option value="tcp">TCP</a-select-option>
|
||||
<a-select-option value="udp">UDP</a-select-option>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{{define "form/mixed"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "pages.inbounds.enable" }} UDP'>
|
||||
<a-switch v-model="inbound.settings.udp"></a-switch>
|
||||
</a-form-item>
|
||||
@@ -11,10 +7,8 @@
|
||||
<a-input v-model.trim="inbound.settings.ip"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "password" }}'>
|
||||
<a-switch
|
||||
:checked="inbound.settings.auth === 'password'"
|
||||
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"
|
||||
></a-switch>
|
||||
<a-switch :checked="inbound.settings.auth === 'password'"
|
||||
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
|
||||
</a-form-item>
|
||||
<template v-if="inbound.settings.auth === 'password'">
|
||||
<table :style="{ width: '100%', textAlign: 'center', margin: '1rem 0' }">
|
||||
@@ -22,39 +16,18 @@
|
||||
<td width="45%">{{ i18n "username" }}</td>
|
||||
<td width="45%">{{ i18n "password" }}</td>
|
||||
<td>
|
||||
<a-button
|
||||
icon="plus"
|
||||
size="small"
|
||||
@click="inbound.settings.addAccount(new Inbound.MixedSettings.SocksAccount())"
|
||||
></a-button>
|
||||
<a-button icon="plus" size="small"
|
||||
@click="inbound.settings.addAccount(new Inbound.MixedSettings.SocksAccount())"></a-button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a-input-group
|
||||
compact
|
||||
v-for="(account, index) in inbound.settings.accounts"
|
||||
:style="{ marginBottom: '10px' }"
|
||||
>
|
||||
<a-input
|
||||
:style="{ width: '50%' }"
|
||||
v-model.trim="account.user"
|
||||
placeholder='{{ i18n "username" }}'
|
||||
>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }"
|
||||
>[[ index+1 ]]</template
|
||||
>
|
||||
<a-input-group compact v-for="(account, index) in inbound.settings.accounts" :style="{ marginBottom: '10px' }">
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }">[[ index+1 ]]</template>
|
||||
</a-input>
|
||||
<a-input
|
||||
:style="{ width: '50%' }"
|
||||
v-model.trim="account.pass"
|
||||
placeholder='{{ i18n "password" }}'
|
||||
>
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
|
||||
<template slot="addonAfter">
|
||||
<a-button
|
||||
icon="minus"
|
||||
size="small"
|
||||
@click="inbound.settings.delAccount(index)"
|
||||
></a-button>
|
||||
<a-button icon="minus" size="small" @click="inbound.settings.delAccount(index)"></a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{{define "form/tun"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
@@ -26,38 +22,18 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model.number="inbound.settings.mtu[0]"
|
||||
:min="1"
|
||||
:max="9000"
|
||||
placeholder="1500"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.settings.mtu[0]" :min="1" :max="9000" placeholder="1500"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="MTU IPv6">
|
||||
<a-input-number
|
||||
v-model.number="inbound.settings.mtu[1]"
|
||||
:min="1"
|
||||
:max="9000"
|
||||
placeholder="1280"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.settings.mtu[1]" :min="1" :max="9000" placeholder="1280"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Gateway">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="inbound.settings.gateway"
|
||||
:style="{ width: '100%' }"
|
||||
:token-separators="[',']"
|
||||
placeholder="IPv4/IPv6 gateway"
|
||||
></a-select>
|
||||
<a-select mode="tags" v-model="inbound.settings.gateway" :style="{ width: '100%' }" :token-separators="[',']"
|
||||
placeholder="IPv4/IPv6 gateway"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="DNS">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="inbound.settings.dns"
|
||||
:style="{ width: '100%' }"
|
||||
:token-separators="[',']"
|
||||
placeholder="DNS servers"
|
||||
></a-select>
|
||||
<a-select mode="tags" v-model="inbound.settings.dns" :style="{ width: '100%' }" :token-separators="[',']"
|
||||
placeholder="DNS servers"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label">
|
||||
@@ -69,26 +45,14 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number
|
||||
v-model.number="inbound.settings.userLevel"
|
||||
:min="0"
|
||||
placeholder="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.settings.userLevel" :min="0" placeholder="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Auto Routing Table">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="inbound.settings.autoSystemRoutingTable"
|
||||
:style="{ width: '100%' }"
|
||||
:token-separators="[',']"
|
||||
placeholder="e.g. vpn, proxy"
|
||||
></a-select>
|
||||
<a-select mode="tags" v-model="inbound.settings.autoSystemRoutingTable" :style="{ width: '100%' }"
|
||||
:token-separators="[',']" placeholder="e.g. vpn, proxy"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Auto Outbounds">
|
||||
<a-input
|
||||
v-model.trim="inbound.settings.autoOutboundsInterface"
|
||||
placeholder="auto"
|
||||
></a-input>
|
||||
<a-input v-model.trim="inbound.settings.autoOutboundsInterface" placeholder="auto"></a-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -12,8 +12,7 @@
|
||||
<th>{{ i18n "pages.inbounds.email" }}</th>
|
||||
<th>ID</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.vlesses"
|
||||
:class="index % 2 == 1 ? ' client-table-odd-row' : ''">
|
||||
<tr v-for="(client, index) in inbound.settings.vlesses" :class="index % 2 == 1 ? ' client-table-odd-row' : ''">
|
||||
<td>[[ client.email ]]</td>
|
||||
<td>[[ client.id ]]</td>
|
||||
</tr>
|
||||
@@ -87,26 +86,26 @@
|
||||
<a-col :span="6">
|
||||
<a-input-number
|
||||
:value="(inbound.settings.testseed && inbound.settings.testseed[0] !== undefined) ? inbound.settings.testseed[0] : 900"
|
||||
@change="(val) => updateTestseed(0, val)" :min="0" :max="9999" :style="{ width: '100%' }"
|
||||
placeholder="900" addon-before="[0]"></a-input-number>
|
||||
@change="(val) => updateTestseed(0, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="900"
|
||||
addon-before="[0]"></a-input-number>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-input-number
|
||||
:value="(inbound.settings.testseed && inbound.settings.testseed[1] !== undefined) ? inbound.settings.testseed[1] : 500"
|
||||
@change="(val) => updateTestseed(1, val)" :min="0" :max="9999" :style="{ width: '100%' }"
|
||||
placeholder="500" addon-before="[1]"></a-input-number>
|
||||
@change="(val) => updateTestseed(1, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="500"
|
||||
addon-before="[1]"></a-input-number>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-input-number
|
||||
:value="(inbound.settings.testseed && inbound.settings.testseed[2] !== undefined) ? inbound.settings.testseed[2] : 900"
|
||||
@change="(val) => updateTestseed(2, val)" :min="0" :max="9999" :style="{ width: '100%' }"
|
||||
placeholder="900" addon-before="[2]"></a-input-number>
|
||||
@change="(val) => updateTestseed(2, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="900"
|
||||
addon-before="[2]"></a-input-number>
|
||||
</a-col>
|
||||
<a-col :span="6">
|
||||
<a-input-number
|
||||
:value="(inbound.settings.testseed && inbound.settings.testseed[3] !== undefined) ? inbound.settings.testseed[3] : 256"
|
||||
@change="(val) => updateTestseed(3, val)" :min="0" :max="9999" :style="{ width: '100%' }"
|
||||
placeholder="256" addon-before="[3]"></a-input-number>
|
||||
@change="(val) => updateTestseed(3, val)" :min="0" :max="9999" :style="{ width: '100%' }" placeholder="256"
|
||||
addon-before="[3]"></a-input-number>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-space :size="8" :style="{ marginTop: '8px' }">
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{{define "form/sniffing"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item>
|
||||
<span slot="label">
|
||||
{{ i18n "enabled" }}
|
||||
@@ -19,9 +15,7 @@
|
||||
<template v-if="inbound.sniffing.enabled">
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-checkbox-group v-model="inbound.sniffing.destOverride">
|
||||
<a-checkbox v-for="key,value in SNIFFING_OPTION" :value="key"
|
||||
>[[ value ]]</a-checkbox
|
||||
>
|
||||
<a-checkbox v-for="key,value in SNIFFING_OPTION" :value="key">[[ value ]]</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="Metadata Only">
|
||||
@@ -31,22 +25,12 @@
|
||||
<a-switch v-model="inbound.sniffing.routeOnly"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="IPs Excluded">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="inbound.sniffing.ipsExcluded"
|
||||
:style="{ width: '100%' }"
|
||||
:token-separators="[',']"
|
||||
placeholder="IP/CIDR/geoip:*/ext:*"
|
||||
></a-select>
|
||||
<a-select mode="tags" v-model="inbound.sniffing.ipsExcluded" :style="{ width: '100%' }" :token-separators="[',']"
|
||||
placeholder="IP/CIDR/geoip:*/ext:*"></a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Domains Excluded">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="inbound.sniffing.domainsExcluded"
|
||||
:style="{ width: '100%' }"
|
||||
:token-separators="[',']"
|
||||
placeholder="domain:*/ext:*"
|
||||
></a-select>
|
||||
<a-select mode="tags" v-model="inbound.sniffing.domainsExcluded" :style="{ width: '100%' }"
|
||||
:token-separators="[',']" placeholder="domain:*/ext:*"></a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</a-form>
|
||||
|
||||
@@ -1,65 +1,29 @@
|
||||
{{define "form/externalProxy"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
|
||||
<a-form-item label="External Proxy">
|
||||
<a-switch v-model="externalProxy"></a-switch>
|
||||
<a-button
|
||||
icon="plus"
|
||||
v-if="externalProxy"
|
||||
type="primary"
|
||||
:style="{ marginLeft: '10px' }"
|
||||
size="small"
|
||||
@click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})"
|
||||
></a-button>
|
||||
<a-button icon="plus" v-if="externalProxy" type="primary" :style="{ marginLeft: '10px' }" size="small"
|
||||
@click="inbound.stream.externalProxy.push({forceTls: 'same', dest: '', port: 443, remark: ''})"></a-button>
|
||||
</a-form-item>
|
||||
<a-input-group
|
||||
:style="{ margin: '8px 0' }"
|
||||
compact
|
||||
v-for="(row, index) in inbound.stream.externalProxy"
|
||||
>
|
||||
<a-input-group :style="{ margin: '8px 0' }" compact v-for="(row, index) in inbound.stream.externalProxy">
|
||||
<template>
|
||||
<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 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: '30%' }"
|
||||
v-model.trim="row.dest"
|
||||
placeholder='{{ i18n "host" }}'
|
||||
></a-input>
|
||||
<a-input :style="{ width: '30%' }" v-model.trim="row.dest" placeholder='{{ i18n "host" }}'></a-input>
|
||||
<a-tooltip title='{{ i18n "pages.inbounds.port" }}'>
|
||||
<a-input-number
|
||||
:style="{ width: '15%' }"
|
||||
v-model.number="row.port"
|
||||
min="1"
|
||||
max="65535"
|
||||
></a-input-number>
|
||||
<a-input-number :style="{ width: '15%' }" v-model.number="row.port" min="1" max="65535"></a-input-number>
|
||||
</a-tooltip>
|
||||
<a-input
|
||||
:style="{ width: '30%', top: '0' }"
|
||||
v-model.trim="row.remark"
|
||||
placeholder='{{ i18n "remark" }}'
|
||||
>
|
||||
<a-input :style="{ width: '30%', top: '0' }" v-model.trim="row.remark" placeholder='{{ i18n "remark" }}'>
|
||||
<template slot="addonAfter">
|
||||
<a-button
|
||||
icon="minus"
|
||||
size="small"
|
||||
@click="inbound.stream.externalProxy.splice(index, 1)"
|
||||
></a-button>
|
||||
<a-button icon="minus" size="small" @click="inbound.stream.externalProxy.splice(index, 1)"></a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
|
||||
@@ -1,44 +1,24 @@
|
||||
{{define "form/streamFinalMask"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
v-if="inbound.protocol == Protocols.HYSTERIA || ['kcp', 'xhttp', 'raw', 'tcp', 'httpupgrade', 'ws', 'grpc'].includes(inbound.stream.network)"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"
|
||||
v-if="inbound.protocol == Protocols.HYSTERIA || ['kcp', 'xhttp', 'raw', 'tcp', 'httpupgrade', 'ws', 'grpc'].includes(inbound.stream.network)">
|
||||
<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
|
||||
|
||||
<!-- TCP Masks – for raw/tcp/httpupgrade/ws/grpc/xhttp -->
|
||||
<template v-if="['raw', 'tcp', 'httpupgrade', 'ws', 'grpc', 'xhttp'].includes(inbound.stream.network)">
|
||||
<a-form-item label="TCP Masks">
|
||||
<a-button
|
||||
icon="plus"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="inbound.stream.addTcpMask('fragment')"
|
||||
></a-button>
|
||||
<a-button icon="plus" type="primary" size="small" @click="inbound.stream.addTcpMask('fragment')"></a-button>
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.finalmask.tcp && inbound.stream.finalmask.tcp.length > 0">
|
||||
<a-form
|
||||
v-for="(mask, index) in inbound.stream.finalmask.tcp"
|
||||
:key="index"
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form v-for="(mask, index) in inbound.stream.finalmask.tcp" :key="index" :colon="false"
|
||||
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-divider :style="{ margin: '0' }">
|
||||
TCP Mask [[ index + 1 ]]
|
||||
<a-icon
|
||||
type="delete"
|
||||
@click="() => inbound.stream.delTcpMask(index)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"
|
||||
></a-icon>
|
||||
<a-icon type="delete" @click="() => inbound.stream.delTcpMask(index)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||
</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
<a-select
|
||||
v-model="mask.type"
|
||||
@change="(type) => { mask.settings = mask._getDefaultSettings(type, {}); }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="mask.type" @change="(type) => { mask.settings = mask._getDefaultSettings(type, {}); }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="fragment">Fragment</a-select-option>
|
||||
<a-select-option value="header-custom">Header Custom</a-select-option>
|
||||
<a-select-option value="sudoku">Sudoku</a-select-option>
|
||||
@@ -48,10 +28,7 @@
|
||||
<!-- Fragment settings -->
|
||||
<template v-if="mask.type === 'fragment'">
|
||||
<a-form-item label="Packets">
|
||||
<a-select
|
||||
v-model="mask.settings.packets"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="mask.settings.packets" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="tlshello">tlshello</a-select-option>
|
||||
<a-select-option value="1-3">1-3</a-select-option>
|
||||
<a-select-option value="1-5">1-5</a-select-option>
|
||||
@@ -94,21 +71,14 @@
|
||||
<template v-if="mask.type === 'header-custom'">
|
||||
<!-- Clients -->
|
||||
<a-form-item label="Clients">
|
||||
<a-button
|
||||
icon="plus"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="mask.settings.clients.push([{delay: 0, rand: 0, randRange: '0-255', type: 'array', packet: []}])"
|
||||
></a-button>
|
||||
<a-button icon="plus" type="primary" size="small"
|
||||
@click="mask.settings.clients.push([{delay: 0, rand: 0, randRange: '0-255', type: 'array', packet: []}])"></a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(group, gi) in mask.settings.clients" :key="'cg'+gi">
|
||||
<a-divider :style="{ margin: '0' }">
|
||||
Clients Group [[ gi + 1 ]]
|
||||
<a-icon
|
||||
type="delete"
|
||||
@click="mask.settings.clients.splice(gi, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer', marginLeft: '8px' }"
|
||||
></a-icon>
|
||||
<a-icon type="delete" @click="mask.settings.clients.splice(gi, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer', marginLeft: '8px' }"></a-icon>
|
||||
</a-divider>
|
||||
<template v-for="(item, ii) in group" :key="'ci'+ii">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
@@ -133,7 +103,8 @@
|
||||
</template>
|
||||
<a-form-item v-else label="Packet">
|
||||
<a-input-group compact v-if="item.type === 'base64'">
|
||||
<a-input v-model.trim="item.packet" placeholder="binary data" :style="{ width: 'calc(100% - 32px)' }" />
|
||||
<a-input v-model.trim="item.packet" placeholder="binary data"
|
||||
:style="{ width: 'calc(100% - 32px)' }" />
|
||||
<a-button icon="reload" @click="item.packet = RandomUtil.randomBase64()" />
|
||||
</a-input-group>
|
||||
<a-input v-else v-model.trim="item.packet" placeholder="binary data" />
|
||||
@@ -143,21 +114,14 @@
|
||||
|
||||
<!-- Servers -->
|
||||
<a-form-item label="Servers">
|
||||
<a-button
|
||||
icon="plus"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="mask.settings.servers.push([{delay: 0, rand: 0, randRange: '0-255', type: 'array', packet: []}])"
|
||||
></a-button>
|
||||
<a-button icon="plus" type="primary" size="small"
|
||||
@click="mask.settings.servers.push([{delay: 0, rand: 0, randRange: '0-255', type: 'array', packet: []}])"></a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(group, gi) in mask.settings.servers" :key="'sg'+gi">
|
||||
<a-divider :style="{ margin: '0' }">
|
||||
Servers Group [[ gi + 1 ]]
|
||||
<a-icon
|
||||
type="delete"
|
||||
@click="mask.settings.servers.splice(gi, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer', marginLeft: '8px' }"
|
||||
></a-icon>
|
||||
<a-icon type="delete" @click="mask.settings.servers.splice(gi, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer', marginLeft: '8px' }"></a-icon>
|
||||
</a-divider>
|
||||
<template v-for="(item, ii) in group" :key="'si'+ii">
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
@@ -182,7 +146,8 @@
|
||||
</template>
|
||||
<a-form-item v-else label="Packet">
|
||||
<a-input-group compact v-if="item.type === 'base64'">
|
||||
<a-input v-model.trim="item.packet" placeholder="binary data" :style="{ width: 'calc(100% - 32px)' }" />
|
||||
<a-input v-model.trim="item.packet" placeholder="binary data"
|
||||
:style="{ width: 'calc(100% - 32px)' }" />
|
||||
<a-button icon="reload" @click="item.packet = RandomUtil.randomBase64()" />
|
||||
</a-input-group>
|
||||
<a-input v-else v-model.trim="item.packet" placeholder="binary data" />
|
||||
@@ -197,61 +162,33 @@
|
||||
|
||||
<template v-if="inbound.protocol == Protocols.HYSTERIA || inbound.stream.network == 'kcp'">
|
||||
<a-form-item label="UDP Masks">
|
||||
<a-button
|
||||
icon="plus"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="inbound.stream.addUdpMask(inbound.protocol === Protocols.HYSTERIA ? 'salamander' : 'mkcp-aes128gcm')"
|
||||
></a-button>
|
||||
<a-button icon="plus" type="primary" size="small"
|
||||
@click="inbound.stream.addUdpMask(inbound.protocol === Protocols.HYSTERIA ? 'salamander' : 'mkcp-aes128gcm')"></a-button>
|
||||
</a-form-item>
|
||||
<template
|
||||
v-if="inbound.stream.finalmask.udp && inbound.stream.finalmask.udp.length > 0"
|
||||
>
|
||||
<a-form
|
||||
v-for="(mask, index) in inbound.stream.finalmask.udp"
|
||||
:key="index"
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<template v-if="inbound.stream.finalmask.udp && inbound.stream.finalmask.udp.length > 0">
|
||||
<a-form v-for="(mask, index) in inbound.stream.finalmask.udp" :key="index" :colon="false"
|
||||
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-divider :style="{ margin: '0' }">
|
||||
UDP Mask [[ index + 1 ]]
|
||||
<a-icon
|
||||
type="delete"
|
||||
@click="() => inbound.stream.delUdpMask(index)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"
|
||||
></a-icon>
|
||||
<a-icon type="delete" @click="() => inbound.stream.delUdpMask(index)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||
</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
<a-select
|
||||
v-model="mask.type"
|
||||
<a-select v-model="mask.type"
|
||||
@change="(type) => { mask.settings = mask._getDefaultSettings(type, {}); if(inbound.stream.network === 'kcp') { inbound.stream.kcp.mtu = type === 'xdns' ? 900 : 1350; } }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<template v-if="inbound.protocol === Protocols.HYSTERIA">
|
||||
<a-select-option value="salamander"
|
||||
>Salamander (Hysteria2)</a-select-option
|
||||
>
|
||||
<a-select-option value="salamander">Salamander (Hysteria2)</a-select-option>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-select-option value="mkcp-aes128gcm"
|
||||
>mKCP AES-128-GCM</a-select-option
|
||||
>
|
||||
<a-select-option value="mkcp-aes128gcm">mKCP AES-128-GCM</a-select-option>
|
||||
<a-select-option value="header-dns">Header DNS</a-select-option>
|
||||
<a-select-option value="header-dtls"
|
||||
>Header DTLS 1.2</a-select-option
|
||||
>
|
||||
<a-select-option value="header-dtls">Header DTLS 1.2</a-select-option>
|
||||
<a-select-option value="header-srtp">Header SRTP</a-select-option>
|
||||
<a-select-option value="header-utp">Header uTP</a-select-option>
|
||||
<a-select-option value="header-wechat"
|
||||
>Header WeChat Video</a-select-option
|
||||
>
|
||||
<a-select-option value="header-wireguard"
|
||||
>Header WireGuard</a-select-option
|
||||
>
|
||||
<a-select-option value="mkcp-original"
|
||||
>mKCP Original</a-select-option
|
||||
>
|
||||
<a-select-option value="header-wechat">Header WeChat Video</a-select-option>
|
||||
<a-select-option value="header-wireguard">Header WireGuard</a-select-option>
|
||||
<a-select-option value="mkcp-original">mKCP Original</a-select-option>
|
||||
<a-select-option value="xdns">xDNS</a-select-option>
|
||||
<a-select-option value="xicmp">xICMP</a-select-option>
|
||||
<a-select-option value="header-custom">Header Custom</a-select-option>
|
||||
@@ -259,30 +196,16 @@
|
||||
</template>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Password"
|
||||
v-if="['mkcp-aes128gcm', 'salamander'].includes(mask.type)"
|
||||
>
|
||||
<a-input
|
||||
v-model.trim="mask.settings.password"
|
||||
placeholder="Obfuscation password"
|
||||
></a-input>
|
||||
<a-form-item label="Password" v-if="['mkcp-aes128gcm', 'salamander'].includes(mask.type)">
|
||||
<a-input v-model.trim="mask.settings.password" placeholder="Obfuscation password"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Domain" v-if="mask.type === 'header-dns'">
|
||||
<a-input
|
||||
v-model.trim="mask.settings.domain"
|
||||
placeholder="e.g., www.example.com"
|
||||
></a-input>
|
||||
<a-input v-model.trim="mask.settings.domain" placeholder="e.g., www.example.com"></a-input>
|
||||
</a-form-item>
|
||||
<template v-if="mask.type === 'xdns'">
|
||||
<a-form-item label="Domains">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="mask.settings.domains"
|
||||
:style="{ width: '100%' }"
|
||||
:token-separators="[',']"
|
||||
placeholder="e.g., www.example.com"
|
||||
></a-select>
|
||||
<a-select mode="tags" v-model="mask.settings.domains" :style="{ width: '100%' }" :token-separators="[',']"
|
||||
placeholder="e.g., www.example.com"></a-select>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-if="mask.type === 'noise'">
|
||||
@@ -290,28 +213,18 @@
|
||||
<a-input-number v-model.number="mask.settings.reset" :min="0" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Noise">
|
||||
<a-button
|
||||
icon="plus"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="mask.settings.noise.push({rand: '1-8192', randRange: '0-255', type: 'array', packet: [], delay: '10-20'})"
|
||||
></a-button>
|
||||
<a-button icon="plus" type="primary" size="small"
|
||||
@click="mask.settings.noise.push({rand: '1-8192', randRange: '0-255', type: 'array', packet: [], delay: '10-20'})"></a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(n, index) in mask.settings.noise" :key="index">
|
||||
<a-divider :style="{ margin: '0' }">
|
||||
Noise [[ index + 1 ]]
|
||||
<a-icon
|
||||
type="delete"
|
||||
@click="() => mask.settings.noise.splice(index, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"
|
||||
></a-icon>
|
||||
<a-icon type="delete" @click="() => mask.settings.noise.splice(index, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||
</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
<a-select
|
||||
v-model="n.type"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
@change="t => { if(t === 'base64') n.packet = RandomUtil.randomBase64(); else if(t === 'array') n.packet = []; else n.packet = ''; }"
|
||||
>
|
||||
<a-select v-model="n.type" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
@change="t => { if(t === 'base64') n.packet = RandomUtil.randomBase64(); else if(t === 'array') n.packet = []; else n.packet = ''; }">
|
||||
<a-select-option value="array">Array</a-select-option>
|
||||
<a-select-option value="str">String</a-select-option>
|
||||
<a-select-option value="hex">Hex</a-select-option>
|
||||
@@ -340,28 +253,18 @@
|
||||
</template>
|
||||
<template v-if="mask.type === 'header-custom'">
|
||||
<a-form-item label="Client">
|
||||
<a-button
|
||||
icon="plus"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="mask.settings.client.push({rand: 0, randRange: '0-255', type: 'array', packet: []})"
|
||||
></a-button>
|
||||
<a-button icon="plus" type="primary" size="small"
|
||||
@click="mask.settings.client.push({rand: 0, randRange: '0-255', type: 'array', packet: []})"></a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(c, index) in mask.settings.client" :key="index">
|
||||
<a-divider :style="{ margin: '0' }">
|
||||
Client [[ index + 1 ]]
|
||||
<a-icon
|
||||
type="delete"
|
||||
@click="mask.settings.client.splice(index, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"
|
||||
></a-icon>
|
||||
<a-icon type="delete" @click="mask.settings.client.splice(index, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||
</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
<a-select
|
||||
v-model="c.type"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
@change="t => { if(t === 'base64') c.packet = RandomUtil.randomBase64(); else if(t === 'array') c.packet = []; else c.packet = ''; }"
|
||||
>
|
||||
<a-select v-model="c.type" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
@change="t => { if(t === 'base64') c.packet = RandomUtil.randomBase64(); else if(t === 'array') c.packet = []; else c.packet = ''; }">
|
||||
<a-select-option value="array">Array</a-select-option>
|
||||
<a-select-option value="str">String</a-select-option>
|
||||
<a-select-option value="hex">Hex</a-select-option>
|
||||
@@ -386,28 +289,18 @@
|
||||
</template>
|
||||
<a-divider :style="{ margin: '0' }"></a-divider>
|
||||
<a-form-item label="Server">
|
||||
<a-button
|
||||
icon="plus"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="mask.settings.server.push({rand: 0, randRange: '0-255', type: 'array', packet: []})"
|
||||
></a-button>
|
||||
<a-button icon="plus" type="primary" size="small"
|
||||
@click="mask.settings.server.push({rand: 0, randRange: '0-255', type: 'array', packet: []})"></a-button>
|
||||
</a-form-item>
|
||||
<template v-for="(s, index) in mask.settings.server" :key="index">
|
||||
<a-divider :style="{ margin: '0' }">
|
||||
Server [[ index + 1 ]]
|
||||
<a-icon
|
||||
type="delete"
|
||||
@click="mask.settings.server.splice(index, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"
|
||||
></a-icon>
|
||||
<a-icon type="delete" @click="mask.settings.server.splice(index, 1)"
|
||||
:style="{ color: 'rgb(255, 77, 79)', cursor: 'pointer' }"></a-icon>
|
||||
</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.xray.outbound.type" }}'>
|
||||
<a-select
|
||||
v-model="s.type"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
@change="t => { if(t === 'base64') s.packet = RandomUtil.randomBase64(); else if(t === 'array') s.packet = []; else s.packet = ''; }"
|
||||
>
|
||||
<a-select v-model="s.type" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
@change="t => { if(t === 'base64') s.packet = RandomUtil.randomBase64(); else if(t === 'array') s.packet = []; else s.packet = ''; }">
|
||||
<a-select-option value="array">Array</a-select-option>
|
||||
<a-select-option value="str">String</a-select-option>
|
||||
<a-select-option value="hex">Hex</a-select-option>
|
||||
@@ -450,10 +343,8 @@
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.finalmask.enableQuicParams">
|
||||
<a-form-item label="Congestion">
|
||||
<a-select
|
||||
v-model="inbound.stream.finalmask.quicParams.congestion"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.finalmask.quicParams.congestion"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="reno">Reno</a-select-option>
|
||||
<a-select-option value="bbr">BBR</a-select-option>
|
||||
<a-select-option value="brutal">Brutal</a-select-option>
|
||||
@@ -492,19 +383,24 @@
|
||||
<a-switch v-model="inbound.stream.finalmask.quicParams.disablePathMTUDiscovery"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Incoming Streams">
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.maxIncomingStreams" :min="0" placeholder="0 = default" />
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.maxIncomingStreams" :min="0"
|
||||
placeholder="0 = default" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Init Stream Window">
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.initStreamReceiveWindow" :min="0" placeholder="0 = default" />
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.initStreamReceiveWindow" :min="0"
|
||||
placeholder="0 = default" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Stream Window">
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.maxStreamReceiveWindow" :min="0" placeholder="0 = default" />
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.maxStreamReceiveWindow" :min="0"
|
||||
placeholder="0 = default" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Init Conn Window">
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.initConnectionReceiveWindow" :min="0" placeholder="0 = default" />
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.initConnectionReceiveWindow" :min="0"
|
||||
placeholder="0 = default" />
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Conn Window">
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.maxConnectionReceiveWindow" :min="0" placeholder="0 = default" />
|
||||
<a-input-number v-model.number="inbound.stream.finalmask.quicParams.maxConnectionReceiveWindow" :min="0"
|
||||
placeholder="0 = default" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{{define "form/streamGRPC"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Service Name">
|
||||
<a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input>
|
||||
</a-form-item>
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
{{define "form/streamHTTPUpgrade"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Proxy Protocol">
|
||||
<a-switch
|
||||
v-model="inbound.stream.httpupgrade.acceptProxyProtocol"
|
||||
></a-switch>
|
||||
<a-switch v-model="inbound.stream.httpupgrade.acceptProxyProtocol"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "host" }}'>
|
||||
<a-input v-model.trim="inbound.stream.httpupgrade.host"></a-input>
|
||||
@@ -16,37 +10,18 @@
|
||||
<a-input v-model.trim="inbound.stream.httpupgrade.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button
|
||||
icon="plus"
|
||||
size="small"
|
||||
@click="inbound.stream.httpupgrade.addHeader('', '')"
|
||||
></a-button>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.httpupgrade.addHeader('', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group
|
||||
compact
|
||||
v-for="(header, index) in inbound.stream.httpupgrade.headers"
|
||||
>
|
||||
<a-input
|
||||
:style="{ width: '50%' }"
|
||||
v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'
|
||||
>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }"
|
||||
>[[ index+1 ]]</template
|
||||
>
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.httpupgrade.headers">
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
||||
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.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'
|
||||
>
|
||||
<a-button
|
||||
icon="minus"
|
||||
slot="addonAfter"
|
||||
size="small"
|
||||
@click="inbound.stream.httpupgrade.removeHeader(index)"
|
||||
></a-button>
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button icon="minus" slot="addonAfter" size="small"
|
||||
@click="inbound.stream.httpupgrade.removeHeader(index)"></a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
{{define "form/streamHysteria"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="UDP Idle Timeout">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.hysteria.udpIdleTimeout"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.hysteria.udpIdleTimeout" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Masquerade">
|
||||
<a-switch v-model="inbound.stream.hysteria.masqueradeSwitch"></a-switch>
|
||||
@@ -16,83 +9,48 @@
|
||||
<template v-if="inbound.stream.hysteria.masqueradeSwitch">
|
||||
<a-divider :style="{ margin: '5px 0 0' }">Masquerade</a-divider>
|
||||
<a-form-item label="Type">
|
||||
<a-select
|
||||
v-model="inbound.stream.hysteria.masquerade.type"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.hysteria.masquerade.type" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="file">File</a-select-option>
|
||||
<a-select-option value="proxy">Proxy</a-select-option>
|
||||
<a-select-option value="string">String</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Dir"
|
||||
v-if="inbound.stream.hysteria.masquerade.type === 'file'"
|
||||
>
|
||||
<a-form-item label="Dir" v-if="inbound.stream.hysteria.masquerade.type === 'file'">
|
||||
<a-input v-model.trim="inbound.stream.hysteria.masquerade.dir"></a-input>
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.hysteria.masquerade.type === 'proxy'">
|
||||
<a-form-item label="URL">
|
||||
<a-input
|
||||
v-model.trim="inbound.stream.hysteria.masquerade.url"
|
||||
></a-input>
|
||||
<a-input v-model.trim="inbound.stream.hysteria.masquerade.url"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Rewrite Host">
|
||||
<a-switch
|
||||
v-model="inbound.stream.hysteria.masquerade.rewriteHost"
|
||||
></a-switch>
|
||||
<a-switch v-model="inbound.stream.hysteria.masquerade.rewriteHost"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="Insecure">
|
||||
<a-switch
|
||||
v-model="inbound.stream.hysteria.masquerade.insecure"
|
||||
></a-switch>
|
||||
<a-switch v-model="inbound.stream.hysteria.masquerade.insecure"></a-switch>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-if="inbound.stream.hysteria.masquerade.type === 'string'">
|
||||
<a-form-item label="Content">
|
||||
<a-input
|
||||
v-model.trim="inbound.stream.hysteria.masquerade.content"
|
||||
></a-input>
|
||||
<a-input v-model.trim="inbound.stream.hysteria.masquerade.content"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button
|
||||
size="small"
|
||||
@click="inbound.stream.hysteria.masquerade.addHeader('', '')"
|
||||
>+</a-button
|
||||
>
|
||||
<a-button size="small" @click="inbound.stream.hysteria.masquerade.addHeader('', '')">+</a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group
|
||||
compact
|
||||
v-for="(header, index) in inbound.stream.hysteria.masquerade.headers"
|
||||
>
|
||||
<a-input
|
||||
style="width: 50%"
|
||||
v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'
|
||||
>
|
||||
<template slot="addonBefore" style="margin: 0"
|
||||
>[[ index+1 ]]</template
|
||||
>
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.hysteria.masquerade.headers">
|
||||
<a-input style="width: 50%" v-model.trim="header.name"
|
||||
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.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'
|
||||
>
|
||||
<a-button
|
||||
slot="addonAfter"
|
||||
size="small"
|
||||
@click="inbound.stream.hysteria.masquerade.removeHeader(index)"
|
||||
>-</a-button
|
||||
>
|
||||
<a-input style="width: 50%" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button slot="addonAfter" size="small"
|
||||
@click="inbound.stream.hysteria.masquerade.removeHeader(index)">-</a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="Status Code">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.hysteria.masquerade.statusCode"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.hysteria.masquerade.statusCode"></a-input-number>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -1,46 +1,22 @@
|
||||
{{define "form/streamKCP"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="MTU">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.kcp.mtu"
|
||||
:min="576"
|
||||
:max="1460"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.mtu" :min="576" :max="1460"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TTI (ms)">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.kcp.tti"
|
||||
:min="10"
|
||||
:max="100"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.tti" :min="10" :max="100"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Uplink (MB/s)">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.kcp.upCap"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.upCap" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Downlink (MB/s)">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.kcp.downCap"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.downCap" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="CWND Multiplier">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.kcp.cwndMultiplier"
|
||||
:min="1"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.cwndMultiplier" :min="1"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Max Sending Window">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.kcp.maxSendingWindow"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.kcp.maxSendingWindow" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -1,18 +1,10 @@
|
||||
{{define "form/streamSettings"}}
|
||||
<!-- select stream network -->
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
v-if="inbound.protocol != Protocols.HYSTERIA"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"
|
||||
v-if="inbound.protocol != Protocols.HYSTERIA">
|
||||
<a-form-item label='{{ i18n "transmission" }}'>
|
||||
<a-select
|
||||
v-model="inbound.stream.network"
|
||||
:style="{ width: '75%' }"
|
||||
@change="streamNetworkChange"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.network" :style="{ width: '75%' }" @change="streamNetworkChange"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="tcp">TCP (RAW)</a-select-option>
|
||||
<a-select-option value="kcp">mKCP</a-select-option>
|
||||
<a-select-option value="ws">WebSocket</a-select-option>
|
||||
|
||||
@@ -1,49 +1,27 @@
|
||||
{{define "form/streamSockopt"}}
|
||||
<a-divider :style="{ margin: '5px 0 0' }"></a-divider>
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Sockopt">
|
||||
<a-switch v-model="inbound.stream.sockoptSwitch"></a-switch>
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.sockoptSwitch">
|
||||
<a-form-item label="Route Mark">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.sockopt.mark"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.mark" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Keep Alive Interval">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.sockopt.tcpKeepAliveInterval"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpKeepAliveInterval" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Keep Alive Idle">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.sockopt.tcpKeepAliveIdle"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpKeepAliveIdle" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Max Seg">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.sockopt.tcpMaxSeg"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpMaxSeg" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP User Timeout">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.sockopt.tcpUserTimeout"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpUserTimeout" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Window Clamp">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.sockopt.tcpWindowClamp"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.sockopt.tcpWindowClamp" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="Proxy Protocol">
|
||||
<a-switch v-model="inbound.stream.sockopt.acceptProxyProtocol"></a-switch>
|
||||
@@ -61,33 +39,20 @@
|
||||
<a-switch v-model.trim="inbound.stream.sockopt.V6Only"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label="Domain Strategy">
|
||||
<a-select
|
||||
v-model="inbound.stream.sockopt.domainStrategy"
|
||||
:style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select-option v-for="key in DOMAIN_STRATEGY_OPTION" :value="key"
|
||||
>[[ key ]]</a-select-option
|
||||
>
|
||||
<a-select v-model="inbound.stream.sockopt.domainStrategy" :style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in DOMAIN_STRATEGY_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="TCP Congestion">
|
||||
<a-select
|
||||
v-model="inbound.stream.sockopt.tcpcongestion"
|
||||
:style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select-option v-for="key in TCP_CONGESTION_OPTION" :value="key"
|
||||
>[[ key ]]</a-select-option
|
||||
>
|
||||
<a-select v-model="inbound.stream.sockopt.tcpcongestion" :style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in TCP_CONGESTION_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="TProxy">
|
||||
<a-select
|
||||
v-model="inbound.stream.sockopt.tproxy"
|
||||
:style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.sockopt.tproxy" :style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="off">Off</a-select-option>
|
||||
<a-select-option value="redirect">Redirect</a-select-option>
|
||||
<a-select-option value="tproxy">TProxy</a-select-option>
|
||||
@@ -100,15 +65,9 @@
|
||||
<a-input v-model="inbound.stream.sockopt.interfaceName"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Trusted X-Forwarded-For">
|
||||
<a-select
|
||||
mode="tags"
|
||||
v-model="inbound.stream.sockopt.trustedXForwardedFor"
|
||||
:style="{ width: '100%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select-option value="CF-Connecting-IP"
|
||||
>CF-Connecting-IP</a-select-option
|
||||
>
|
||||
<a-select mode="tags" v-model="inbound.stream.sockopt.trustedXForwardedFor" :style="{ width: '100%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="CF-Connecting-IP">CF-Connecting-IP</a-select-option>
|
||||
<a-select-option value="X-Real-IP">X-Real-IP</a-select-option>
|
||||
<a-select-option value="True-Client-IP">True-Client-IP</a-select-option>
|
||||
<a-select-option value="X-Client-IP">X-Client-IP</a-select-option>
|
||||
|
||||
@@ -1,31 +1,19 @@
|
||||
{{define "form/streamTCP"}}
|
||||
<!-- tcp type -->
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Proxy Protocol" v-if="inbound.canEnableTls()">
|
||||
<a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label='HTTP {{ i18n "camouflage" }}'>
|
||||
<a-switch
|
||||
:checked="inbound.stream.tcp.type === 'http'"
|
||||
@change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'"
|
||||
></a-switch>
|
||||
<a-switch :checked="inbound.stream.tcp.type === 'http'"
|
||||
@change="checked => inbound.stream.tcp.type = checked ? 'http' : 'none'"></a-switch>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
<a-form
|
||||
v-if="inbound.stream.tcp.type === 'http'"
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form v-if="inbound.stream.tcp.type === 'http'" :colon="false" :label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }">
|
||||
<!-- tcp request -->
|
||||
<a-divider :style="{ margin: '0' }"
|
||||
>{{ i18n "pages.inbounds.stream.general.request" }}</a-divider
|
||||
>
|
||||
<a-divider :style="{ margin: '0' }">{{ i18n "pages.inbounds.stream.general.request" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input>
|
||||
</a-form-item>
|
||||
@@ -33,66 +21,35 @@
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
<template slot="label"
|
||||
>{{ i18n "pages.inbounds.stream.tcp.path" }}
|
||||
<a-button
|
||||
icon="plus"
|
||||
size="small"
|
||||
@click="inbound.stream.tcp.request.addPath('/')"
|
||||
></a-button>
|
||||
<template slot="label">{{ i18n "pages.inbounds.stream.tcp.path" }}
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.tcp.request.addPath('/')"></a-button>
|
||||
</template>
|
||||
<template v-for="(path, index) in inbound.stream.tcp.request.path">
|
||||
<a-input v-model.trim="inbound.stream.tcp.request.path[index]">
|
||||
<a-button
|
||||
icon="minus"
|
||||
size="small"
|
||||
slot="addonAfter"
|
||||
@click="inbound.stream.tcp.request.removePath(index)"
|
||||
v-if="inbound.stream.tcp.request.path.length>1"
|
||||
></a-button>
|
||||
<a-button icon="minus" size="small" slot="addonAfter" @click="inbound.stream.tcp.request.removePath(index)"
|
||||
v-if="inbound.stream.tcp.request.path.length>1"></a-button>
|
||||
</a-input>
|
||||
</template>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button
|
||||
icon="plus"
|
||||
size="small"
|
||||
@click="inbound.stream.tcp.request.addHeader('Host', '')"
|
||||
></a-button>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.tcp.request.addHeader('Host', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group
|
||||
compact
|
||||
v-for="(header, index) in inbound.stream.tcp.request.headers"
|
||||
>
|
||||
<a-input
|
||||
:style="{ width: '50%' }"
|
||||
v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'
|
||||
>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }"
|
||||
>[[ index+1 ]]</template
|
||||
>
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
||||
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.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'
|
||||
>
|
||||
<a-button
|
||||
icon="minus"
|
||||
slot="addonAfter"
|
||||
size="small"
|
||||
@click="inbound.stream.tcp.request.removeHeader(index)"
|
||||
></a-button>
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button icon="minus" slot="addonAfter" size="small"
|
||||
@click="inbound.stream.tcp.request.removeHeader(index)"></a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
|
||||
<!-- tcp response -->
|
||||
<a-divider :style="{ margin: '0' }"
|
||||
>{{ i18n "pages.inbounds.stream.general.response" }}</a-divider
|
||||
>
|
||||
<a-divider :style="{ margin: '0' }">{{ i18n "pages.inbounds.stream.general.response" }}</a-divider>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.version" }}'>
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input>
|
||||
</a-form-item>
|
||||
@@ -103,37 +60,19 @@
|
||||
<a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}'>
|
||||
<a-button
|
||||
icon="plus"
|
||||
size="small"
|
||||
@click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')"
|
||||
></a-button>
|
||||
<a-button icon="plus" size="small"
|
||||
@click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group
|
||||
compact
|
||||
v-for="(header, index) in inbound.stream.tcp.response.headers"
|
||||
>
|
||||
<a-input
|
||||
:style="{ width: '50%' }"
|
||||
v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'
|
||||
>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }"
|
||||
>[[ index+1 ]]</template
|
||||
>
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.tcp.response.headers">
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
||||
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.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'
|
||||
>
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<template slot="addonAfter">
|
||||
<a-button
|
||||
icon="minus"
|
||||
size="small"
|
||||
@click="inbound.stream.tcp.response.removeHeader(index)"
|
||||
></a-button>
|
||||
<a-button icon="minus" size="small" @click="inbound.stream.tcp.response.removeHeader(index)"></a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{{define "form/streamWS"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label="Proxy Protocol">
|
||||
<a-switch v-model="inbound.stream.ws.acceptProxyProtocol"></a-switch>
|
||||
</a-form-item>
|
||||
@@ -14,40 +10,20 @@
|
||||
<a-input v-model.trim="inbound.stream.ws.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Heartbeat Period">
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.ws.heartbeatPeriod"
|
||||
:min="0"
|
||||
></a-input-number>
|
||||
<a-input-number v-model.number="inbound.stream.ws.heartbeatPeriod" :min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button
|
||||
icon="plus"
|
||||
size="small"
|
||||
@click="inbound.stream.ws.addHeader('', '')"
|
||||
></a-button>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.ws.addHeader('', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.ws.headers">
|
||||
<a-input
|
||||
:style="{ width: '50%' }"
|
||||
v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'
|
||||
>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }"
|
||||
>[[ index+1 ]]</template
|
||||
>
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
||||
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.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'
|
||||
>
|
||||
<a-button
|
||||
icon="minus"
|
||||
slot="addonAfter"
|
||||
size="small"
|
||||
@click="inbound.stream.ws.removeHeader(index)"
|
||||
></a-button>
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button icon="minus" slot="addonAfter" size="small" @click="inbound.stream.ws.removeHeader(index)"></a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
{{define "form/streamXHTTP"}}
|
||||
<a-form
|
||||
:colon="false"
|
||||
:label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }"
|
||||
>
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='{{ i18n "host" }}'>
|
||||
<a-input v-model.trim="inbound.stream.xhttp.host"></a-input>
|
||||
</a-form-item>
|
||||
@@ -11,69 +7,34 @@
|
||||
<a-input v-model.trim="inbound.stream.xhttp.path"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestHeader" }}'>
|
||||
<a-button
|
||||
icon="plus"
|
||||
size="small"
|
||||
@click="inbound.stream.xhttp.addHeader('', '')"
|
||||
></a-button>
|
||||
<a-button icon="plus" size="small" @click="inbound.stream.xhttp.addHeader('', '')"></a-button>
|
||||
</a-form-item>
|
||||
<a-form-item :wrapper-col="{span:24}">
|
||||
<a-input-group
|
||||
compact
|
||||
v-for="(header, index) in inbound.stream.xhttp.headers"
|
||||
>
|
||||
<a-input
|
||||
:style="{ width: '50%' }"
|
||||
v-model.trim="header.name"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.name"}}'
|
||||
>
|
||||
<template slot="addonBefore" :style="{ margin: '0' }"
|
||||
>[[ index+1 ]]</template
|
||||
>
|
||||
<a-input-group compact v-for="(header, index) in inbound.stream.xhttp.headers">
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.name"
|
||||
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.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'
|
||||
>
|
||||
<a-button
|
||||
icon="minus"
|
||||
slot="addonAfter"
|
||||
size="small"
|
||||
@click="inbound.stream.xhttp.removeHeader(index)"
|
||||
></a-button>
|
||||
<a-input :style="{ width: '50%' }" v-model.trim="header.value"
|
||||
placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
|
||||
<a-button icon="minus" slot="addonAfter" size="small"
|
||||
@click="inbound.stream.xhttp.removeHeader(index)"></a-button>
|
||||
</a-input>
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="Mode">
|
||||
<a-select
|
||||
v-model="inbound.stream.xhttp.mode"
|
||||
:style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select-option v-for="key in MODE_OPTION" :value="key"
|
||||
>[[ key ]]</a-select-option
|
||||
>
|
||||
<a-select v-model="inbound.stream.xhttp.mode" :style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in MODE_OPTION" :value="key">[[ key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Max Buffered Upload"
|
||||
v-if="inbound.stream.xhttp.mode === 'packet-up'"
|
||||
>
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.xhttp.scMaxBufferedPosts"
|
||||
></a-input-number>
|
||||
<a-form-item label="Max Buffered Upload" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||
<a-input-number v-model.number="inbound.stream.xhttp.scMaxBufferedPosts"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Max Upload Size (Byte)"
|
||||
v-if="inbound.stream.xhttp.mode === 'packet-up'"
|
||||
>
|
||||
<a-form-item label="Max Upload Size (Byte)" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.scMaxEachPostBytes"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Stream-Up Server"
|
||||
v-if="inbound.stream.xhttp.mode === 'stream-up'"
|
||||
>
|
||||
<a-form-item label="Stream-Up Server" v-if="inbound.stream.xhttp.mode === 'stream-up'">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.scStreamUpServerSecs"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Padding Bytes">
|
||||
@@ -84,22 +45,13 @@
|
||||
</a-form-item>
|
||||
<template v-if="inbound.stream.xhttp.xPaddingObfsMode">
|
||||
<a-form-item label="Padding Key">
|
||||
<a-input
|
||||
v-model.trim="inbound.stream.xhttp.xPaddingKey"
|
||||
placeholder="x_padding"
|
||||
></a-input>
|
||||
<a-input v-model.trim="inbound.stream.xhttp.xPaddingKey" placeholder="x_padding"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Padding Header">
|
||||
<a-input
|
||||
v-model.trim="inbound.stream.xhttp.xPaddingHeader"
|
||||
placeholder="X-Padding"
|
||||
></a-input>
|
||||
<a-input v-model.trim="inbound.stream.xhttp.xPaddingHeader" placeholder="X-Padding"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Padding Placement">
|
||||
<a-select
|
||||
v-model="inbound.stream.xhttp.xPaddingPlacement"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.xhttp.xPaddingPlacement" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>Default (queryInHeader)</a-select-option>
|
||||
<a-select-option value="queryInHeader">queryInHeader</a-select-option>
|
||||
<a-select-option value="header">header</a-select-option>
|
||||
@@ -108,10 +60,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Padding Method">
|
||||
<a-select
|
||||
v-model="inbound.stream.xhttp.xPaddingMethod"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.xhttp.xPaddingMethod" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>Default (repeat-x)</a-select-option>
|
||||
<a-select-option value="repeat-x">repeat-x</a-select-option>
|
||||
<a-select-option value="tokenish">tokenish</a-select-option>
|
||||
@@ -119,10 +68,7 @@
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label="Uplink HTTP Method">
|
||||
<a-select
|
||||
v-model="inbound.stream.xhttp.uplinkHTTPMethod"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.xhttp.uplinkHTTPMethod" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>Default (POST)</a-select-option>
|
||||
<a-select-option value="POST">POST</a-select-option>
|
||||
<a-select-option value="PUT">PUT</a-select-option>
|
||||
@@ -130,10 +76,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="Session Placement">
|
||||
<a-select
|
||||
v-model="inbound.stream.xhttp.sessionPlacement"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.xhttp.sessionPlacement" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>Default (path)</a-select-option>
|
||||
<a-select-option value="path">path</a-select-option>
|
||||
<a-select-option value="header">header</a-select-option>
|
||||
@@ -141,20 +84,12 @@
|
||||
<a-select-option value="query">query</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Session Key"
|
||||
v-if="inbound.stream.xhttp.sessionPlacement && inbound.stream.xhttp.sessionPlacement !== 'path'"
|
||||
>
|
||||
<a-input
|
||||
v-model.trim="inbound.stream.xhttp.sessionKey"
|
||||
placeholder="x_session"
|
||||
></a-input>
|
||||
<a-form-item label="Session Key"
|
||||
v-if="inbound.stream.xhttp.sessionPlacement && inbound.stream.xhttp.sessionPlacement !== 'path'">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.sessionKey" placeholder="x_session"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Sequence Placement">
|
||||
<a-select
|
||||
v-model="inbound.stream.xhttp.seqPlacement"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-select v-model="inbound.stream.xhttp.seqPlacement" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>Default (path)</a-select-option>
|
||||
<a-select-option value="path">path</a-select-option>
|
||||
<a-select-option value="header">header</a-select-option>
|
||||
@@ -162,23 +97,12 @@
|
||||
<a-select-option value="query">query</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Sequence Key"
|
||||
v-if="inbound.stream.xhttp.seqPlacement && inbound.stream.xhttp.seqPlacement !== 'path'"
|
||||
>
|
||||
<a-input
|
||||
v-model.trim="inbound.stream.xhttp.seqKey"
|
||||
placeholder="x_seq"
|
||||
></a-input>
|
||||
<a-form-item label="Sequence Key"
|
||||
v-if="inbound.stream.xhttp.seqPlacement && inbound.stream.xhttp.seqPlacement !== 'path'">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.seqKey" placeholder="x_seq"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Uplink Data Placement"
|
||||
v-if="inbound.stream.xhttp.mode === 'packet-up'"
|
||||
>
|
||||
<a-select
|
||||
v-model="inbound.stream.xhttp.uplinkDataPlacement"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
>
|
||||
<a-form-item label="Uplink Data Placement" v-if="inbound.stream.xhttp.mode === 'packet-up'">
|
||||
<a-select v-model="inbound.stream.xhttp.uplinkDataPlacement" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>Default (body)</a-select-option>
|
||||
<a-select-option value="body">body</a-select-option>
|
||||
<a-select-option value="header">header</a-select-option>
|
||||
@@ -186,24 +110,14 @@
|
||||
<a-select-option value="query">query</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Uplink Data Key"
|
||||
v-if="inbound.stream.xhttp.mode === 'packet-up' && inbound.stream.xhttp.uplinkDataPlacement && inbound.stream.xhttp.uplinkDataPlacement !== 'body'"
|
||||
>
|
||||
<a-input
|
||||
v-model.trim="inbound.stream.xhttp.uplinkDataKey"
|
||||
placeholder="x_data"
|
||||
></a-input>
|
||||
<a-form-item label="Uplink Data Key"
|
||||
v-if="inbound.stream.xhttp.mode === 'packet-up' && inbound.stream.xhttp.uplinkDataPlacement && inbound.stream.xhttp.uplinkDataPlacement !== 'body'">
|
||||
<a-input v-model.trim="inbound.stream.xhttp.uplinkDataKey" placeholder="x_data"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item
|
||||
label="Uplink Chunk Size"
|
||||
v-if="inbound.stream.xhttp.mode === 'packet-up' && inbound.stream.xhttp.uplinkDataPlacement && inbound.stream.xhttp.uplinkDataPlacement !== 'body'"
|
||||
>
|
||||
<a-input-number
|
||||
v-model.number="inbound.stream.xhttp.uplinkChunkSize"
|
||||
:min="0"
|
||||
placeholder="0 (unlimited)"
|
||||
></a-input-number>
|
||||
<a-form-item label="Uplink Chunk Size"
|
||||
v-if="inbound.stream.xhttp.mode === 'packet-up' && inbound.stream.xhttp.uplinkDataPlacement && inbound.stream.xhttp.uplinkDataPlacement !== 'body'">
|
||||
<a-input-number v-model.number="inbound.stream.xhttp.uplinkChunkSize" :min="0"
|
||||
placeholder="0 (unlimited)"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item label="No SSE Header">
|
||||
<a-switch v-model="inbound.stream.xhttp.noSSEHeader"></a-switch>
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
{{define "form/tlsSettings"}}
|
||||
<!-- tls enable -->
|
||||
<a-form v-if="inbound.canEnableTls()" :colon="false"
|
||||
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<a-form v-if="inbound.canEnableTls()" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<template v-if="inbound.protocol !== Protocols.HYSTERIA">
|
||||
<a-divider :style="{ margin: '3px 0' }"></a-divider>
|
||||
<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 v-if="inbound.canEnableReality()"
|
||||
value="reality">Reality</a-radio-button>
|
||||
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
|
||||
<a-radio-button value="tls">TLS</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
@@ -21,8 +19,7 @@
|
||||
<a-input v-model.trim="inbound.stream.tls.sni"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="Cipher Suites">
|
||||
<a-select v-model="inbound.stream.tls.cipherSuites"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select v-model="inbound.stream.tls.cipherSuites" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>Auto</a-select-option>
|
||||
<a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[
|
||||
value ]]</a-select-option>
|
||||
@@ -30,14 +27,12 @@
|
||||
</a-form-item>
|
||||
<a-form-item label="Min/Max Version">
|
||||
<a-input-group compact>
|
||||
<a-select v-model="inbound.stream.tls.minVersion"
|
||||
:style="{ width: '50%' }"
|
||||
<a-select v-model="inbound.stream.tls.minVersion" :style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key
|
||||
]]</a-select-option>
|
||||
</a-select>
|
||||
<a-select v-model="inbound.stream.tls.maxVersion"
|
||||
:style="{ width: '50%' }"
|
||||
<a-select v-model="inbound.stream.tls.maxVersion" :style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key
|
||||
]]</a-select-option>
|
||||
@@ -45,8 +40,7 @@
|
||||
</a-input-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="uTLS">
|
||||
<a-select v-model="inbound.stream.tls.settings.fingerprint"
|
||||
:style="{ width: '100%' }"
|
||||
<a-select v-model="inbound.stream.tls.settings.fingerprint" :style="{ width: '100%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value>None</a-select-option>
|
||||
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key
|
||||
@@ -54,9 +48,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label="ALPN">
|
||||
<a-select mode="multiple"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
v-model="inbound.stream.tls.alpn">
|
||||
<a-select mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme" v-model="inbound.stream.tls.alpn">
|
||||
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn
|
||||
]]</a-select-option>
|
||||
</a-select>
|
||||
@@ -75,8 +67,7 @@
|
||||
<a-form-item label='{{ i18n "certificate" }}'>
|
||||
<a-radio-group v-model="cert.useFile" button-style="solid"
|
||||
:style="{ display: 'inline-flex', whiteSpace: 'nowrap', maxWidth: '100%' }">
|
||||
<a-radio-button :value="true"
|
||||
:style="{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }">{{
|
||||
<a-radio-button :value="true" :style="{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }">{{
|
||||
i18n "pages.inbounds.certificatePath" }}</a-radio-button>
|
||||
<a-radio-button :value="false"
|
||||
:style="{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }">{{
|
||||
@@ -87,8 +78,7 @@
|
||||
<a-space>
|
||||
<a-button icon="plus" v-if="index === 0" type="primary" size="small"
|
||||
@click="inbound.stream.tls.addCert()"></a-button>
|
||||
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1"
|
||||
type="primary" size="small"
|
||||
<a-button icon="minus" v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
|
||||
@click="inbound.stream.tls.removeCert(index)"></a-button>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
@@ -100,8 +90,7 @@
|
||||
<a-input v-model.trim="cert.keyFile"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-button type="primary" icon="import"
|
||||
@click="setDefaultCertData(index)">
|
||||
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">
|
||||
{{ i18n "pages.inbounds.setDefaultCert" }}</a-button>
|
||||
</a-form-item>
|
||||
</template>
|
||||
@@ -117,8 +106,7 @@
|
||||
<a-switch v-model="cert.oneTimeLoading"></a-switch>
|
||||
</a-form-item>
|
||||
<a-form-item label='Usage Option'>
|
||||
<a-select v-model="cert.usage" :style="{ width: '50%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select v-model="cert.usage" :style="{ width: '50%' }" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in USAGE_OPTION" :value="key">[[ key
|
||||
]]</a-select-option>
|
||||
</a-select>
|
||||
@@ -133,13 +121,6 @@
|
||||
<a-form-item label='ECH config'>
|
||||
<a-input v-model="inbound.stream.tls.settings.echConfigList"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='ECH force query'>
|
||||
<a-select v-model="inbound.stream.tls.echForceQuery"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in ['none', 'half', 'full']" :value="key">[[
|
||||
key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label=" ">
|
||||
<a-space>
|
||||
<a-button type="primary" icon="import" @click="getNewEchCert">Get New
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
html[data-theme="ultra-dark"] body.dark .custom-geo-section code.custom-geo-ext-code {
|
||||
color: var(--dark-color-text-primary, rgba(255, 255, 255, 0.88));
|
||||
background: var(--dark-color-surface-700, #111929);
|
||||
@@ -119,7 +120,8 @@
|
||||
</a-row>
|
||||
</span>
|
||||
<template slot="content">
|
||||
<span class="max-w-400" v-for="line in (status.xray.errorMsg || '').split('\n')">[[ line ]]</span>
|
||||
<span class="max-w-400" v-for="line in (status.xray.errorMsg || '').split('\n')">[[ line
|
||||
]]</span>
|
||||
</template>
|
||||
<a-badge :text="status.xray.stateMsg" :color="status.xray.color"
|
||||
:class="status.xray.color === 'red' ? 'xray-error-animation' : ''" />
|
||||
@@ -170,7 +172,8 @@
|
||||
<a-col :sm="24" :lg="12">
|
||||
<a-card title='3X-UI' hoverable>
|
||||
<template v-if="panelUpdateModal.info.updateAvailable" #extra>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme" :title='`{{ i18n "pages.index.updatePanel" }}: ${panelUpdateModal.info.latestVersion}`'>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme"
|
||||
:title='`{{ i18n "pages.index.updatePanel" }}: ${panelUpdateModal.info.latestVersion}`'>
|
||||
<a-tag color="orange" style="cursor:pointer;margin:0" @click="openPanelUpdate">
|
||||
<a-icon type="cloud-download"></a-icon>[[ panelUpdateModal.info.latestVersion ]]
|
||||
<span v-if="!isMobile">{{ i18n "pages.index.updatePanel" }}</span>
|
||||
@@ -327,8 +330,7 @@
|
||||
</a-layout>
|
||||
<a-modal id="panel-update-modal" v-model="panelUpdateModal.visible" title='{{ i18n "pages.index.updatePanel" }}'
|
||||
:closable="true" @ok="() => panelUpdateModal.visible = false" :class="themeSwitcher.currentTheme" footer="">
|
||||
<a-alert type="warning" class="mb-12 w-100" message='{{ i18n "pages.index.panelUpdateDesc" }}'
|
||||
show-icon></a-alert>
|
||||
<a-alert type="warning" class="mb-12 w-100" message='{{ i18n "pages.index.panelUpdateDesc" }}' show-icon></a-alert>
|
||||
<a-list class="ant-version-list w-100" bordered>
|
||||
<a-list-item class="ant-version-list-item">
|
||||
<span>{{ i18n "pages.index.currentPanelVersion" }}</span>
|
||||
@@ -405,7 +407,8 @@
|
||||
</a-tooltip>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="title">{{ i18n "pages.index.customGeoDownload" }}</template>
|
||||
<a-button type="link" size="small" icon="reload" @click="downloadCustomGeo(record.id)" :loading="customGeoActionId === record.id"></a-button>
|
||||
<a-button type="link" size="small" icon="reload" @click="downloadCustomGeo(record.id)"
|
||||
:loading="customGeoActionId === record.id"></a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="title">{{ i18n "pages.index.customGeoDelete" }}</template>
|
||||
@@ -418,18 +421,22 @@
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-modal>
|
||||
<a-modal v-model="customGeoModal.visible" :title="customGeoModal.editId ? '{{ i18n "pages.index.customGeoModalEdit" }}' : '{{ i18n "pages.index.customGeoModalAdd" }}'"
|
||||
:confirm-loading="customGeoModal.saving" @ok="submitCustomGeo" :ok-text="'{{ i18n "pages.index.customGeoModalSave" }}'" :cancel-text="'{{ i18n "close" }}'"
|
||||
<a-modal v-model="customGeoModal.visible"
|
||||
:title="customGeoModal.editId ? '{{ i18n "pages.index.customGeoModalEdit" }}' : '{{ i18n "pages.index.customGeoModalAdd" }}'"
|
||||
:confirm-loading="customGeoModal.saving" @ok="submitCustomGeo"
|
||||
:ok-text="'{{ i18n "pages.index.customGeoModalSave" }}'" :cancel-text="'{{ i18n "close" }}'"
|
||||
:class="themeSwitcher.currentTheme">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label='{{ i18n "pages.index.customGeoType" }}'>
|
||||
<a-select v-model="customGeoModal.form.type" :disabled="!!customGeoModal.editId" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select v-model="customGeoModal.form.type" :disabled="!!customGeoModal.editId"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option value="geosite">geosite</a-select-option>
|
||||
<a-select-option value="geoip">geoip</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.index.customGeoAlias" }}'>
|
||||
<a-input v-model.trim="customGeoModal.form.alias" :disabled="!!customGeoModal.editId" placeholder='{{ i18n "pages.index.customGeoAliasPlaceholder" }}'></a-input>
|
||||
<a-input v-model.trim="customGeoModal.form.alias" :disabled="!!customGeoModal.editId"
|
||||
placeholder='{{ i18n "pages.index.customGeoAliasPlaceholder" }}'></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.index.customGeoUrl" }}'>
|
||||
<a-input v-model.trim="customGeoModal.form.url" placeholder="https://"></a-input>
|
||||
@@ -469,7 +476,8 @@
|
||||
<a-checkbox v-model="logModal.syslog" @change="openLogs()">SysLog</a-checkbox>
|
||||
</a-form-item>
|
||||
<a-form-item style="float: right;">
|
||||
<a-button type="primary" icon="download" @click="FileManager.downloadTextFile(logModal.logs?.join('\n'), 'x-ui.log')"></a-button>
|
||||
<a-button type="primary" icon="download"
|
||||
@click="FileManager.downloadTextFile(logModal.logs?.join('\n'), 'x-ui.log')"></a-button>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="ant-input log-container" v-html="logModal.formattedLogs"></div>
|
||||
@@ -547,7 +555,8 @@
|
||||
<sparkline :data="cpuHistoryLong" :labels="cpuHistoryLabels" :vb-width="840" :height="220"
|
||||
:stroke="status.cpu.color" :stroke-width="2.2" :show-grid="true" :show-axes="true" :tick-count-x="5"
|
||||
:max-points="cpuHistoryLong.length" :fill-opacity="0.18" :marker-radius="3.2" :show-tooltip="true" />
|
||||
<div style="margin-top:4px;font-size:11px;opacity:0.65">Timeframe: [[ cpuHistoryModal.bucket ]] sec per point (total [[ cpuHistoryLong.length ]] points)</div>
|
||||
<div style="margin-top:4px;font-size:11px;opacity:0.65">Timeframe: [[ cpuHistoryModal.bucket ]] sec per point
|
||||
(total [[ cpuHistoryLong.length ]] points)</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</a-layout>
|
||||
@@ -560,28 +569,88 @@
|
||||
// Tiny Sparkline component using an inline SVG polyline
|
||||
Vue.component('sparkline', {
|
||||
props: {
|
||||
data: { type: Array, required: true },
|
||||
data: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
// viewBox width for drawing space; SVG width will be 100% of container
|
||||
vbWidth: { type: Number, default: 320 },
|
||||
height: { type: Number, default: 80 },
|
||||
stroke: { type: String, default: '#008771' },
|
||||
strokeWidth: { type: Number, default: 2 },
|
||||
maxPoints: { type: Number, default: 120 },
|
||||
showGrid: { type: Boolean, default: true },
|
||||
gridColor: { type: String, default: 'rgba(0,0,0,0.1)' },
|
||||
fillOpacity: { type: Number, default: 0.15 },
|
||||
showMarker: { type: Boolean, default: true },
|
||||
markerRadius: { type: Number, default: 2.8 },
|
||||
vbWidth: {
|
||||
type: Number,
|
||||
default: 320
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 80
|
||||
},
|
||||
stroke: {
|
||||
type: String,
|
||||
default: '#008771'
|
||||
},
|
||||
strokeWidth: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
maxPoints: {
|
||||
type: Number,
|
||||
default: 120
|
||||
},
|
||||
showGrid: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
gridColor: {
|
||||
type: String,
|
||||
default: 'rgba(0,0,0,0.1)'
|
||||
},
|
||||
fillOpacity: {
|
||||
type: Number,
|
||||
default: 0.15
|
||||
},
|
||||
showMarker: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
markerRadius: {
|
||||
type: Number,
|
||||
default: 2.8
|
||||
},
|
||||
// New opts for axes/labels/tooltip
|
||||
labels: { type: Array, default: () => [] }, // same length as data for x labels (e.g., timestamps)
|
||||
showAxes: { type: Boolean, default: false },
|
||||
yTickStep: { type: Number, default: 25 }, // percent ticks
|
||||
tickCountX: { type: Number, default: 4 },
|
||||
paddingLeft: { type: Number, default: 32 },
|
||||
paddingRight: { type: Number, default: 6 },
|
||||
paddingTop: { type: Number, default: 6 },
|
||||
paddingBottom: { type: Number, default: 20 },
|
||||
showTooltip: { type: Boolean, default: false },
|
||||
labels: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}, // same length as data for x labels (e.g., timestamps)
|
||||
showAxes: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
yTickStep: {
|
||||
type: Number,
|
||||
default: 25
|
||||
}, // percent ticks
|
||||
tickCountX: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
paddingLeft: {
|
||||
type: Number,
|
||||
default: 32
|
||||
},
|
||||
paddingRight: {
|
||||
type: Number,
|
||||
default: 6
|
||||
},
|
||||
paddingTop: {
|
||||
type: Number,
|
||||
default: 6
|
||||
},
|
||||
paddingBottom: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
showTooltip: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -644,7 +713,12 @@
|
||||
// draw at 25%, 50%, 75%
|
||||
return [0, 0.25, 0.5, 0.75, 1]
|
||||
.map(r => Math.round(this.paddingTop + h * r))
|
||||
.map(y => ({ x1: this.paddingLeft, y1: y, x2: this.paddingLeft + w, y2: y }))
|
||||
.map(y => ({
|
||||
x1: this.paddingLeft,
|
||||
y1: y,
|
||||
x2: this.paddingLeft + w,
|
||||
y2: y
|
||||
}))
|
||||
},
|
||||
lastPoint() {
|
||||
if (this.pointsArr.length === 0) return null
|
||||
@@ -656,7 +730,10 @@
|
||||
const ticks = []
|
||||
for (let p = 0; p <= 100; p += step) {
|
||||
const y = Math.round(this.paddingTop + (this.drawHeight - (p / 100) * this.drawHeight))
|
||||
ticks.push({ y, label: `${p}%` })
|
||||
ticks.push({
|
||||
y,
|
||||
label: `${p}%`
|
||||
})
|
||||
}
|
||||
return ticks
|
||||
},
|
||||
@@ -677,7 +754,10 @@
|
||||
positions.forEach(idx => {
|
||||
const label = labels[idx] != null ? String(labels[idx]) : String(idx)
|
||||
const x = Math.round(this.paddingLeft + idx * dx)
|
||||
ticks.push({ x, label })
|
||||
ticks.push({
|
||||
x,
|
||||
label
|
||||
})
|
||||
})
|
||||
return ticks
|
||||
},
|
||||
@@ -778,17 +858,36 @@
|
||||
this.disk = new CurTotal(0, 0);
|
||||
this.loads = [0, 0, 0];
|
||||
this.mem = new CurTotal(0, 0);
|
||||
this.netIO = { up: 0, down: 0 };
|
||||
this.netTraffic = { sent: 0, recv: 0 };
|
||||
this.publicIP = { ipv4: 0, ipv6: 0 };
|
||||
this.netIO = {
|
||||
up: 0,
|
||||
down: 0
|
||||
};
|
||||
this.netTraffic = {
|
||||
sent: 0,
|
||||
recv: 0
|
||||
};
|
||||
this.publicIP = {
|
||||
ipv4: 0,
|
||||
ipv6: 0
|
||||
};
|
||||
this.swap = new CurTotal(0, 0);
|
||||
this.tcpCount = 0;
|
||||
this.udpCount = 0;
|
||||
this.uptime = 0;
|
||||
this.appUptime = 0;
|
||||
this.appStats = { threads: 0, mem: 0, uptime: 0 };
|
||||
this.appStats = {
|
||||
threads: 0,
|
||||
mem: 0,
|
||||
uptime: 0
|
||||
};
|
||||
|
||||
this.xray = { state: 'stop', stateMsg: "", errorMsg: "", version: "", color: "" };
|
||||
this.xray = {
|
||||
state: 'stop',
|
||||
stateMsg: "",
|
||||
errorMsg: "",
|
||||
version: "",
|
||||
color: ""
|
||||
};
|
||||
|
||||
if (data == null) {
|
||||
return;
|
||||
@@ -958,8 +1057,7 @@
|
||||
let outboundColor = '';
|
||||
if (log.Event === 1) {
|
||||
outboundColor = ' style="color: #e04141;"'; //red for blocked
|
||||
}
|
||||
else if (log.Event === 2) {
|
||||
} else if (log.Event === 2) {
|
||||
outboundColor = ' style="color: #3c89e8;"'; //blue for proxies
|
||||
}
|
||||
|
||||
@@ -996,10 +1094,31 @@
|
||||
},
|
||||
};
|
||||
|
||||
const customGeoColumns = [
|
||||
{ title: '{{ i18n "pages.index.customGeoExtColumn" }}', key: 'extDat', scopedSlots: { customRender: 'extDat' }, ellipsis: true },
|
||||
{ title: '{{ i18n "pages.index.customGeoLastUpdated" }}', key: 'lastUpdatedAt', scopedSlots: { customRender: 'lastUpdatedAt' }, width: 160 },
|
||||
{ title: '{{ i18n "pages.index.customGeoActions" }}', key: 'action', scopedSlots: { customRender: 'action' }, width: 120, fixed: 'right' },
|
||||
const customGeoColumns = [{
|
||||
title: '{{ i18n "pages.index.customGeoExtColumn" }}',
|
||||
key: 'extDat',
|
||||
scopedSlots: {
|
||||
customRender: 'extDat'
|
||||
},
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: '{{ i18n "pages.index.customGeoLastUpdated" }}',
|
||||
key: 'lastUpdatedAt',
|
||||
scopedSlots: {
|
||||
customRender: 'lastUpdatedAt'
|
||||
},
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
title: '{{ i18n "pages.index.customGeoActions" }}',
|
||||
key: 'action',
|
||||
scopedSlots: {
|
||||
customRender: 'action'
|
||||
},
|
||||
width: 120,
|
||||
fixed: 'right'
|
||||
},
|
||||
];
|
||||
|
||||
const app = new Vue({
|
||||
@@ -1016,7 +1135,10 @@
|
||||
cpuHistory: [], // small live widget history
|
||||
cpuHistoryLong: [], // aggregated points from backend
|
||||
cpuHistoryLabels: [],
|
||||
cpuHistoryModal: { visible: false, bucket: 2 },
|
||||
cpuHistoryModal: {
|
||||
visible: false,
|
||||
bucket: 2
|
||||
},
|
||||
versionModal,
|
||||
panelUpdateModal,
|
||||
logModal,
|
||||
@@ -1129,9 +1251,9 @@
|
||||
return typeof moment !== 'undefined' ? moment(ts * 1000).format('YYYY-MM-DD HH:mm') : String(ts);
|
||||
},
|
||||
customGeoExtDisplay(record) {
|
||||
const fn = record.type === 'geoip'
|
||||
? `geoip_${record.alias}.dat`
|
||||
: `geosite_${record.alias}.dat`;
|
||||
const fn = record.type === 'geoip' ?
|
||||
`geoip_${record.alias}.dat` :
|
||||
`geosite_${record.alias}.dat`;
|
||||
return `ext:${fn}:tag`;
|
||||
},
|
||||
async loadCustomGeo() {
|
||||
@@ -1285,18 +1407,18 @@
|
||||
const isSingleFile = !!fileName;
|
||||
this.$confirm({
|
||||
title: '{{ i18n "pages.index.geofileUpdateDialog" }}',
|
||||
content: isSingleFile
|
||||
? '{{ i18n "pages.index.geofileUpdateDialogDesc" }}'.replace("#filename#", fileName)
|
||||
: '{{ i18n "pages.index.geofilesUpdateDialogDesc" }}',
|
||||
content: isSingleFile ?
|
||||
'{{ i18n "pages.index.geofileUpdateDialogDesc" }}'.replace("#filename#", fileName) :
|
||||
'{{ i18n "pages.index.geofilesUpdateDialogDesc" }}',
|
||||
okText: '{{ i18n "confirm"}}',
|
||||
class: themeSwitcher.currentTheme,
|
||||
cancelText: '{{ i18n "cancel"}}',
|
||||
onOk: async () => {
|
||||
versionModal.hide();
|
||||
this.loading(true, '{{ i18n "pages.index.dontRefresh"}}');
|
||||
const url = isSingleFile
|
||||
? `/panel/api/server/updateGeofile/${fileName}`
|
||||
: `/panel/api/server/updateGeofile`;
|
||||
const url = isSingleFile ?
|
||||
`/panel/api/server/updateGeofile/${fileName}` :
|
||||
`/panel/api/server/updateGeofile`;
|
||||
await HttpUtil.post(url);
|
||||
this.loading(false);
|
||||
},
|
||||
@@ -1320,7 +1442,10 @@
|
||||
},
|
||||
async openLogs() {
|
||||
logModal.loading = true;
|
||||
const msg = await HttpUtil.post('/panel/api/server/logs/' + logModal.rows, { level: logModal.level, syslog: logModal.syslog });
|
||||
const msg = await HttpUtil.post('/panel/api/server/logs/' + logModal.rows, {
|
||||
level: logModal.level,
|
||||
syslog: logModal.syslog
|
||||
});
|
||||
if (!msg.success) {
|
||||
return;
|
||||
}
|
||||
@@ -1330,7 +1455,12 @@
|
||||
},
|
||||
async openXrayLogs() {
|
||||
xraylogModal.loading = true;
|
||||
const msg = await HttpUtil.post('/panel/api/server/xraylogs/' + xraylogModal.rows, { filter: xraylogModal.filter, showDirect: xraylogModal.showDirect, showBlocked: xraylogModal.showBlocked, showProxy: xraylogModal.showProxy });
|
||||
const msg = await HttpUtil.post('/panel/api/server/xraylogs/' + xraylogModal.rows, {
|
||||
filter: xraylogModal.filter,
|
||||
showDirect: xraylogModal.showDirect,
|
||||
showBlocked: xraylogModal.showBlocked,
|
||||
showProxy: xraylogModal.showProxy
|
||||
});
|
||||
if (!msg.success) {
|
||||
return;
|
||||
}
|
||||
@@ -1347,10 +1477,15 @@
|
||||
try {
|
||||
const dt = l.DateTime ? new Date(l.DateTime) : null;
|
||||
const dateStr = dt && !isNaN(dt.getTime()) ? dt.toISOString() : '';
|
||||
const eventMap = { 0: 'DIRECT', 1: 'BLOCKED', 2: 'PROXY' };
|
||||
const eventMap = {
|
||||
0: 'DIRECT',
|
||||
1: 'BLOCKED',
|
||||
2: 'PROXY'
|
||||
};
|
||||
const eventText = eventMap[l.Event] || String(l.Event ?? '');
|
||||
const emailPart = l.Email ? ` Email=${l.Email}` : '';
|
||||
return `${dateStr} FROM=${l.FromAddress || ''} TO=${l.ToAddress || ''} INBOUND=${l.Inbound || ''} OUTBOUND=${l.Outbound || ''}${emailPart} EVENT=${eventText}`.trim();
|
||||
return `${dateStr} FROM=${l.FromAddress || ''} TO=${l.ToAddress || ''} INBOUND=${l.Inbound || ''} OUTBOUND=${l.Outbound || ''}${emailPart} EVENT=${eventText}`
|
||||
.trim();
|
||||
} catch (e) {
|
||||
return JSON.stringify(l);
|
||||
}
|
||||
|
||||
@@ -108,8 +108,15 @@
|
||||
el: '#app',
|
||||
data: {
|
||||
themeSwitcher,
|
||||
loadingStates: { fetched: false, spinning: false },
|
||||
user: { username: "", password: "", twoFactorCode: "" },
|
||||
loadingStates: {
|
||||
fetched: false,
|
||||
spinning: false
|
||||
},
|
||||
user: {
|
||||
username: "",
|
||||
password: "",
|
||||
twoFactorCode: ""
|
||||
},
|
||||
twoFactorEnable: false,
|
||||
lang: "",
|
||||
animationStarted: false
|
||||
@@ -233,11 +240,16 @@
|
||||
}
|
||||
}
|
||||
});
|
||||
pm_wait_for_forms.observe(pm_host, { childList: true, subtree: true });
|
||||
pm_wait_for_forms.observe(pm_host, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', pm_init, { once: true });
|
||||
document.addEventListener('DOMContentLoaded', pm_init, {
|
||||
once: true
|
||||
});
|
||||
} else {
|
||||
pm_init();
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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}}
|
||||
@@ -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;
|
||||
@@ -58,20 +62,31 @@
|
||||
},
|
||||
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() {
|
||||
@@ -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}}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 = [
|
||||
{
|
||||
inModal.inbound.stream.externalProxy = [{
|
||||
forceTls: "same",
|
||||
dest: window.location.hostname,
|
||||
port: inModal.inbound.port,
|
||||
remark: "",
|
||||
},
|
||||
];
|
||||
}, ];
|
||||
} else {
|
||||
inModal.inbound.stream.externalProxy = [];
|
||||
}
|
||||
|
||||
@@ -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 || [];
|
||||
@@ -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;
|
||||
@@ -291,7 +311,8 @@
|
||||
computed: {
|
||||
nordOutboundIndex: {
|
||||
get: function() {
|
||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag.startsWith("nord-")) : -1;
|
||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag
|
||||
.startsWith("nord-")) : -1;
|
||||
}
|
||||
},
|
||||
filteredServers: function() {
|
||||
|
||||
@@ -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"
|
||||
<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"
|
||||
<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: '',
|
||||
@@ -66,6 +62,5 @@
|
||||
promptModal: promptModal,
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -63,16 +63,19 @@
|
||||
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);
|
||||
}
|
||||
@@ -86,10 +89,12 @@
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
|
||||
@@ -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;
|
||||
@@ -236,11 +238,11 @@
|
||||
computed: {
|
||||
warpOutboundIndex: {
|
||||
get: function() {
|
||||
return app.templateSettings ? app.templateSettings.outbounds.findIndex((o) => o.tag == 'warp') : -1;
|
||||
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>
|
||||
@@ -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();
|
||||
@@ -115,9 +116,7 @@
|
||||
data: {
|
||||
balancerModal: balancerModal
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {}
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -75,9 +75,13 @@
|
||||
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 = '',
|
||||
@@ -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}}
|
||||
@@ -6,7 +6,6 @@
|
||||
{{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;
|
||||
@@ -100,7 +106,8 @@
|
||||
},
|
||||
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;
|
||||
}
|
||||
},
|
||||
@@ -122,6 +129,5 @@
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
||||
@@ -28,13 +28,11 @@
|
||||
</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: [],
|
||||
@@ -73,7 +76,14 @@
|
||||
}
|
||||
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;
|
||||
@@ -86,9 +96,16 @@
|
||||
};
|
||||
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,16 +122,23 @@
|
||||
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;
|
||||
@@ -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}}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,8 @@
|
||||
</template>
|
||||
{{ template "settings/panel/subscription/general" . }}
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="5" v-if="allSetting.subJsonEnable || allSetting.subClashEnable" :style="{ paddingTop: '20px' }">
|
||||
<a-tab-pane key="5" v-if="allSetting.subJsonEnable || allSetting.subClashEnable"
|
||||
:style="{ paddingTop: '20px' }">
|
||||
<template #tab>
|
||||
<a-icon type="code"></a-icon>
|
||||
<span>{{ i18n "pages.settings.subSettings" }} (Formats)</span>
|
||||
@@ -124,9 +125,19 @@
|
||||
user: {},
|
||||
lang: LanguageManager.getLanguage(),
|
||||
inboundOptions: [],
|
||||
remarkModels: { i: 'Inbound', e: 'Email', o: 'Other' },
|
||||
remarkModels: {
|
||||
i: 'Inbound',
|
||||
e: 'Email',
|
||||
o: 'Other'
|
||||
},
|
||||
remarkSeparators: [' ', '-', '_', '@', ':', '~', '|', ',', '.', '/'],
|
||||
datepickerList: [{ name: 'Gregorian (Standard)', value: 'gregorian' }, { name: 'Jalalian (شمسی)', value: 'jalalian' }],
|
||||
datepickerList: [{
|
||||
name: 'Gregorian (Standard)',
|
||||
value: 'gregorian'
|
||||
}, {
|
||||
name: 'Jalalian (شمسی)',
|
||||
value: 'jalalian'
|
||||
}],
|
||||
remarkSample: '',
|
||||
defaultFragment: {
|
||||
packets: "tlshello",
|
||||
@@ -134,17 +145,19 @@
|
||||
interval: "10-20",
|
||||
maxSplit: "300-400"
|
||||
},
|
||||
defaultNoises: [
|
||||
{ type: "rand", packet: "10-20", delay: "10-16", applyTo: "ip" }
|
||||
],
|
||||
defaultNoises: [{
|
||||
type: "rand",
|
||||
packet: "10-20",
|
||||
delay: "10-16",
|
||||
applyTo: "ip"
|
||||
}],
|
||||
defaultMux: {
|
||||
enabled: true,
|
||||
concurrency: 8,
|
||||
xudpConcurrency: 16,
|
||||
xudpProxyUDP443: "reject"
|
||||
},
|
||||
defaultRules: [
|
||||
{
|
||||
defaultRules: [{
|
||||
type: "field",
|
||||
outboundTag: "direct",
|
||||
domain: [
|
||||
@@ -160,26 +173,75 @@
|
||||
]
|
||||
},
|
||||
],
|
||||
directIPsOptions: [
|
||||
{ label: 'Private IP', value: 'geoip:private' },
|
||||
{ label: '🇮🇷 Iran', value: 'geoip:ir' },
|
||||
{ label: '🇨🇳 China', value: 'geoip:cn' },
|
||||
{ label: '🇷🇺 Russia', value: 'geoip:ru' },
|
||||
{ label: '🇻🇳 Vietnam', value: 'geoip:vn' },
|
||||
{ label: '🇪🇸 Spain', value: 'geoip:es' },
|
||||
{ label: '🇮🇩 Indonesia', value: 'geoip:id' },
|
||||
{ label: '🇺🇦 Ukraine', value: 'geoip:ua' },
|
||||
{ label: '🇹🇷 Türkiye', value: 'geoip:tr' },
|
||||
{ label: '🇧🇷 Brazil', value: 'geoip:br' },
|
||||
directIPsOptions: [{
|
||||
label: 'Private IP',
|
||||
value: 'geoip:private'
|
||||
},
|
||||
{
|
||||
label: '🇮🇷 Iran',
|
||||
value: 'geoip:ir'
|
||||
},
|
||||
{
|
||||
label: '🇨🇳 China',
|
||||
value: 'geoip:cn'
|
||||
},
|
||||
{
|
||||
label: '🇷🇺 Russia',
|
||||
value: 'geoip:ru'
|
||||
},
|
||||
{
|
||||
label: '🇻🇳 Vietnam',
|
||||
value: 'geoip:vn'
|
||||
},
|
||||
{
|
||||
label: '🇪🇸 Spain',
|
||||
value: 'geoip:es'
|
||||
},
|
||||
{
|
||||
label: '🇮🇩 Indonesia',
|
||||
value: 'geoip:id'
|
||||
},
|
||||
{
|
||||
label: '🇺🇦 Ukraine',
|
||||
value: 'geoip:ua'
|
||||
},
|
||||
{
|
||||
label: '🇹🇷 Türkiye',
|
||||
value: 'geoip:tr'
|
||||
},
|
||||
{
|
||||
label: '🇧🇷 Brazil',
|
||||
value: 'geoip:br'
|
||||
},
|
||||
],
|
||||
diretDomainsOptions: [
|
||||
{ label: 'Private DNS', value: 'geosite:private' },
|
||||
{ label: '🇮🇷 Iran', value: 'geosite:category-ir' },
|
||||
{ label: '🇨🇳 China', value: 'geosite:cn' },
|
||||
{ label: '🇷🇺 Russia', value: 'geosite:category-ru' },
|
||||
{ label: 'Apple', value: 'geosite:apple' },
|
||||
{ label: 'Meta', value: 'geosite:meta' },
|
||||
{ label: 'Google', value: 'geosite:google' },
|
||||
diretDomainsOptions: [{
|
||||
label: 'Private DNS',
|
||||
value: 'geosite:private'
|
||||
},
|
||||
{
|
||||
label: '🇮🇷 Iran',
|
||||
value: 'geosite:category-ir'
|
||||
},
|
||||
{
|
||||
label: '🇨🇳 China',
|
||||
value: 'geosite:cn'
|
||||
},
|
||||
{
|
||||
label: '🇷🇺 Russia',
|
||||
value: 'geosite:category-ru'
|
||||
},
|
||||
{
|
||||
label: 'Apple',
|
||||
value: 'geosite:apple'
|
||||
},
|
||||
{
|
||||
label: 'Meta',
|
||||
value: 'geosite:meta'
|
||||
},
|
||||
{
|
||||
label: 'Google',
|
||||
value: 'geosite:google'
|
||||
},
|
||||
],
|
||||
get remarkModel() {
|
||||
rm = this.allSetting.remarkModel;
|
||||
@@ -317,7 +379,13 @@
|
||||
this.loading(true);
|
||||
await PromiseUtil.sleep(5000);
|
||||
|
||||
const { webDomain, webPort, webBasePath, webCertFile, webKeyFile } = this.allSetting;
|
||||
const {
|
||||
webDomain,
|
||||
webPort,
|
||||
webBasePath,
|
||||
webCertFile,
|
||||
webKeyFile
|
||||
} = this.allSetting;
|
||||
const newProtocol = (webCertFile || webKeyFile) ? "https:" : "http:";
|
||||
|
||||
let base = webBasePath ? webBasePath.replace(/^\//, "") : "";
|
||||
@@ -358,7 +426,8 @@
|
||||
type: 'set',
|
||||
confirm: (success) => {
|
||||
if (success) {
|
||||
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalSetSuccess" }}')
|
||||
Vue.prototype.$message['success'](
|
||||
'{{ i18n "pages.settings.security.twoFactorModalSetSuccess" }}')
|
||||
|
||||
this.allSetting.twoFactorToken = newTwoFactorToken
|
||||
}
|
||||
@@ -374,7 +443,8 @@
|
||||
type: 'confirm',
|
||||
confirm: (success) => {
|
||||
if (success) {
|
||||
Vue.prototype.$message['success']('{{ i18n "pages.settings.security.twoFactorModalDeleteSuccess" }}')
|
||||
Vue.prototype.$message['success'](
|
||||
'{{ i18n "pages.settings.security.twoFactorModalDeleteSuccess" }}')
|
||||
|
||||
this.allSetting.twoFactorEnable = false
|
||||
this.allSetting.twoFactorToken = ""
|
||||
@@ -384,7 +454,12 @@
|
||||
}
|
||||
},
|
||||
addNoise() {
|
||||
const newNoise = { type: "rand", packet: "10-20", delay: "10-16", applyTo: "ip" };
|
||||
const newNoise = {
|
||||
type: "rand",
|
||||
packet: "10-20",
|
||||
delay: "10-16",
|
||||
applyTo: "ip"
|
||||
};
|
||||
this.noisesArray = [...this.noisesArray, newNoise];
|
||||
},
|
||||
removeNoise(index) {
|
||||
@@ -394,22 +469,34 @@
|
||||
},
|
||||
updateNoiseType(index, value) {
|
||||
const updatedNoises = [...this.noisesArray];
|
||||
updatedNoises[index] = { ...updatedNoises[index], type: value };
|
||||
updatedNoises[index] = {
|
||||
...updatedNoises[index],
|
||||
type: value
|
||||
};
|
||||
this.noisesArray = updatedNoises;
|
||||
},
|
||||
updateNoisePacket(index, value) {
|
||||
const updatedNoises = [...this.noisesArray];
|
||||
updatedNoises[index] = { ...updatedNoises[index], packet: value };
|
||||
updatedNoises[index] = {
|
||||
...updatedNoises[index],
|
||||
packet: value
|
||||
};
|
||||
this.noisesArray = updatedNoises;
|
||||
},
|
||||
updateNoiseDelay(index, value) {
|
||||
const updatedNoises = [...this.noisesArray];
|
||||
updatedNoises[index] = { ...updatedNoises[index], delay: value };
|
||||
updatedNoises[index] = {
|
||||
...updatedNoises[index],
|
||||
delay: value
|
||||
};
|
||||
this.noisesArray = updatedNoises;
|
||||
},
|
||||
updateNoiseApplyTo(index, value) {
|
||||
const updatedNoises = [...this.noisesArray];
|
||||
updatedNoises[index] = { ...updatedNoises[index], applyTo: value };
|
||||
updatedNoises[index] = {
|
||||
...updatedNoises[index],
|
||||
applyTo: value
|
||||
};
|
||||
this.noisesArray = updatedNoises;
|
||||
},
|
||||
},
|
||||
@@ -424,13 +511,17 @@
|
||||
}
|
||||
},
|
||||
fragment: {
|
||||
get: function () { return this.allSetting?.subJsonFragment != ""; },
|
||||
get: function() {
|
||||
return this.allSetting?.subJsonFragment != "";
|
||||
},
|
||||
set: function(v) {
|
||||
this.allSetting.subJsonFragment = v ? JSON.stringify(this.defaultFragment) : "";
|
||||
}
|
||||
},
|
||||
fragmentPackets: {
|
||||
get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).packets : ""; },
|
||||
get: function() {
|
||||
return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).packets : "";
|
||||
},
|
||||
set: function(v) {
|
||||
if (v != "") {
|
||||
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||
@@ -440,7 +531,9 @@
|
||||
}
|
||||
},
|
||||
fragmentLength: {
|
||||
get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).length : ""; },
|
||||
get: function() {
|
||||
return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).length : "";
|
||||
},
|
||||
set: function(v) {
|
||||
if (v != "") {
|
||||
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||
@@ -450,7 +543,9 @@
|
||||
}
|
||||
},
|
||||
fragmentInterval: {
|
||||
get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).interval : ""; },
|
||||
get: function() {
|
||||
return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).interval : "";
|
||||
},
|
||||
set: function(v) {
|
||||
if (v != "") {
|
||||
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||
@@ -460,7 +555,9 @@
|
||||
}
|
||||
},
|
||||
fragmentMaxSplit: {
|
||||
get: function () { return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).maxSplit : ""; },
|
||||
get: function() {
|
||||
return this.fragment ? JSON.parse(this.allSetting.subJsonFragment).maxSplit : "";
|
||||
},
|
||||
set: function(v) {
|
||||
if (v != "") {
|
||||
newFragment = JSON.parse(this.allSetting.subJsonFragment);
|
||||
@@ -492,13 +589,17 @@
|
||||
}
|
||||
},
|
||||
enableMux: {
|
||||
get: function () { return this.allSetting?.subJsonMux != ""; },
|
||||
get: function() {
|
||||
return this.allSetting?.subJsonMux != "";
|
||||
},
|
||||
set: function(v) {
|
||||
this.allSetting.subJsonMux = v ? JSON.stringify(this.defaultMux) : "";
|
||||
}
|
||||
},
|
||||
muxConcurrency: {
|
||||
get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).concurrency : -1; },
|
||||
get: function() {
|
||||
return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).concurrency : -1;
|
||||
},
|
||||
set: function(v) {
|
||||
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||
newMux.concurrency = v;
|
||||
@@ -506,7 +607,9 @@
|
||||
}
|
||||
},
|
||||
muxXudpConcurrency: {
|
||||
get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpConcurrency : -1; },
|
||||
get: function() {
|
||||
return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpConcurrency : -1;
|
||||
},
|
||||
set: function(v) {
|
||||
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||
newMux.xudpConcurrency = v;
|
||||
@@ -514,7 +617,9 @@
|
||||
}
|
||||
},
|
||||
muxXudpProxyUDP443: {
|
||||
get: function () { return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpProxyUDP443 : "reject"; },
|
||||
get: function() {
|
||||
return this.enableMux ? JSON.parse(this.allSetting.subJsonMux).xudpProxyUDP443 : "reject";
|
||||
},
|
||||
set: function(v) {
|
||||
newMux = JSON.parse(this.allSetting.subJsonMux);
|
||||
newMux.xudpProxyUDP443 = v;
|
||||
@@ -522,7 +627,9 @@
|
||||
}
|
||||
},
|
||||
enableDirect: {
|
||||
get: function () { return this.allSetting?.subJsonRules != ""; },
|
||||
get: function() {
|
||||
return this.allSetting?.subJsonRules != "";
|
||||
},
|
||||
set: function(v) {
|
||||
this.allSetting.subJsonRules = v ? JSON.stringify(this.defaultRules) : "";
|
||||
}
|
||||
@@ -584,11 +691,13 @@
|
||||
panelPath = window.location.pathname.split('/').length < 4
|
||||
if (panelPath && this.allSetting.webBasePath == '/') alerts.push('{{ i18n "secAlertPanelURI" }}');
|
||||
if (this.allSetting.subEnable) {
|
||||
subPath = this.allSetting.subURI.length > 0 ? new URL(this.allSetting.subURI).pathname : this.allSetting.subPath;
|
||||
subPath = this.allSetting.subURI.length > 0 ? new URL(this.allSetting.subURI).pathname : this
|
||||
.allSetting.subPath;
|
||||
if (subPath == '/sub/') alerts.push('{{ i18n "secAlertSubURI" }}');
|
||||
}
|
||||
if (this.allSetting.subJsonEnable) {
|
||||
subJsonPath = this.allSetting.subJsonURI.length > 0 ? new URL(this.allSetting.subJsonURI).pathname : this.allSetting.subJsonPath;
|
||||
subJsonPath = this.allSetting.subJsonURI.length > 0 ? new URL(this.allSetting.subJsonURI).pathname :
|
||||
this.allSetting.subJsonPath;
|
||||
if (subJsonPath == '/json/') alerts.push('{{ i18n "secAlertSubJsonURI" }}');
|
||||
}
|
||||
return alerts
|
||||
|
||||
@@ -162,7 +162,8 @@
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>LDAP Port</template>
|
||||
<template #control>
|
||||
<a-input-number :min="1" :max="65535" v-model="allSetting.ldapPort" :style="{ width: '100%' }"></a-input-number>
|
||||
<a-input-number :min="1" :max="65535" v-model="allSetting.ldapPort"
|
||||
:style="{ width: '100%' }"></a-input-number>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-setting-list-item paddings="small">
|
||||
@@ -239,10 +240,13 @@
|
||||
<template #title>Inbound tags</template>
|
||||
<template #description>Select inbounds to manage (auto create/delete)</template>
|
||||
<template #control>
|
||||
<a-select mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }" v-model="ldapInboundTagList">
|
||||
<a-select-option v-for="opt in inboundOptions" :key="opt.value" :value="opt.value">[[ opt.label ]]</a-select-option>
|
||||
<a-select mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme" :style="{ width: '100%' }"
|
||||
v-model="ldapInboundTagList">
|
||||
<a-select-option v-for="opt in inboundOptions" :key="opt.value" :value="opt.value">[[ opt.label
|
||||
]]</a-select-option>
|
||||
</a-select>
|
||||
<div v-if="inboundOptions.length==0" style="margin-top:6px;color:#999">No inbounds found. Please create one in Inbounds.</div>
|
||||
<div v-if="inboundOptions.length==0" style="margin-top:6px;color:#999">No inbounds found. Please create
|
||||
one in Inbounds.</div>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-setting-list-item paddings="small">
|
||||
@@ -260,19 +264,22 @@
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>Default total (GB)</template>
|
||||
<template #control>
|
||||
<a-input-number :min="0" v-model="allSetting.ldapDefaultTotalGB" :style="{ width: '100%' }"></a-input-number>
|
||||
<a-input-number :min="0" v-model="allSetting.ldapDefaultTotalGB"
|
||||
:style="{ width: '100%' }"></a-input-number>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>Default expiry (days)</template>
|
||||
<template #control>
|
||||
<a-input-number :min="0" v-model="allSetting.ldapDefaultExpiryDays" :style="{ width: '100%' }"></a-input-number>
|
||||
<a-input-number :min="0" v-model="allSetting.ldapDefaultExpiryDays"
|
||||
:style="{ width: '100%' }"></a-input-number>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>Default Limit IP</template>
|
||||
<template #control>
|
||||
<a-input-number :min="0" v-model="allSetting.ldapDefaultLimitIP" :style="{ width: '100%' }"></a-input-number>
|
||||
<a-input-number :min="0" v-model="allSetting.ldapDefaultLimitIP"
|
||||
:style="{ width: '100%' }"></a-input-number>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
</a-collapse-panel>
|
||||
|
||||
@@ -46,8 +46,7 @@
|
||||
<template #description>{{ i18n
|
||||
"pages.settings.subPortDesc"}}</template>
|
||||
<template #control>
|
||||
<a-input-number v-model="allSetting.subPort" :min="1"
|
||||
:min="65535"
|
||||
<a-input-number v-model="allSetting.subPort" :min="1" :min="65535"
|
||||
:style="{ width: '100%' }"></a-input-number>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
@@ -67,8 +66,7 @@
|
||||
<template #description>{{ i18n
|
||||
"pages.settings.subURIDesc"}}</template>
|
||||
<template #control>
|
||||
<a-input type="text"
|
||||
placeholder="(http|https)://domain[:port]/path/"
|
||||
<a-input type="text" placeholder="(http|https)://domain[:port]/path/"
|
||||
v-model="allSetting.subURI"></a-input>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
@@ -104,8 +102,7 @@
|
||||
<template #description>{{ i18n
|
||||
"pages.settings.subSupportUrlDesc"}}</template>
|
||||
<template #control>
|
||||
<a-input type="text" v-model="allSetting.subSupportUrl"
|
||||
placeholder="https://example.com"></a-input>
|
||||
<a-input type="text" v-model="allSetting.subSupportUrl" placeholder="https://example.com"></a-input>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-setting-list-item paddings="small">
|
||||
@@ -113,8 +110,7 @@
|
||||
<template #description>{{ i18n
|
||||
"pages.settings.subProfileUrlDesc"}}</template>
|
||||
<template #control>
|
||||
<a-input type="text" v-model="allSetting.subProfileUrl"
|
||||
placeholder="https://example.com"></a-input>
|
||||
<a-input type="text" v-model="allSetting.subProfileUrl" placeholder="https://example.com"></a-input>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-setting-list-item paddings="small">
|
||||
@@ -141,8 +137,7 @@
|
||||
<template #description>{{ i18n
|
||||
"pages.settings.subRoutingRulesDesc"}}</template>
|
||||
<template #control>
|
||||
<a-textarea v-model="allSetting.subRoutingRules"
|
||||
placeholder="happ://routing/add/..."></a-textarea>
|
||||
<a-textarea v-model="allSetting.subRoutingRules" placeholder="happ://routing/add/..."></a-textarea>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
</a-collapse-panel>
|
||||
@@ -170,8 +165,7 @@
|
||||
<template #description>{{ i18n
|
||||
"pages.settings.subUpdatesDesc"}}</template>
|
||||
<template #control>
|
||||
<a-input-number :min="1" v-model="allSetting.subUpdates"
|
||||
:style="{ width: '100%' }"></a-input-number>
|
||||
<a-input-number :min="1" v-model="allSetting.subUpdates" :style="{ width: '100%' }"></a-input-number>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
</a-collapse-panel>
|
||||
|
||||
@@ -100,7 +100,8 @@
|
||||
<a-form-item>
|
||||
<a-space direction="vertical" align="center">
|
||||
<a-row type="flex" :gutter="[8,8]" justify="center" style="width:100%">
|
||||
<a-col :xs="24" :sm="app.subJsonUrl || app.subClashUrl ? 12 : 24" style="text-align:center;">
|
||||
<a-col :xs="24" :sm="app.subJsonUrl || app.subClashUrl ? 12 : 24"
|
||||
style="text-align:center;">
|
||||
<tr-qr-box class="qr-box">
|
||||
<a-tag color="purple" class="qr-tag">
|
||||
<span>{{ i18n
|
||||
@@ -270,11 +271,11 @@
|
||||
</a-layout>
|
||||
|
||||
<!-- Bootstrap data for external JS -->
|
||||
<template id="subscription-data" data-sid="{{ .sId }}" data-sub-url="{{ .subUrl }}" data-subjson-url="{{ .subJsonUrl }}" data-subclash-url="{{ .subClashUrl }}"
|
||||
data-download="{{ .download }}" data-upload="{{ .upload }}" data-used="{{ .used }}" data-total="{{ .total }}"
|
||||
data-remained="{{ .remained }}" data-expire="{{ .expire }}" data-lastonline="{{ .lastOnline }}"
|
||||
data-downloadbyte="{{ .downloadByte }}" data-uploadbyte="{{ .uploadByte }}" data-totalbyte="{{ .totalByte }}"
|
||||
data-datepicker="{{ .datepicker }}"></template>
|
||||
<template id="subscription-data" data-sid="{{ .sId }}" data-sub-url="{{ .subUrl }}" data-subjson-url="{{ .subJsonUrl }}"
|
||||
data-subclash-url="{{ .subClashUrl }}" data-download="{{ .download }}" data-upload="{{ .upload }}"
|
||||
data-used="{{ .used }}" data-total="{{ .total }}" data-remained="{{ .remained }}" data-expire="{{ .expire }}"
|
||||
data-lastonline="{{ .lastOnline }}" data-downloadbyte="{{ .downloadByte }}" data-uploadbyte="{{ .uploadByte }}"
|
||||
data-totalbyte="{{ .totalByte }}" data-datepicker="{{ .datepicker }}"></template>
|
||||
<textarea id="subscription-links" style="display:none">{{ range .result }}{{ . }}
|
||||
{{ end }}</textarea>
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
<span>{{ i18n "pages.xray.balancer.addBalancer"}}</span>
|
||||
</a-button>
|
||||
<a-table :columns="balancerColumns" bordered :row-key="r => r.key" :data-source="balancersData"
|
||||
:scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0" :locale='{ filterConfirm: `{{ i18n "confirm" }}`, filterReset: `{{ i18n "reset" }}` }'>
|
||||
:scroll="isMobile ? {} : { x: 200 }" :pagination="false" :indent-size="0"
|
||||
:locale='{ filterConfirm: `{{ i18n "confirm" }}`, filterReset: `{{ i18n "reset" }}` }'>
|
||||
<template slot="action" slot-scope="text, balancer, index">
|
||||
<span>[[ index+1 ]]</span>
|
||||
<a-dropdown :trigger="['click']">
|
||||
@@ -32,7 +33,8 @@
|
||||
<a-tag :style="{ margin: '0' }" v-if="balancer.strategy=='leastPing'" color="green">Least Ping</a-tag>
|
||||
</template>
|
||||
<template slot="selector" slot-scope="text, balancer, index">
|
||||
<a-tag class="info-large-tag" :style="{ margin: '1' }" v-for="sel in balancer.selector">[[ sel ]]</a-tag>
|
||||
<a-tag class="info-large-tag" :style="{ margin: '1' }" v-for="sel in balancer.selector">[[ sel
|
||||
]]</a-tag>
|
||||
</template>
|
||||
</a-table>
|
||||
<a-radio-group v-if="observatoryEnable || burstObservatoryEnable" v-model="obsSettings" @change="changeObsCode"
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled"
|
||||
:style="{ color: '#FFA031' }"></a-icon>
|
||||
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||
<span>{{ i18n "pages.xray.generalConfigsDesc" }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
@@ -15,11 +14,9 @@
|
||||
<template #description>{{ i18n "pages.xray.FreedomStrategyDesc"
|
||||
}}</template>
|
||||
<template #control>
|
||||
<a-select v-model="freedomStrategy"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
<a-select v-model="freedomStrategy" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
:style="{ width: '100%' }">
|
||||
<a-select-option v-for="s in OutboundDomainStrategies"
|
||||
:value="s">
|
||||
<a-select-option v-for="s in OutboundDomainStrategies" :value="s">
|
||||
<span>[[ s ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
@@ -30,11 +27,9 @@
|
||||
<template #description>{{ i18n "pages.xray.RoutingStrategyDesc"
|
||||
}}</template>
|
||||
<template #control>
|
||||
<a-select v-model="routingStrategy"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
<a-select v-model="routingStrategy" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
:style="{ width: '100%' }">
|
||||
<a-select-option v-for="s in routingDomainStrategies"
|
||||
:value="s">
|
||||
<a-select-option v-for="s in routingDomainStrategies" :value="s">
|
||||
<span>[[ s ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
@@ -45,8 +40,7 @@
|
||||
<template #description>{{ i18n "pages.xray.outboundTestUrlDesc"
|
||||
}}</template>
|
||||
<template #control>
|
||||
<a-input v-model="outboundTestUrl"
|
||||
:placeholder="'https://www.google.com/generate_204'"
|
||||
<a-input v-model="outboundTestUrl" :placeholder="'https://www.google.com/generate_204'"
|
||||
:style="{ width: '100%' }"></a-input>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
@@ -93,8 +87,7 @@
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled"
|
||||
:style="{ color: '#FFA031' }"></a-icon>
|
||||
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||
<span>{{ i18n "pages.xray.logConfigsDesc" }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
@@ -104,8 +97,7 @@
|
||||
<template #description>{{ i18n "pages.xray.logLevelDesc"
|
||||
}}</template>
|
||||
<template #control>
|
||||
<a-select v-model="logLevel"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
<a-select v-model="logLevel" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
:style="{ width: '100%' }">
|
||||
<a-select-option v-for="s in log.loglevel" :value="s">
|
||||
<span>[[ s ]]</span>
|
||||
@@ -118,8 +110,7 @@
|
||||
<template #description>{{ i18n "pages.xray.accessLogDesc"
|
||||
}}</template>
|
||||
<template #control>
|
||||
<a-select v-model="accessLog"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
<a-select v-model="accessLog" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
:style="{ width: '100%' }">
|
||||
<a-select-option value>
|
||||
<span>Empty</span>
|
||||
@@ -135,8 +126,7 @@
|
||||
<template #description>{{ i18n "pages.xray.errorLogDesc"
|
||||
}}</template>
|
||||
<template #control>
|
||||
<a-select v-model="errorLog"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
<a-select v-model="errorLog" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
:style="{ width: '100%' }">
|
||||
<a-select-option value>
|
||||
<span>Empty</span>
|
||||
@@ -152,8 +142,7 @@
|
||||
<template #description>{{ i18n "pages.xray.maskAddressDesc"
|
||||
}}</template>
|
||||
<template #control>
|
||||
<a-select v-model="maskAddressLog"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme"
|
||||
<a-select v-model="maskAddressLog" :dropdown-class-name="themeSwitcher.currentTheme"
|
||||
:style="{ width: '100%' }">
|
||||
<a-select-option value>
|
||||
<span>Empty</span>
|
||||
@@ -176,8 +165,7 @@
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled"
|
||||
:style="{ color: '#FFA031' }"></a-icon>
|
||||
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||
<span>{{ i18n "pages.xray.blockConfigsDesc" }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
@@ -191,8 +179,7 @@
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning" :style="{ textAlign: 'center' }">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled"
|
||||
:style="{ color: '#FFA031' }"></a-icon>
|
||||
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||
<span>{{ i18n "pages.xray.blockConnectionsConfigsDesc"
|
||||
}}</span>
|
||||
</template>
|
||||
@@ -201,11 +188,9 @@
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>{{ i18n "pages.xray.blockips" }}</template>
|
||||
<template #control>
|
||||
<a-select mode="tags" v-model="blockedIPs"
|
||||
:style="{ width: '100%' }"
|
||||
<a-select mode="tags" v-model="blockedIPs" :style="{ width: '100%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in settingsData.IPsOptions">
|
||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.IPsOptions">
|
||||
<span>[[ p.label ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
@@ -214,22 +199,18 @@
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>{{ i18n "pages.xray.blockdomains" }}</template>
|
||||
<template #control>
|
||||
<a-select mode="tags" v-model="blockedDomains"
|
||||
:style="{ width: '100%' }"
|
||||
<a-select mode="tags" v-model="blockedDomains" :style="{ width: '100%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in settingsData.BlockDomainsOptions">
|
||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.BlockDomainsOptions">
|
||||
<span>[[ p.label ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning"
|
||||
:style="{ textAlign: 'center', marginTop: '20px' }">
|
||||
<a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled"
|
||||
:style="{ color: '#FFA031' }"></a-icon>
|
||||
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||
<span>{{ i18n "pages.xray.directConnectionsConfigsDesc"
|
||||
}}</span>
|
||||
</template>
|
||||
@@ -238,11 +219,9 @@
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>{{ i18n "pages.xray.directips" }}</template>
|
||||
<template #control>
|
||||
<a-select mode="tags" :style="{ width: '100%' }"
|
||||
v-model="directIPs"
|
||||
<a-select mode="tags" :style="{ width: '100%' }" v-model="directIPs"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in settingsData.IPsOptions">
|
||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.IPsOptions">
|
||||
<span>[[ p.label ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
@@ -251,22 +230,18 @@
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>{{ i18n "pages.xray.directdomains" }}</template>
|
||||
<template #control>
|
||||
<a-select mode="tags" :style="{ width: '100%' }"
|
||||
v-model="directDomains"
|
||||
<a-select mode="tags" :style="{ width: '100%' }" v-model="directDomains"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in settingsData.DomainsOptions">
|
||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.DomainsOptions">
|
||||
<span>[[ p.label ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning"
|
||||
:style="{ textAlign: 'center', marginTop: '20px' }">
|
||||
<a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled"
|
||||
:style="{ color: '#FFA031' }"></a-icon>
|
||||
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||
<span>{{ i18n "pages.xray.ipv4RoutingDesc" }}</span>
|
||||
</template>
|
||||
</a-alert>
|
||||
@@ -274,22 +249,18 @@
|
||||
<a-setting-list-item paddings="small">
|
||||
<template #title>{{ i18n "pages.xray.ipv4Routing" }}</template>
|
||||
<template #control>
|
||||
<a-select mode="tags" :style="{ width: '100%' }"
|
||||
v-model="ipv4Domains"
|
||||
<a-select mode="tags" :style="{ width: '100%' }" v-model="ipv4Domains"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in settingsData.ServicesOptions">
|
||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">
|
||||
<span>[[ p.label ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
<a-row :xs="24" :sm="24" :lg="12">
|
||||
<a-alert type="warning"
|
||||
:style="{ textAlign: 'center', marginTop: '20px' }">
|
||||
<a-alert type="warning" :style="{ textAlign: 'center', marginTop: '20px' }">
|
||||
<template slot="message">
|
||||
<a-icon type="exclamation-circle" theme="filled"
|
||||
:style="{ color: '#FFA031' }"></a-icon>
|
||||
<a-icon type="exclamation-circle" theme="filled" :style="{ color: '#FFA031' }"></a-icon>
|
||||
{{ i18n "pages.xray.warpRoutingDesc" }}
|
||||
</template>
|
||||
</a-alert>
|
||||
@@ -298,18 +269,15 @@
|
||||
<template #title>{{ i18n "pages.xray.warpRouting" }}</template>
|
||||
<template #control>
|
||||
<template v-if="WarpExist">
|
||||
<a-select mode="tags" :style="{ width: '100%' }"
|
||||
v-model="warpDomains"
|
||||
<a-select mode="tags" :style="{ width: '100%' }" v-model="warpDomains"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in settingsData.ServicesOptions">
|
||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">
|
||||
<span>[[ p.label ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-button type="primary" icon="cloud"
|
||||
@click="showWarp()">WARP</a-button>
|
||||
<a-button type="primary" icon="cloud" @click="showWarp()">WARP</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
@@ -317,11 +285,9 @@
|
||||
<template #title>{{ i18n "pages.xray.nordRouting" }}</template>
|
||||
<template #control>
|
||||
<template v-if="NordExist">
|
||||
<a-select mode="tags" :style="{ width: '100%' }"
|
||||
v-model="nordDomains"
|
||||
<a-select mode="tags" :style="{ width: '100%' }" v-model="nordDomains"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="p.value" :label="p.label"
|
||||
v-for="p in settingsData.ServicesOptions">
|
||||
<a-select-option :value="p.value" :label="p.label" v-for="p in settingsData.ServicesOptions">
|
||||
<span>[[ p.label ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
@@ -333,8 +299,7 @@
|
||||
</template>
|
||||
</a-setting-list-item>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel key="6"
|
||||
header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
||||
<a-collapse-panel key="6" header='{{ i18n "pages.settings.resetDefaultConfig"}}'>
|
||||
<a-space direction="horizontal" :style="{ padding: '0 20px' }">
|
||||
<a-button type="danger" @click="resetXrayConfigToDefault">
|
||||
<span>{{ i18n "pages.settings.resetDefaultConfig" }}</span>
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
<template #control>
|
||||
<a-select v-model="dnsStrategy" :style="{ width: '100%' }"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option :value="l" :label="l" v-for="l in ['UseSystem', 'UseIP', 'UseIPv4', 'UseIPv6']">
|
||||
<a-select-option :value="l" :label="l"
|
||||
v-for="l in ['UseSystem', 'UseIP', 'UseIPv4', 'UseIPv6']">
|
||||
<span>[[ l ]]</span>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
@@ -7,21 +7,16 @@
|
||||
<span v-if="!isMobile">{{ i18n
|
||||
"pages.xray.outbound.addOutbound" }}</span>
|
||||
</a-button>
|
||||
<a-button type="primary" icon="cloud"
|
||||
@click="showWarp()">WARP</a-button>
|
||||
<a-button type="primary" icon="api"
|
||||
@click="showNord()">NordVPN</a-button>
|
||||
<a-button type="primary" icon="cloud" @click="showWarp()">WARP</a-button>
|
||||
<a-button type="primary" icon="api" @click="showNord()">NordVPN</a-button>
|
||||
</a-space>
|
||||
</a-col>
|
||||
<a-col :xs="12" :sm="12" :lg="12" :style="{ textAlign: 'right' }">
|
||||
<a-button-group>
|
||||
<a-button icon="sync" @click="refreshOutboundTraffic()"
|
||||
:loading="refreshing"></a-button>
|
||||
<a-popconfirm placement="topRight"
|
||||
@confirm="resetOutboundTraffic(-1)"
|
||||
<a-button icon="sync" @click="refreshOutboundTraffic()" :loading="refreshing"></a-button>
|
||||
<a-popconfirm placement="topRight" @confirm="resetOutboundTraffic(-1)"
|
||||
title='{{ i18n "pages.inbounds.resetTrafficContent"}}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme"
|
||||
ok-text='{{ i18n "reset"}}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme" ok-text='{{ i18n "reset"}}'
|
||||
cancel-text='{{ i18n "cancel"}}'>
|
||||
<a-icon slot="icon" type="question-circle-o"
|
||||
:style="{ color: themeSwitcher.isDarkTheme ? '#008771' : '#008771' }"></a-icon>
|
||||
@@ -30,10 +25,8 @@
|
||||
</a-button-group>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-table :columns="outboundColumns" bordered :row-key="r => r.key"
|
||||
:data-source="outboundData"
|
||||
:scroll="isMobile ? {} : { x: 800 }" :pagination="false"
|
||||
:indent-size="0"
|
||||
<a-table :columns="outboundColumns" bordered :row-key="r => r.key" :data-source="outboundData"
|
||||
:scroll="isMobile ? {} : { x: 800 }" :pagination="false" :indent-size="0"
|
||||
:locale='{ filterConfirm: `{{ i18n "confirm" }}`, filterReset: `{{ i18n "reset" }}` }'>
|
||||
<template slot="action" slot-scope="text, outbound, index">
|
||||
<span>[[ index+1 ]]</span>
|
||||
@@ -41,8 +34,7 @@
|
||||
<a-icon @click="e => e.preventDefault()" type="more"
|
||||
:style="{ fontSize: '16px', textDecoration: 'bold' }"></a-icon>
|
||||
<a-menu slot="overlay" :theme="themeSwitcher.currentTheme">
|
||||
<a-menu-item v-if="index>0"
|
||||
@click="setFirstOutbound(index)">
|
||||
<a-menu-item v-if="index>0" @click="setFirstOutbound(index)">
|
||||
<a-icon type="vertical-align-top"></a-icon>
|
||||
<span>{{ i18n "pages.xray.rules.first"}}</span>
|
||||
</a-menu-item>
|
||||
@@ -66,8 +58,7 @@
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template slot="address" slot-scope="text, outbound, index">
|
||||
<p :style="{ margin: '0 5px' }"
|
||||
v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
||||
<p :style="{ margin: '0 5px' }" v-for="addr in findOutboundAddress(outbound)">[[ addr ]]</p>
|
||||
</template>
|
||||
<template slot="protocol" slot-scope="text, outbound, index">
|
||||
<a-tag :style="{ margin: '0' }" color="purple">[[ outbound.protocol
|
||||
@@ -76,11 +67,8 @@
|
||||
v-if="[Protocols.VMess, Protocols.VLESS, Protocols.Trojan, Protocols.Shadowsocks].includes(outbound.protocol)">
|
||||
<a-tag :style="{ margin: '0' }" color="blue">[[
|
||||
outbound.streamSettings.network ]]</a-tag>
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
v-if="outbound.streamSettings.security=='tls'"
|
||||
color="green">tls</a-tag>
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
v-if="outbound.streamSettings.security=='reality'"
|
||||
<a-tag :style="{ margin: '0' }" v-if="outbound.streamSettings.security=='tls'" color="green">tls</a-tag>
|
||||
<a-tag :style="{ margin: '0' }" v-if="outbound.streamSettings.security=='reality'"
|
||||
color="green">reality</a-tag>
|
||||
</template>
|
||||
</template>
|
||||
@@ -91,10 +79,7 @@
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.xray.outbound.test"
|
||||
}}</template>
|
||||
<a-button
|
||||
type="primary"
|
||||
shape="circle"
|
||||
icon="thunderbolt"
|
||||
<a-button type="primary" shape="circle" icon="thunderbolt"
|
||||
:loading="outboundTestStates[index] && outboundTestStates[index].testing"
|
||||
@click="testOutbound(index)"
|
||||
:disabled="(outbound.protocol === 'blackhole' || outbound.tag === 'blocked') || (outboundTestStates[index] && outboundTestStates[index].testing)">
|
||||
@@ -102,24 +87,20 @@
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template slot="testResult" slot-scope="text, outbound, index">
|
||||
<div
|
||||
v-if="outboundTestStates[index] && outboundTestStates[index].result">
|
||||
<a-tag v-if="outboundTestStates[index].result.success"
|
||||
color="green">
|
||||
<div v-if="outboundTestStates[index] && outboundTestStates[index].result">
|
||||
<a-tag v-if="outboundTestStates[index].result.success" color="green">
|
||||
[[ outboundTestStates[index].result.delay ]]ms
|
||||
<span v-if="outboundTestStates[index].result.statusCode">
|
||||
([[ outboundTestStates[index].result.statusCode
|
||||
]])</span>
|
||||
</a-tag>
|
||||
<a-tooltip v-else
|
||||
:title="outboundTestStates[index].result.error">
|
||||
<a-tooltip v-else :title="outboundTestStates[index].result.error">
|
||||
<a-tag color="red">
|
||||
Failed
|
||||
</a-tag>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<span
|
||||
v-else-if="outboundTestStates[index] && outboundTestStates[index].testing">
|
||||
<span v-else-if="outboundTestStates[index] && outboundTestStates[index].testing">
|
||||
<a-icon type="loading" />
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
17
x-ui.sh
17
x-ui.sh
@@ -929,9 +929,18 @@ update_all_geofiles() {
|
||||
|
||||
update_geofiles() {
|
||||
case "${1}" in
|
||||
"main") dat_files=(geoip geosite); dat_source="Loyalsoldier/v2ray-rules-dat";;
|
||||
"IR") dat_files=(geoip_IR geosite_IR); dat_source="chocolate4u/Iran-v2ray-rules" ;;
|
||||
"RU") dat_files=(geoip_RU geosite_RU); dat_source="runetfreedom/russia-v2ray-rules-dat";;
|
||||
"main")
|
||||
dat_files=(geoip geosite)
|
||||
dat_source="Loyalsoldier/v2ray-rules-dat"
|
||||
;;
|
||||
"IR")
|
||||
dat_files=(geoip_IR geosite_IR)
|
||||
dat_source="chocolate4u/Iran-v2ray-rules"
|
||||
;;
|
||||
"RU")
|
||||
dat_files=(geoip_RU geosite_RU)
|
||||
dat_source="runetfreedom/russia-v2ray-rules-dat"
|
||||
;;
|
||||
esac
|
||||
for dat in "${dat_files[@]}"; do
|
||||
# Remove suffix for remote filename (e.g., geoip_IR -> geoip)
|
||||
@@ -1687,8 +1696,6 @@ run_speedtest() {
|
||||
speedtest
|
||||
}
|
||||
|
||||
|
||||
|
||||
ip_validation() {
|
||||
ipv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
|
||||
ipv4_regex="^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)$"
|
||||
|
||||
Reference in New Issue
Block a user