mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-06 20:39:35 +00:00
add hysteria inbound
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template slot="enable" slot-scope="text, client, index">
|
||||
<a-switch v-model="client.enable" @change="switchEnableClient(record.id,client)"></a-switch>
|
||||
<a-switch v-model="client.enable" @change="switchEnableClient(record.id, client, $event)"></a-switch>
|
||||
</template>
|
||||
<template slot="online" slot-scope="text, client, index">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||
@@ -165,7 +165,7 @@
|
||||
<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)"></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>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{{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>
|
||||
@@ -10,38 +11,62 @@
|
||||
<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>
|
||||
<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>
|
||||
<a-input v-model.trim="client.password"></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.HYSTERIA">
|
||||
<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>
|
||||
Auth Password
|
||||
<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">
|
||||
<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-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">
|
||||
@@ -51,7 +76,8 @@
|
||||
<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>
|
||||
@@ -66,7 +92,8 @@
|
||||
<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>
|
||||
@@ -77,19 +104,21 @@
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.inbounds.IPLimitDesc"}}</span>
|
||||
</template>
|
||||
<span>{{ i18n "pages.inbounds.IPLimit"}} </span>
|
||||
<span>{{ i18n "pages.inbounds.IPLimit"}} </span>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="client.limitIp" min="0"></a-input-number>
|
||||
<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">
|
||||
<span>{{ i18n "pages.inbounds.IPLimitlogDesc" }}</span>
|
||||
</template>
|
||||
<span>{{ i18n "pages.inbounds.IPLimitlog" }} </span>
|
||||
<span>{{ i18n "pages.inbounds.IPLimitlog" }} </span>
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@@ -98,19 +127,24 @@
|
||||
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
|
||||
</template>
|
||||
<span :style="{ color: '#FF4D4F' }">
|
||||
<a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
|
||||
<a-icon type="delete"
|
||||
@click="clearDBClientIps(client.email)"></a-icon>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
<a-form layout="block">
|
||||
<a-textarea id="clientIPs" readonly @click="getDBClientIps(client.email)" placeholder="Click To Get IPs"
|
||||
<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-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 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>
|
||||
</a-form-item>
|
||||
<a-form-item>
|
||||
@@ -123,45 +157,57 @@
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-input-number v-model.number="client._totalGB" :min="0"></a-input-number>
|
||||
<a-input-number v-model.number="client._totalGB"
|
||||
:min="0"></a-input-number>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="isEdit && clientStats" label='{{ i18n "usage" }}'>
|
||||
<a-tag :color="ColorUtils.clientUsageColor(clientStats, app.trafficDiff)">
|
||||
<a-tag
|
||||
:color="ColorUtils.clientUsageColor(clientStats, app.trafficDiff)">
|
||||
[[ SizeFormatter.sizeFormat(clientStats.up) ]] /
|
||||
[[ SizeFormatter.sizeFormat(clientStats.down) ]]
|
||||
([[ SizeFormatter.sizeFormat(clientStats.up + clientStats.down) ]])
|
||||
</a-tag>
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.inbounds.resetTraffic" }}</template>
|
||||
<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-tooltip>
|
||||
</a-form-item>
|
||||
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
|
||||
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
|
||||
<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-form-item v-if="delayedStart"
|
||||
label='{{ i18n "pages.client.expireDays" }}'>
|
||||
<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">
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">{{ i18n "pages.client.renewDesc" }}</template>
|
||||
<template slot="title">{{ i18n "pages.client.renewDesc"
|
||||
}}</template>
|
||||
{{ i18n "pages.client.renew" }}
|
||||
<a-icon type="question-circle"></a-icon>
|
||||
</a-tooltip>
|
||||
|
||||
@@ -74,7 +74,8 @@
|
||||
<a-select-option value="never">{{ i18n
|
||||
"pages.inbounds.periodicTrafficReset.never" }}</a-select-option>
|
||||
<a-select-option value="hourly">{{ i18n
|
||||
"pages.inbounds.periodicTrafficReset.hourly" }}</a-select-option>
|
||||
"pages.inbounds.periodicTrafficReset.hourly"
|
||||
}}</a-select-option>
|
||||
<a-select-option value="daily">{{ i18n
|
||||
"pages.inbounds.periodicTrafficReset.daily" }}</a-select-option>
|
||||
<a-select-option value="weekly">{{ i18n
|
||||
@@ -154,6 +155,11 @@
|
||||
{{template "form/tun"}}
|
||||
</template>
|
||||
|
||||
<!-- hysteria -->
|
||||
<template v-if="inbound.protocol === Protocols.HYSTERIA">
|
||||
{{template "form/hysteria"}}
|
||||
</template>
|
||||
|
||||
<!-- stream settings -->
|
||||
<template v-if="inbound.canEnableStream()">
|
||||
{{template "form/streamSettings"}}
|
||||
|
||||
32
web/html/form/protocol/hysteria.html
Normal file
32
web/html/form/protocol/hysteria.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{{define "form/hysteria"}}
|
||||
<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>
|
||||
</a-collapse>
|
||||
<a-collapse v-else>
|
||||
<a-collapse-panel :header="'{{ i18n "pages.client.clientCount"}} : ' +
|
||||
inbound.settings.hysterias.length">
|
||||
<table width="100%">
|
||||
<tr class="client-table-header">
|
||||
<th>{{ i18n "pages.inbounds.email" }}</th>
|
||||
<th>Auth</th>
|
||||
</tr>
|
||||
<tr v-for="(client, index) in inbound.settings.hysterias"
|
||||
:class="index % 2 == 1 ? 'client-table-odd-row' : ''">
|
||||
<td>[[ client.email ]]</td>
|
||||
<td>[[ client.auth ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
<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-form-item>
|
||||
</a-form>
|
||||
{{end}}
|
||||
75
web/html/form/stream/stream_hysteria.html
Normal file
75
web/html/form/stream/stream_hysteria.html
Normal file
@@ -0,0 +1,75 @@
|
||||
{{define "form/streamHysteria"}}
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }">
|
||||
<a-form-item label='Auth Password'>
|
||||
<a-input v-model.trim="inbound.stream.hysteria.auth"></a-input>
|
||||
</a-form-item>
|
||||
<a-form-item label='UDP Idle Timeout'>
|
||||
<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>
|
||||
</a-form-item>
|
||||
<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-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-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-form-item>
|
||||
<a-form-item label='Rewrite Host'>
|
||||
<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-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-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-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>
|
||||
<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-form-item>
|
||||
</template>
|
||||
</template>
|
||||
</a-form>
|
||||
{{end}}
|
||||
@@ -1,7 +1,8 @@
|
||||
{{define "form/streamSettings"}}
|
||||
<!-- select stream network -->
|
||||
<a-form :colon="false" :label-col="{ md: {span:8} }"
|
||||
:wrapper-col="{ md: {span:14} }">
|
||||
: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"
|
||||
@@ -36,6 +37,11 @@
|
||||
{{template "form/streamGRPC"}}
|
||||
</template>
|
||||
|
||||
<!-- hysteria -->
|
||||
<template v-if="inbound.stream.network === 'hysteria'">
|
||||
{{template "form/streamHysteria"}}
|
||||
</template>
|
||||
|
||||
<!-- httpupgrade -->
|
||||
<template v-if="inbound.stream.network === 'httpupgrade'">
|
||||
{{template "form/streamHTTPUpgrade"}}
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
<!-- tls enable -->
|
||||
<a-form v-if="inbound.canEnableTls()" :colon="false"
|
||||
:label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }">
|
||||
<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 value="tls">TLS</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<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 value="tls">TLS</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-else><a-divider style="margin:0;">TLS</a-divider></template>
|
||||
|
||||
<!-- tls settings -->
|
||||
<template v-if="inbound.stream.isTls">
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
{{ template "page/head_end" .}}
|
||||
|
||||
{{ template "page/body_start" .}}
|
||||
<a-layout id="app" v-cloak :class="themeSwitcher.currentTheme + ' inbounds-page'">
|
||||
<a-layout id="app" v-cloak
|
||||
:class="themeSwitcher.currentTheme + ' inbounds-page'">
|
||||
<a-sidebar></a-sidebar>
|
||||
<a-layout id="content-layout">
|
||||
<a-layout-content>
|
||||
<a-spin :spinning="loadingStates.spinning" :delay="500" tip='{{ i18n "loading"}}' size="large">
|
||||
<a-spin :spinning="loadingStates.spinning" :delay="500"
|
||||
tip='{{ i18n "loading"}}' size="large">
|
||||
<transition name="list" appear>
|
||||
<a-alert type="error" v-if="showAlert && loadingStates.fetched" :style="{ marginBottom: '10px' }"
|
||||
message='{{ i18n "secAlertTitle" }}' color="red" description='{{ i18n "secAlertSsl" }}' show-icon closable>
|
||||
<a-alert type="error" v-if="showAlert && loadingStates.fetched"
|
||||
:style="{ marginBottom: '10px' }"
|
||||
message='{{ i18n "secAlertTitle" }}' color="red"
|
||||
description='{{ i18n "secAlertSsl" }}' show-icon closable>
|
||||
</a-alert>
|
||||
</transition>
|
||||
<transition name="list" appear>
|
||||
@@ -21,7 +25,8 @@
|
||||
<a-card size="small" :style="{ padding: '16px' }" hoverable>
|
||||
<a-row>
|
||||
<a-col :sm="12" :md="5">
|
||||
<a-custom-statistic title='{{ i18n "pages.inbounds.totalDownUp" }}'
|
||||
<a-custom-statistic
|
||||
title='{{ i18n "pages.inbounds.totalDownUp" }}'
|
||||
:value="`${SizeFormatter.sizeFormat(total.up)} / ${SizeFormatter.sizeFormat(total.down)}`">
|
||||
<template #prefix>
|
||||
<a-icon type="swap"></a-icon>
|
||||
@@ -29,7 +34,8 @@
|
||||
</a-custom-statistic>
|
||||
</a-col>
|
||||
<a-col :sm="12" :md="5">
|
||||
<a-custom-statistic title='{{ i18n "pages.inbounds.totalUsage" }}'
|
||||
<a-custom-statistic
|
||||
title='{{ i18n "pages.inbounds.totalUsage" }}'
|
||||
:value="SizeFormatter.sizeFormat(total.up + total.down)"
|
||||
:style="{ marginTop: isMobile ? '10px' : 0 }">
|
||||
<template #prefix>
|
||||
@@ -38,15 +44,19 @@
|
||||
</a-custom-statistic>
|
||||
</a-col>
|
||||
<a-col :sm="12" :md="5">
|
||||
<a-custom-statistic title='{{ i18n "pages.inbounds.allTimeTrafficUsage" }}'
|
||||
:value="SizeFormatter.sizeFormat(total.allTime)" :style="{ marginTop: isMobile ? '10px' : 0 }">
|
||||
<a-custom-statistic
|
||||
title='{{ i18n "pages.inbounds.allTimeTrafficUsage" }}'
|
||||
:value="SizeFormatter.sizeFormat(total.allTime)"
|
||||
:style="{ marginTop: isMobile ? '10px' : 0 }">
|
||||
<template #prefix>
|
||||
<a-icon type="history"></a-icon>
|
||||
</template>
|
||||
</a-custom-statistic>
|
||||
</a-col>
|
||||
<a-col :sm="12" :md="5">
|
||||
<a-custom-statistic title='{{ i18n "pages.inbounds.inboundCount" }}' :value="dbInbounds.length"
|
||||
<a-custom-statistic
|
||||
title='{{ i18n "pages.inbounds.inboundCount" }}'
|
||||
:value="dbInbounds.length"
|
||||
:style="{ marginTop: isMobile ? '10px' : 0 }">
|
||||
<template #prefix>
|
||||
<a-icon type="bars"></a-icon>
|
||||
@@ -60,33 +70,50 @@
|
||||
<a-space direction="horizontal">
|
||||
<a-icon type="team"></a-icon>
|
||||
<div>
|
||||
<a-back-top :target="() => document.getElementById('content-layout')"
|
||||
<a-back-top
|
||||
:target="() => document.getElementById('content-layout')"
|
||||
visibility-height="200"></a-back-top>
|
||||
<a-tag color="green">[[ total.clients ]]</a-tag>
|
||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover title='{{ i18n "disabled" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in total.deactive"><span>[[ clientEmail ]]</span></div>
|
||||
<div
|
||||
v-for="clientEmail in total.deactive"><span>[[
|
||||
clientEmail ]]</span></div>
|
||||
</template>
|
||||
<a-tag v-if="total.deactive.length">[[ total.deactive.length ]]</a-tag>
|
||||
<a-tag v-if="total.deactive.length">[[
|
||||
total.deactive.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover title='{{ i18n "depleted" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in total.depleted"><span>[[ clientEmail ]]</span></div>
|
||||
<div
|
||||
v-for="clientEmail in total.depleted"><span>[[
|
||||
clientEmail ]]</span></div>
|
||||
</template>
|
||||
<a-tag color="red" v-if="total.depleted.length">[[ total.depleted.length ]]</a-tag>
|
||||
<a-tag color="red" v-if="total.depleted.length">[[
|
||||
total.depleted.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depletingSoon" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in total.expiring"><span>[[ clientEmail ]]</span></div>
|
||||
<div
|
||||
v-for="clientEmail in total.expiring"><span>[[
|
||||
clientEmail ]]</span></div>
|
||||
</template>
|
||||
<a-tag color="orange" v-if="total.expiring.length">[[ total.expiring.length ]]</a-tag>
|
||||
<a-tag color="orange"
|
||||
v-if="total.expiring.length">[[
|
||||
total.expiring.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover title='{{ i18n "online" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in onlineClients"><span>[[ clientEmail ]]</span></div>
|
||||
<div
|
||||
v-for="clientEmail in onlineClients"><span>[[
|
||||
clientEmail ]]</span></div>
|
||||
</template>
|
||||
<a-tag color="blue" v-if="onlineClients.length">[[ onlineClients.length ]]</a-tag>
|
||||
<a-tag color="blue" v-if="onlineClients.length">[[
|
||||
onlineClients.length ]]</a-tag>
|
||||
</a-popover>
|
||||
</div>
|
||||
</a-space>
|
||||
@@ -100,14 +127,18 @@
|
||||
<a-card hoverable>
|
||||
<template #title>
|
||||
<a-space direction="horizontal">
|
||||
<a-button type="primary" icon="plus" @click="openAddInbound">
|
||||
<template v-if="!isMobile">{{ i18n "pages.inbounds.addInbound" }}</template>
|
||||
<a-button type="primary" icon="plus"
|
||||
@click="openAddInbound">
|
||||
<template v-if="!isMobile">{{ i18n
|
||||
"pages.inbounds.addInbound" }}</template>
|
||||
</a-button>
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a-button type="primary" icon="menu">
|
||||
<template v-if="!isMobile">{{ i18n "pages.inbounds.generalActions" }}</template>
|
||||
<template v-if="!isMobile">{{ i18n
|
||||
"pages.inbounds.generalActions" }}</template>
|
||||
</a-button>
|
||||
<a-menu slot="overlay" @click="a => generalActions(a)" :theme="themeSwitcher.currentTheme">
|
||||
<a-menu slot="overlay" @click="a => generalActions(a)"
|
||||
:theme="themeSwitcher.currentTheme">
|
||||
<a-menu-item key="import">
|
||||
<a-icon type="import"></a-icon>
|
||||
{{ i18n "pages.inbounds.importInbound" }}
|
||||
@@ -118,7 +149,8 @@
|
||||
</a-menu-item>
|
||||
<a-menu-item key="subs" v-if="subSettings.enable">
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export" }} - {{ i18n "pages.settings.subSettings" }}
|
||||
{{ i18n "pages.inbounds.export" }} - {{ i18n
|
||||
"pages.settings.subSettings" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="resetInbounds">
|
||||
<a-icon type="reload"></a-icon>
|
||||
@@ -128,7 +160,8 @@
|
||||
<a-icon type="file-done"></a-icon>
|
||||
{{ i18n "pages.inbounds.resetAllClientTraffics" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }">
|
||||
<a-menu-item key="delDepletedClients"
|
||||
:style="{ color: '#FF4D4F' }">
|
||||
<a-icon type="rest"></a-icon>
|
||||
{{ i18n "pages.inbounds.delDepletedClients" }}
|
||||
</a-menu-item>
|
||||
@@ -138,20 +171,28 @@
|
||||
</template>
|
||||
<template #extra>
|
||||
<a-button-group>
|
||||
<a-button icon="sync" @click="manualRefresh" :loading="refreshing"></a-button>
|
||||
<a-popover placement="bottomRight" trigger="click" :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-button icon="sync" @click="manualRefresh"
|
||||
:loading="refreshing"></a-button>
|
||||
<a-popover placement="bottomRight" trigger="click"
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
<div class="ant-custom-popover-title">
|
||||
<a-switch v-model="isRefreshEnabled" @change="toggleRefresh" size="small"></a-switch>
|
||||
<a-switch v-model="isRefreshEnabled"
|
||||
@change="toggleRefresh" size="small"></a-switch>
|
||||
<span>{{ i18n "pages.inbounds.autoRefresh" }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #content>
|
||||
<a-space direction="vertical">
|
||||
<span>{{ i18n "pages.inbounds.autoRefreshInterval" }}</span>
|
||||
<a-select v-model="refreshInterval" :disabled="!isRefreshEnabled" :style="{ width: '100%' }"
|
||||
@change="changeRefreshInterval" :dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in [5,10,30,60]" :value="key*1000">[[ key ]]s</a-select-option>
|
||||
<span>{{ i18n "pages.inbounds.autoRefreshInterval"
|
||||
}}</span>
|
||||
<a-select v-model="refreshInterval"
|
||||
:disabled="!isRefreshEnabled"
|
||||
:style="{ width: '100%' }"
|
||||
@change="changeRefreshInterval"
|
||||
:dropdown-class-name="themeSwitcher.currentTheme">
|
||||
<a-select-option v-for="key in [5,10,30,60]"
|
||||
:value="key*1000">[[ key ]]s</a-select-option>
|
||||
</a-select>
|
||||
</a-space>
|
||||
</template>
|
||||
@@ -160,27 +201,38 @@
|
||||
</a-button-group>
|
||||
</template>
|
||||
<a-space direction="vertical">
|
||||
<div :style="isMobile ? {} : { display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }">
|
||||
<div
|
||||
:style="isMobile ? {} : { display: 'flex', alignItems: 'center', justifyContent: 'flex-start' }">
|
||||
<a-switch v-model="enableFilter"
|
||||
:style="isMobile ? { marginBottom: '.5rem', display: 'flex' } : { marginRight: '.5rem' }"
|
||||
@change="toggleFilter">
|
||||
<a-icon slot="checkedChildren" type="search"></a-icon>
|
||||
<a-icon slot="unCheckedChildren" type="filter"></a-icon>
|
||||
</a-switch>
|
||||
<a-input v-if="!enableFilter" v-model.lazy="searchKey" placeholder='{{ i18n "search" }}' autofocus
|
||||
:style="{ maxWidth: '300px' }" :size="isMobile ? 'small' : ''"></a-input>
|
||||
<a-radio-group v-if="enableFilter" v-model="filterBy" @change="filterInbounds" button-style="solid"
|
||||
<a-input v-if="!enableFilter" v-model.lazy="searchKey"
|
||||
placeholder='{{ i18n "search" }}' autofocus
|
||||
:style="{ maxWidth: '300px' }"
|
||||
:size="isMobile ? 'small' : ''"></a-input>
|
||||
<a-radio-group v-if="enableFilter" v-model="filterBy"
|
||||
@change="filterInbounds" button-style="solid"
|
||||
:size="isMobile ? 'small' : ''">
|
||||
<a-radio-button value="">{{ i18n "none" }}</a-radio-button>
|
||||
<a-radio-button value="deactive">{{ i18n "disabled" }}</a-radio-button>
|
||||
<a-radio-button value="depleted">{{ i18n "depleted" }}</a-radio-button>
|
||||
<a-radio-button value="expiring">{{ i18n "depletingSoon" }}</a-radio-button>
|
||||
<a-radio-button value="online">{{ i18n "online" }}</a-radio-button>
|
||||
<a-radio-button value>{{ i18n "none" }}</a-radio-button>
|
||||
<a-radio-button value="deactive">{{ i18n "disabled"
|
||||
}}</a-radio-button>
|
||||
<a-radio-button value="depleted">{{ i18n "depleted"
|
||||
}}</a-radio-button>
|
||||
<a-radio-button value="expiring">{{ i18n "depletingSoon"
|
||||
}}</a-radio-button>
|
||||
<a-radio-button value="online">{{ i18n "online"
|
||||
}}</a-radio-button>
|
||||
</a-radio-group>
|
||||
</div>
|
||||
<a-table :columns="isMobile ? mobileColumns : columns" :row-key="dbInbound => dbInbound.id"
|
||||
:data-source="searchedInbounds" :scroll="isMobile ? {} : { x: 1000 }"
|
||||
:pagination=pagination(searchedInbounds) :expand-icon-as-cell="false" :expand-row-by-click="false"
|
||||
<a-table :columns="isMobile ? mobileColumns : columns"
|
||||
:row-key="dbInbound => dbInbound.id"
|
||||
:data-source="searchedInbounds"
|
||||
:scroll="isMobile ? {} : { x: 1000 }"
|
||||
:pagination=pagination(searchedInbounds)
|
||||
:expand-icon-as-cell="false" :expand-row-by-click="false"
|
||||
:expand-icon-column-index="0" :indent-size="0"
|
||||
:row-class-name="dbInbound => (dbInbound.isMultiUser() ? '' : 'hideExpandIcon')"
|
||||
:style="{ marginTop: '10px' }"
|
||||
@@ -189,7 +241,8 @@
|
||||
<a-dropdown :trigger="['click']">
|
||||
<a-icon @click="e => e.preventDefault()" type="more"
|
||||
:style="{ fontSize: '20px', textDecoration: 'solid' }"></a-icon>
|
||||
<a-menu slot="overlay" @click="a => clickAction(a, dbInbound)"
|
||||
<a-menu slot="overlay"
|
||||
@click="a => clickAction(a, dbInbound)"
|
||||
:theme="themeSwitcher.currentTheme">
|
||||
<a-menu-item key="edit">
|
||||
<a-icon type="edit"></a-icon>
|
||||
@@ -211,7 +264,8 @@
|
||||
</a-menu-item>
|
||||
<a-menu-item key="resetClients">
|
||||
<a-icon type="file-done"></a-icon>
|
||||
{{ i18n "pages.inbounds.resetInboundClientTraffics"}}
|
||||
{{ i18n
|
||||
"pages.inbounds.resetInboundClientTraffics"}}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="export">
|
||||
<a-icon type="export"></a-icon>
|
||||
@@ -219,9 +273,11 @@
|
||||
</a-menu-item>
|
||||
<a-menu-item key="subs" v-if="subSettings.enable">
|
||||
<a-icon type="export"></a-icon>
|
||||
{{ i18n "pages.inbounds.export"}} - {{ i18n "pages.settings.subSettings" }}
|
||||
{{ i18n "pages.inbounds.export"}} - {{ i18n
|
||||
"pages.settings.subSettings" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delDepletedClients" :style="{ color: '#FF4D4F' }">
|
||||
<a-menu-item key="delDepletedClients"
|
||||
:style="{ color: '#FF4D4F' }">
|
||||
<a-icon type="rest"></a-icon>
|
||||
{{ i18n "pages.inbounds.delDepletedClients" }}
|
||||
</a-menu-item>
|
||||
@@ -237,10 +293,12 @@
|
||||
{{ i18n "pages.inbounds.exportInbound" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="resetTraffic">
|
||||
<a-icon type="retweet"></a-icon> {{ i18n "pages.inbounds.resetTraffic" }}
|
||||
<a-icon type="retweet"></a-icon> {{ i18n
|
||||
"pages.inbounds.resetTraffic" }}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="clone">
|
||||
<a-icon type="block"></a-icon> {{ i18n "pages.inbounds.clone"}}
|
||||
<a-icon type="block"></a-icon> {{ i18n
|
||||
"pages.inbounds.clone"}}
|
||||
</a-menu-item>
|
||||
<a-menu-item key="delete">
|
||||
<span :style="{ color: '#FF4D4F' }">
|
||||
@@ -256,26 +314,38 @@
|
||||
</a-dropdown>
|
||||
</template>
|
||||
<template slot="protocol" slot-scope="text, dbInbound">
|
||||
<a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag>
|
||||
<template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||
<a-tag :style="{ margin: '0' }" color="green">[[ dbInbound.toInbound().stream.network ]]</a-tag>
|
||||
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls"
|
||||
<a-tag :style="{ margin: '0' }" color="purple">[[
|
||||
dbInbound.protocol ]]</a-tag>
|
||||
<template
|
||||
v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||
<a-tag :style="{ margin: '0' }" color="green">[[
|
||||
dbInbound.toInbound().stream.network ]]</a-tag>
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
v-if="dbInbound.toInbound().stream.isTls"
|
||||
color="blue">TLS</a-tag>
|
||||
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality"
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
v-if="dbInbound.toInbound().stream.isReality"
|
||||
color="blue">Reality</a-tag>
|
||||
</template>
|
||||
</template>
|
||||
<template slot="clients" slot-scope="text, dbInbound">
|
||||
<template v-if="clientCount[dbInbound.id]">
|
||||
<a-tag :style="{ margin: '0' }" color="green">[[ clientCount[dbInbound.id].clients ]]</a-tag>
|
||||
<a-popover title='{{ i18n "disabled" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tag :style="{ margin: '0' }" color="green">[[
|
||||
clientCount[dbInbound.id].clients ]]</a-tag>
|
||||
<a-popover title='{{ i18n "disabled" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].deactive"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
@@ -286,84 +356,113 @@
|
||||
v-if="clientCount[dbInbound.id].deactive.length">[[
|
||||
clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depleted" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover title='{{ i18n "depleted" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].depleted"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="red"
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }"
|
||||
color="red"
|
||||
v-if="clientCount[dbInbound.id].depleted.length">[[
|
||||
clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depletingSoon" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover title='{{ i18n "depletingSoon" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].expiring"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange"
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }"
|
||||
color="orange"
|
||||
v-if="clientCount[dbInbound.id].expiring.length">[[
|
||||
clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover title='{{ i18n "online" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].online"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="blue"
|
||||
v-if="clientCount[dbInbound.id].online.length">[[ clientCount[dbInbound.id].online.length
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }"
|
||||
color="blue"
|
||||
v-if="clientCount[dbInbound.id].online.length">[[
|
||||
clientCount[dbInbound.id].online.length
|
||||
]]</a-tag>
|
||||
</a-popover>
|
||||
</template>
|
||||
</template>
|
||||
<template slot="traffic" slot-scope="text, dbInbound">
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<table cellpadding="2" width="100%">
|
||||
<tr>
|
||||
<td>↑[[ SizeFormatter.sizeFormat(dbInbound.up) ]]</td>
|
||||
<td>↓[[ SizeFormatter.sizeFormat(dbInbound.down) ]]</td>
|
||||
<td>↑[[ SizeFormatter.sizeFormat(dbInbound.up)
|
||||
]]</td>
|
||||
<td>↓[[ SizeFormatter.sizeFormat(dbInbound.down)
|
||||
]]</td>
|
||||
</tr>
|
||||
<tr v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
|
||||
<tr
|
||||
v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
|
||||
<td>{{ i18n "remained" }}</td>
|
||||
<td>[[ SizeFormatter.sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down) ]]</td>
|
||||
<td>[[ SizeFormatter.sizeFormat(dbInbound.total -
|
||||
dbInbound.up - dbInbound.down) ]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
<a-tag
|
||||
:color="ColorUtils.usageColor(dbInbound.up + dbInbound.down, app.trafficDiff, dbInbound.total)">
|
||||
[[ SizeFormatter.sizeFormat(dbInbound.up + dbInbound.down) ]] /
|
||||
[[ SizeFormatter.sizeFormat(dbInbound.up +
|
||||
dbInbound.down) ]] /
|
||||
<template v-if="dbInbound.total > 0">
|
||||
[[ SizeFormatter.sizeFormat(dbInbound.total) ]]
|
||||
</template>
|
||||
<template v-else>
|
||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||
<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>
|
||||
@@ -372,25 +471,30 @@
|
||||
</a-tag>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template slot="allTimeInbound" slot-scope="text, dbInbound">
|
||||
<a-tag>[[ SizeFormatter.sizeFormat(dbInbound.allTime || 0) ]]</a-tag>
|
||||
<template slot="allTimeInbound"
|
||||
slot-scope="text, dbInbound">
|
||||
<a-tag>[[ SizeFormatter.sizeFormat(dbInbound.allTime || 0)
|
||||
]]</a-tag>
|
||||
</template>
|
||||
<template slot="enable" slot-scope="text, dbInbound">
|
||||
<a-switch v-model="dbInbound.enable"
|
||||
@change="switchEnable(dbInbound.id,dbInbound.enable)"></a-switch>
|
||||
</template>
|
||||
<template slot="expiryTime" slot-scope="text, dbInbound">
|
||||
<a-popover v-if="dbInbound.expiryTime > 0" :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover v-if="dbInbound.expiryTime > 0"
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
[[ IntlUtil.formatDate(dbInbound.expiryTime) ]]
|
||||
</template>
|
||||
<a-tag :style="{ minWidth: '50px' }"
|
||||
:color="ColorUtils.usageColor(new Date().getTime(), app.expireDiff, dbInbound._expiryTime)">
|
||||
[[ IntlUtil.formatRelativeTime(dbInbound.expiryTime) ]]
|
||||
[[ IntlUtil.formatRelativeTime(dbInbound.expiryTime)
|
||||
]]
|
||||
</a-tag>
|
||||
</a-popover>
|
||||
<a-tag v-else color="purple" class="infinite-tag">
|
||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||
<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>
|
||||
@@ -398,21 +502,28 @@
|
||||
</a-tag>
|
||||
</template>
|
||||
<template slot="info" slot-scope="text, dbInbound">
|
||||
<a-popover placement="bottomRight" :overlay-class-name="themeSwitcher.currentTheme"
|
||||
<a-popover placement="bottomRight"
|
||||
:overlay-class-name="themeSwitcher.currentTheme"
|
||||
trigger="click">
|
||||
<template slot="content">
|
||||
<table cellpadding="2">
|
||||
<tr>
|
||||
<td>{{ i18n "pages.inbounds.protocol" }}</td>
|
||||
<td>
|
||||
<a-tag :style="{ margin: '0' }" color="purple">[[ dbInbound.protocol ]]</a-tag>
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
color="purple">[[ dbInbound.protocol
|
||||
]]</a-tag>
|
||||
<template
|
||||
v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
|
||||
<a-tag :style="{ margin: '0' }" color="blue">[[ dbInbound.toInbound().stream.network
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
color="blue">[[
|
||||
dbInbound.toInbound().stream.network
|
||||
]]</a-tag>
|
||||
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isTls"
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
v-if="dbInbound.toInbound().stream.isTls"
|
||||
color="green">tls</a-tag>
|
||||
<a-tag :style="{ margin: '0' }" v-if="dbInbound.toInbound().stream.isReality"
|
||||
<a-tag :style="{ margin: '0' }"
|
||||
v-if="dbInbound.toInbound().stream.isReality"
|
||||
color="green">reality</a-tag>
|
||||
</template>
|
||||
</td>
|
||||
@@ -424,111 +535,156 @@
|
||||
<tr v-if="clientCount[dbInbound.id]">
|
||||
<td>{{ i18n "clients" }}</td>
|
||||
<td>
|
||||
<a-tag :style="{ margin: '0' }" color="blue">[[ clientCount[dbInbound.id].clients
|
||||
<a-tag :style="{ margin: '0' }" color="blue">[[
|
||||
clientCount[dbInbound.id].clients
|
||||
]]</a-tag>
|
||||
<a-popover title='{{ i18n "disabled" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].deactive" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].deactive"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }"
|
||||
<a-tag
|
||||
:style="{ margin: '0', padding: '0 2px' }"
|
||||
v-if="clientCount[dbInbound.id].deactive.length">[[
|
||||
clientCount[dbInbound.id].deactive.length ]]</a-tag>
|
||||
clientCount[dbInbound.id].deactive.length
|
||||
]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depleted" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].depleted" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].depleted"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="red"
|
||||
<a-tag
|
||||
:style="{ margin: '0', padding: '0 2px' }"
|
||||
color="red"
|
||||
v-if="clientCount[dbInbound.id].depleted.length">[[
|
||||
clientCount[dbInbound.id].depleted.length ]]</a-tag>
|
||||
clientCount[dbInbound.id].depleted.length
|
||||
]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "depletingSoon" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].expiring" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].expiring"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="orange"
|
||||
<a-tag
|
||||
:style="{ margin: '0', padding: '0 2px' }"
|
||||
color="orange"
|
||||
v-if="clientCount[dbInbound.id].expiring.length">[[
|
||||
clientCount[dbInbound.id].expiring.length ]]</a-tag>
|
||||
clientCount[dbInbound.id].expiring.length
|
||||
]]</a-tag>
|
||||
</a-popover>
|
||||
<a-popover title='{{ i18n "online" }}' :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover title='{{ i18n "online" }}'
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<div v-for="clientEmail in clientCount[dbInbound.id].online" :key="clientEmail"
|
||||
<div
|
||||
v-for="clientEmail in clientCount[dbInbound.id].online"
|
||||
:key="clientEmail"
|
||||
class="client-popup-item">
|
||||
<span>[[ clientEmail ]]</span>
|
||||
<a-tooltip :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-tooltip
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template #title>
|
||||
[[ clientCount[dbInbound.id].comments.get(clientEmail) ]]
|
||||
[[
|
||||
clientCount[dbInbound.id].comments.get(clientEmail)
|
||||
]]
|
||||
</template>
|
||||
<a-icon type="message"
|
||||
v-if="clientCount[dbInbound.id].comments.get(clientEmail)"></a-icon>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<a-tag :style="{ margin: '0', padding: '0 2px' }" color="green"
|
||||
<a-tag
|
||||
:style="{ margin: '0', padding: '0 2px' }"
|
||||
color="green"
|
||||
v-if="clientCount[dbInbound.id].online.length">[[
|
||||
clientCount[dbInbound.id].online.length ]]</a-tag>
|
||||
clientCount[dbInbound.id].online.length
|
||||
]]</a-tag>
|
||||
</a-popover>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ i18n "pages.inbounds.traffic" }}</td>
|
||||
<td>
|
||||
<a-popover :overlay-class-name="themeSwitcher.currentTheme">
|
||||
<a-popover
|
||||
:overlay-class-name="themeSwitcher.currentTheme">
|
||||
<template slot="content">
|
||||
<table cellpadding="2" width="100%">
|
||||
<tr>
|
||||
<td>↑[[ SizeFormatter.sizeFormat(dbInbound.up) ]]</td>
|
||||
<td>↓[[ SizeFormatter.sizeFormat(dbInbound.down) ]]</td>
|
||||
<td>↑[[
|
||||
SizeFormatter.sizeFormat(dbInbound.up)
|
||||
]]</td>
|
||||
<td>↓[[
|
||||
SizeFormatter.sizeFormat(dbInbound.down)
|
||||
]]</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="dbInbound.total > 0 && dbInbound.up + dbInbound.down < dbInbound.total">
|
||||
<td>{{ i18n "remained" }}</td>
|
||||
<td>[[ SizeFormatter.sizeFormat(dbInbound.total - dbInbound.up - dbInbound.down)
|
||||
<td>[[
|
||||
SizeFormatter.sizeFormat(dbInbound.total
|
||||
- dbInbound.up - dbInbound.down)
|
||||
]]</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
<a-tag
|
||||
:color="ColorUtils.usageColor(dbInbound.up + dbInbound.down, app.trafficDiff, dbInbound.total)">
|
||||
[[ SizeFormatter.sizeFormat(dbInbound.up + dbInbound.down) ]] /
|
||||
[[ SizeFormatter.sizeFormat(dbInbound.up +
|
||||
dbInbound.down) ]] /
|
||||
<template v-if="dbInbound.total > 0">
|
||||
[[ SizeFormatter.sizeFormat(dbInbound.total) ]]
|
||||
[[
|
||||
SizeFormatter.sizeFormat(dbInbound.total)
|
||||
]]
|
||||
</template>
|
||||
<template v-else>
|
||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||
<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>
|
||||
@@ -541,12 +697,17 @@
|
||||
<tr>
|
||||
<td>{{ i18n "pages.inbounds.expireDate" }}</td>
|
||||
<td>
|
||||
<a-tag :style="{ minWidth: '50px', textAlign: 'center' }"
|
||||
v-if="dbInbound.expiryTime > 0" :color="dbInbound.isExpiry? 'red': 'blue'">
|
||||
[[ IntlUtil.formatDate(dbInbound.expiryTime) ]]
|
||||
<a-tag
|
||||
:style="{ minWidth: '50px', textAlign: 'center' }"
|
||||
v-if="dbInbound.expiryTime > 0"
|
||||
:color="dbInbound.isExpiry? 'red': 'blue'">
|
||||
[[ IntlUtil.formatDate(dbInbound.expiryTime)
|
||||
]]
|
||||
</a-tag>
|
||||
<a-tag v-else :style="{ textAlign: 'center' }" color="purple" class="infinite-tag">
|
||||
<svg height="10px" width="14px" viewBox="0 0 640 512" fill="currentColor">
|
||||
<a-tag v-else :style="{ textAlign: 'center' }"
|
||||
color="purple" 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>
|
||||
@@ -555,25 +716,32 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ i18n "pages.inbounds.periodicTrafficResetTitle" }}</td>
|
||||
<td>{{ i18n
|
||||
"pages.inbounds.periodicTrafficResetTitle"
|
||||
}}</td>
|
||||
<td>
|
||||
<a-tag color="blue">[[ dbInbound.trafficReset ]]</a-tag>
|
||||
<a-tag color="blue">[[ dbInbound.trafficReset
|
||||
]]</a-tag>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</template>
|
||||
<a-badge>
|
||||
<a-icon v-if="!dbInbound.enable" slot="count" type="pause-circle"
|
||||
<a-icon v-if="!dbInbound.enable" slot="count"
|
||||
type="pause-circle"
|
||||
:style="{ color: themeSwitcher.isDarkTheme ? '#2c3950' : '#bcbcbc' }"></a-icon>
|
||||
<a-button shape="round" size="small" :style="{ fontSize: '14px', padding: '0 10px' }">
|
||||
<a-button shape="round" size="small"
|
||||
:style="{ fontSize: '14px', padding: '0 10px' }">
|
||||
<a-icon type="info"></a-icon>
|
||||
</a-button>
|
||||
</a-badge>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template slot="expandedRowRender" slot-scope="record">
|
||||
<a-table :row-key="client => client.id" :columns="isMobile ? innerMobileColumns : innerColumns"
|
||||
:data-source="getInboundClients(record)" :pagination=pagination(getInboundClients(record))
|
||||
<a-table :row-key="client => client.id"
|
||||
:columns="isMobile ? innerMobileColumns : innerColumns"
|
||||
:data-source="getInboundClients(record)"
|
||||
:pagination=pagination(getInboundClients(record))
|
||||
:style="{ margin: `-10px ${isMobile ? '2px' : '22px'} -11px` }">
|
||||
{{template "component/aClientTable"}}
|
||||
</a-table>
|
||||
@@ -589,11 +757,15 @@
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
{{template "page/body_scripts" .}}
|
||||
<script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
||||
<script
|
||||
src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/js/model/reality_targets.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
||||
<script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
|
||||
<script
|
||||
src="{{ .base_path }}assets/js/model/reality_targets.js?{{ .cur_ver }}"></script>
|
||||
<script
|
||||
src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script>
|
||||
<script
|
||||
src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script>
|
||||
{{template "component/aSidebar" .}}
|
||||
{{template "component/aThemeSwitch" .}}
|
||||
{{template "component/aCustomStatistic" .}}
|
||||
@@ -1247,6 +1419,7 @@
|
||||
switch (protocol) {
|
||||
case Protocols.TROJAN: return client.password;
|
||||
case Protocols.SHADOWSOCKS: return client.email;
|
||||
case Protocols.HYSTERIA: return client.auth;
|
||||
default: return client.id;
|
||||
}
|
||||
},
|
||||
@@ -1322,19 +1495,25 @@
|
||||
|
||||
this.submit(`/panel/api/inbounds/update/${dbInboundId}`, formData);
|
||||
},
|
||||
async switchEnableClient(dbInboundId, client) {
|
||||
this.loading()
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
if (!dbInbound) return;
|
||||
inbound = dbInbound.toInbound();
|
||||
clients = inbound && inbound.clients ? inbound.clients : null;
|
||||
if (!clients || !Array.isArray(clients)) return;
|
||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||
if (index < 0 || !clients[index]) return;
|
||||
clients[index].enable = !clients[index].enable;
|
||||
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
||||
await this.updateClient(clients[index], dbInboundId, clientId);
|
||||
this.loading(false);
|
||||
async switchEnableClient(dbInboundId, client, state) {
|
||||
this.loading();
|
||||
try {
|
||||
dbInbound = this.dbInbounds.find(row => row.id === dbInboundId);
|
||||
if (!dbInbound) return;
|
||||
|
||||
inbound = dbInbound.toInbound();
|
||||
clients = inbound && inbound.clients ? inbound.clients : null;
|
||||
if (!clients || !Array.isArray(clients)) return;
|
||||
|
||||
index = this.findIndexOfClient(dbInbound.protocol, clients, client);
|
||||
if (index < 0 || !clients[index]) return;
|
||||
|
||||
clients[index].enable = typeof state === 'boolean' ? state : !!client.enable;
|
||||
clientId = this.getClientId(dbInbound.protocol, clients[index]);
|
||||
await this.updateClient(clients[index], dbInboundId, clientId);
|
||||
} finally {
|
||||
this.loading(false);
|
||||
}
|
||||
},
|
||||
async submit(url, data, modal) {
|
||||
const msg = await HttpUtil.postWithModal(url, data, modal);
|
||||
|
||||
@@ -1,52 +1,75 @@
|
||||
{{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-select-option v-for="key in USERS_SECURITY" :value="key">[[ key ]]</a-select-option>
|
||||
<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-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-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">[[
|
||||
key ]]</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="app.subSettings?.enable">
|
||||
<template slot="label">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
|
||||
<span>{{ i18n "pages.inbounds.subscriptionDesc"
|
||||
}}</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>
|
||||
@@ -61,7 +84,8 @@
|
||||
<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">
|
||||
@@ -73,7 +97,8 @@
|
||||
<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">
|
||||
@@ -85,29 +110,38 @@
|
||||
<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">
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
|
||||
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire"
|
||||
}}</span>
|
||||
</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"
|
||||
<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">
|
||||
@@ -120,7 +154,8 @@
|
||||
<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>
|
||||
@@ -214,6 +249,7 @@
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
{{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 is (Expired|Traffic Ended) And Disabled</a-tag>
|
||||
<a-tag v-if="isExpiry || isTrafficExhausted" color="red"
|
||||
:style="{ marginBottom: '10px', display: 'block', textAlign: 'center' }">Account
|
||||
is (Expired|Traffic Ended) And Disabled</a-tag>
|
||||
</template>
|
||||
{{template "form/client"}}
|
||||
</a-modal>
|
||||
@@ -56,6 +60,7 @@
|
||||
switch (protocol) {
|
||||
case Protocols.TROJAN: return client.password;
|
||||
case Protocols.SHADOWSOCKS: return client.email;
|
||||
case Protocols.HYSTERIA: return client.auth;
|
||||
default: return client.id;
|
||||
}
|
||||
},
|
||||
@@ -65,6 +70,7 @@
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user