<tr> <td> instead of <a-form-item>

convert the form structure to use <tr> and <td> instead of <a-form-item>,
This commit is contained in:
MHSanaei 2023-12-09 18:48:06 +03:30
parent 2088a4f815
commit 0f1f3d8439
15 changed files with 1285 additions and 733 deletions

View file

@ -1,134 +1,207 @@
{{define "clientsBulkModal"}} {{define "clientsBulkModal"}}
<a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title" @ok="clientsBulkModal.ok" <a-modal id="client-bulk-modal" v-model="clientsBulkModal.visible" :title="clientsBulkModal.title"
:confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false" @ok="clientsBulkModal.ok" :confirm-loading="clientsBulkModal.confirmLoading" :closable="true" :mask-closable="false"
:class="themeSwitcher.currentTheme" :ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}' :class="themeSwitcher.currentTheme">
:ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'>
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "pages.client.method" }}'> <table width="100%" class="ant-table-tbody">
<a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 350px" :dropdown-class-name="themeSwitcher.currentTheme"> <tr>
<a-select-option :value="0">Random</a-select-option> <td>{{ i18n "pages.client.method" }}</td>
<a-select-option :value="1">Random+Prefix</a-select-option> <td>
<a-select-option :value="2">Random+Prefix+Num</a-select-option> <a-form-item>
<a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option> <a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 250px"
<a-select-option :value="4">Prefix+Num+Postfix [ BE CAREFUL! ]</a-select-option> :dropdown-class-name="themeSwitcher.currentTheme">
</a-select> <a-select-option :value="0">Random</a-select-option>
</a-form-item><br /> <a-select-option :value="1">Random+Prefix</a-select-option>
<a-form-item v-if="clientsBulkModal.emailMethod>1"> <a-select-option :value="2">Random+Prefix+Num</a-select-option>
<span slot="label">{{ i18n "pages.client.first" }}</span> <a-select-option :value="3">Random+Prefix+Num+Postfix</a-select-option>
<a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number> <a-select-option :value="4">Prefix+Num+Postfix</a-select-option>
</a-form-item> </a-select>
<a-form-item v-if="clientsBulkModal.emailMethod>1"> </a-form-item>
<span slot="label">{{ i18n "pages.client.last" }}</span> </td>
<a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number> </tr>
</a-form-item> <tr v-if="clientsBulkModal.emailMethod>1">
<a-form-item v-if="clientsBulkModal.emailMethod>0"> <td>{{ i18n "pages.client.first" }}</td>
<span slot="label">{{ i18n "pages.client.prefix" }}</span> <td>
<a-input v-model="clientsBulkModal.emailPrefix" style="width: 120px"></a-input> <a-form-item>
</a-form-item> <a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
<a-form-item v-if="clientsBulkModal.emailMethod>2"> </a-form-item>
<span slot="label">{{ i18n "pages.client.postfix" }}</span> </td>
<a-input v-model="clientsBulkModal.emailPostfix" style="width: 120px"></a-input> </tr>
</a-form-item> <tr v-if="clientsBulkModal.emailMethod>1">
<a-form-item v-if="clientsBulkModal.emailMethod < 2"> <td>{{ i18n "pages.client.last" }}</td>
<span slot="label">{{ i18n "pages.client.clientCount" }}</span> <td>
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number> <a-form-item>
</a-form-item> <a-input-number v-model="clientsBulkModal.lastNum"
<a-form-item v-if="app.subSettings.enable"> :min="clientsBulkModal.firstNum"></a-input-number>
<span slot="label"> </a-form-item>
Subscription </td>
</tr>
<tr v-if="clientsBulkModal.emailMethod>0">
<td>{{ i18n "pages.client.prefix" }}</td>
<td>
<a-form-item>
<a-input v-model="clientsBulkModal.emailPrefix" style="width: 250px"></a-input>
</a-form-item>
</td>
</tr>
<tr v-if="clientsBulkModal.emailMethod>2">
<td>{{ i18n "pages.client.postfix" }}</td>
<td>
<a-form-item>
<a-input v-model="clientsBulkModal.emailPostfix" style="width: 250px"></a-input>
</a-form-item>
</td>
</tr>
<tr v-if="clientsBulkModal.emailMethod < 2">
<td>{{ i18n "pages.client.clientCount" }}</td>
<td>
<a-form-item>
<a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
</a-form-item>
</td>
</tr>
<tr v-if="clientsBulkModal.inbound.canEnableTlsFlow()">
<td>Flow</td>
<td>
<a-form-item>
<a-select v-model="clientsBulkModal.flow" style="width: 250px"
: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>
</td>
</tr>
<tr v-if="app.subSettings.enable">
<td>Subscription
<a-tooltip> <a-tooltip>
<template slot="title"> <template slot="title">
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span> <span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
</template> </template>
<a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"></a-icon>
</a-tooltip>
</td>
<td>
<a-form-item>
<a-input v-model.trim="clientsBulkModal.subId" style="width: 250px"></a-input>
</a-form-item>
</td>
</tr>
<tr v-if="app.tgBotEnable">
<td>Telegram ID
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon> <a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip> </a-tooltip>
</span> </td>
<a-input v-model.trim="clientsBulkModal.subId"></a-input> <td>
</a-form-item> <a-form-item>
<a-form-item v-if="app.tgBotEnable"> <a-input v-model.trim="clientsBulkModal.tgId" style="width: 250px"></a-input>
<span slot="label"> </a-form-item>
Telegram ID </td>
<a-tooltip> </tr>
<template slot="title"> <tr>
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span> <td>
</template> <label>
<a-icon type="question-circle" theme="filled"></a-icon> <span>{{ i18n "pages.inbounds.IPLimit" }}</span>
</a-tooltip> <a-tooltip>
</span> <template slot="title">
<a-input v-model.trim="clientsBulkModal.tgId"></a-input> <span>{{ i18n "pages.inbounds.IPLimitDesc" }}</span>
</a-form-item> </template>
<br> <a-icon type="question-circle" theme="filled"></a-icon>
<a-form-item> </a-tooltip>
<span slot="label"> </label>
<span>{{ i18n "pages.inbounds.IPLimit" }}</span> </td>
<a-tooltip> <td>
<template slot="title"> <a-form-item>
<span>{{ i18n "pages.inbounds.IPLimitDesc" }}</span> <a-input-number v-model="clientsBulkModal.limitIp" min="0"></a-input-number>
</template> </a-form-item>
<a-icon type="question-circle" theme="filled"></a-icon> </td>
</a-tooltip> </tr>
</span> <tr>
<a-input-number v-model="clientsBulkModal.limitIp" min="0"></a-input-number> <td v-if="clientsBulkModal.inbound.xtls">
</a-form-item> <label>Flow</label>
<br> </td>
<a-form-item v-if="clientsBulkModal.inbound.xtls" label="Flow"> <td v-if="clientsBulkModal.inbound.xtls">
<a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="themeSwitcher.currentTheme"> <a-form-item>
<a-select-option value="">{{ i18n "none" }}</a-select-option> <a-select v-model="clientsBulkModal.flow" style="width: 200px"
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option> :dropdown-class-name="themeSwitcher.currentTheme">
</a-select> <a-select-option value="">{{ i18n "none" }}</a-select-option>
</a-form-item> <a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
<a-form-item v-if="clientsBulkModal.inbound.canEnableTlsFlow()" label="Flow" layout="inline"> </a-select>
<a-select v-model="clientsBulkModal.flow" style="width: 200px" :dropdown-class-name="themeSwitcher.currentTheme"> </a-form-item>
<a-select-option value="" selected>{{ i18n "none" }}</a-select-option> </td>
<a-select-option v-for="key in TLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option> </tr>
</a-select> <tr>
</a-form-item> <td>
<a-form-item> <span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
<span slot="label"> <a-tooltip>
<span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB) <template slot="title">
<a-tooltip> 0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
<template slot="title"> </template>
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span> <a-icon type="question-circle" theme="filled"></a-icon>
</template> </a-tooltip>
<a-icon type="question-circle" theme="filled"></a-icon> </td>
</a-tooltip> <td>
</span> <a-form-item>
<a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number> <a-input-number v-model="clientsBulkModal.totalGB" :min="0"></a-input-number>
</a-form-item> </a-form-item>
<br> </td>
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'> </tr>
<a-switch v-model="clientsBulkModal.delayedStart" @click="clientsBulkModal.expiryTime=0"></a-switch> <tr>
</a-form-item> <td>{{ i18n "pages.client.delayedStart" }}</td>
<br> <td>
<a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="clientsBulkModal.delayedStart"> <a-form-item>
<a-input-number v-model="delayedExpireDays" :min="0"></a-input-number> <a-switch v-model="clientsBulkModal.delayedStart"
</a-form-item> @click="clientsBulkModal.expiryTime=0"></a-switch>
<a-form-item v-else> </a-form-item>
<span slot="label"> </td>
<span>{{ i18n "pages.inbounds.expireDate" }}</span> </tr>
<a-tooltip> <tr v-if="clientsBulkModal.delayedStart">
<template slot="title"> <td>{{ i18n "pages.client.expireDays" }}</td>
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span> <td>
</template> <a-form-item>
<a-icon type="question-circle" theme="filled"></a-icon> <a-input-number v-model.number="delayedExpireDays" :min="0"></a-input-number>
</a-tooltip> </a-form-item>
</span> </td>
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" </tr>
:dropdown-class-name="themeSwitcher.darkCardClass" <tr v-else>
v-model="clientsBulkModal.expiryTime" style="width: 300px;"></a-date-picker> <td>
</a-form-item> <span>{{ i18n "pages.inbounds.expireDate" }}</span>
<a-form-item v-if="clientsBulkModal.expiryTime != 0"> <a-tooltip>
<span slot="label"> <template slot="title">
<span>{{ i18n "pages.client.renew" }}</span> <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
<a-tooltip> </template>
<template slot="title"> <a-icon type="question-circle" theme="filled"></a-icon>
<span>{{ i18n "pages.client.renewDesc" }}</span> </a-tooltip>
</template> </td>
<a-icon type="question-circle" theme="filled"></a-icon> <td>
</a-tooltip> <a-form-item>
</span> <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
<a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number> :dropdown-class-name="themeSwitcher.currentTheme" v-model="clientsBulkModal.expiryTime"
</a-form-item> style="width: 250px;"></a-date-picker>
</a-form-item>
</td>
</tr>
<tr v-if="clientsBulkModal.expiryTime != 0">
<td>
<span>{{ i18n "pages.client.renew" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.client.renewDesc" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</td>
<td>
<a-form-item>
<a-input-number v-model.number="clientsBulkModal.reset" :min="0"></a-input-number>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
</a-modal> </a-modal>
<script> <script>

View file

@ -1,168 +1,223 @@
{{define "form/client"}} {{define "form/client"}}
<a-form layout="inline" v-if="client"> <a-form layout="inline" v-if="client">
<a-form-item label='{{ i18n "pages.inbounds.enable" }}'> <table width="100%" class="ant-table-tbody">
<a-switch v-model="client.enable"></a-switch> <tr>
</a-form-item> <td>{{ i18n "pages.inbounds.enable" }}</td>
<br> <td>
<a-form-item> <a-form-item>
<span slot="label"> <a-switch v-model="client.enable"></a-switch>
<span>{{ i18n "pages.inbounds.email" }}</span> </a-form-item>
<a-tooltip> </td>
<template slot="title"> </tr>
<span>{{ i18n "pages.inbounds.emailDesc" }}</span> <tr>
<td>
<span>{{ i18n "pages.inbounds.email" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.emailDesc" }}</span>
</template>
<a-icon type="sync" @click="client.email = RandomUtil.randomLowerAndNum(9)"></a-icon>
</a-tooltip>
</td>
<td>
<a-form-item>
<a-input v-model.trim="client.email" style="width: 250px"></a-input>
</a-form-item>
</td>
</tr>
<tr v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS">
<td>password
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
<a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)" type="sync"> </a-icon>
</td>
<td>
<a-form-item>
<a-input v-model.trim="client.password" style="width: 250px"></a-input>
</a-form-item>
</td>
</tr>
<tr v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
<td>ID <a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"></a-icon></td>
<td>
<a-input v-model.trim="client.id" style="width: 300px;"></a-input>
</td>
</tr>
<tr v-if="client.email && app.subSettings.enable">
<td>Subscription</td>
<td>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span>
</template>
<a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"> </a-icon>
</a-tooltip>
<a-form-item>
<a-input v-model.trim="client.subId" style="width: 150px;"></a-input>
</a-form-item>
</td>
</tr>
<tr v-if="client.email && app.tgBotEnable">
<td>Telegram ID</td>
<td>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
<a-form-item>
<a-input v-model.trim="client.tgId"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>
<span>{{ i18n "pages.inbounds.IPLimit" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.IPLimitDesc" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</td>
<td>
<a-form-item>
<a-input-number v-model="client.limitIp" min="0"></a-input-number>
</a-form-item>
</td>
</tr>
<tr v-if="client.email && client.limitIp > 0 && isEdit">
<td>
<span>{{ i18n "pages.inbounds.IPLimitlog" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.IPLimitlogDesc" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
</template>
<span style="color: #FF4D4F">
<a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
</span>
</a-tooltip>
</td>
<td>
<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>
</a-form>
</td>
</tr>
<tr v-if="inbound.xtls">
<td>Flow</td>
<td>
<a-form-item>
<a-select v-model="client.flow" style="width: 200px"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="">{{ i18n "none" }}</a-select-option>
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
</td>
</tr>
<tr v-else-if="inbound.canEnableTlsFlow()">
<td>Flow</td>
<td>
<a-form-item>
<a-select v-model="client.flow" style="width: 200px"
: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>
</td>
</tr>
<tr>
<td>
<span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
<a-tooltip>
<template slot="title">
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</td>
<td>
<a-form-item>
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
</a-form-item>
<template v-if="isEdit && clientStats">
<br>
<span> {{ i18n "usage" }}:</span>
<a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
[[ sizeFormat(clientStats.up) ]] /
[[ sizeFormat(clientStats.down) ]]
([[ sizeFormat(clientStats.up + clientStats.down) ]])
</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-tooltip>
</template> </template>
</a-tooltip> </td>
</span> </tr>
<a-icon @click="client.email = RandomUtil.randomLowerAndNum(8)" type="sync"> </a-icon> <tr>
<a-input v-model.trim="client.email" style="width: 200px;"></a-input> <td>{{ i18n "pages.client.delayedStart" }}</td>
</a-form-item> <td>
<a-form-item label="Password" v-if="inbound.protocol === Protocols.TROJAN || inbound.protocol === Protocols.SHADOWSOCKS"> <a-form-item>
<a-icon v-if="inbound.protocol === Protocols.SHADOWSOCKS" @click="client.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon> <a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
<a-icon v-if="inbound.protocol === Protocols.TROJAN" @click="client.password = RandomUtil.randomSeq(10)" type="sync"> </a-icon> </a-form-item>
<a-input v-model.trim="client.password" style="width: 300px;"></a-input> </td>
</a-form-item> </tr>
<br> <tr v-if="delayedStart">
<a-form-item label="ID" v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS"> <td>{{ i18n "pages.client.expireDays" }}</td>
<a-icon @click="client.id = RandomUtil.randomUUID()" type="sync"> </a-icon> <td>
<a-input v-model.trim="client.id" style="width: 300px;"></a-input> <a-form-item>
</a-form-item> <a-input-number v-model="delayedExpireDays" :min="0"></a-input-number>
<a-form-item v-if="client.email && app.subSettings.enable"> </a-form-item>
<span slot="label"> </td>
Subscription </tr>
<a-tooltip> <tr v-else>
<template slot="title"> <td>
<span>{{ i18n "pages.inbounds.subscriptionDesc" }}</span> <span>{{ i18n "pages.inbounds.expireDate" }}</span>
</template> <a-tooltip>
<a-icon type="question-circle" theme="filled"></a-icon> <template slot="title">
</a-tooltip> <span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
</span> </template>
<a-icon @click="client.subId = RandomUtil.randomLowerAndNum(16)" type="sync"> </a-icon> <a-icon type="question-circle" theme="filled"></a-icon>
<a-input v-model.trim="client.subId" style="width: 150px;"></a-input> </a-tooltip>
</a-form-item> </td>
<a-form-item v-if="client.email && app.tgBotEnable" > <td>
<span slot="label"> <a-form-item>
Telegram ID <a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
<a-tooltip> :dropdown-class-name="themeSwitcher.currentTheme" v-model="client._expiryTime"
<template slot="title"> style="width: 170px;"></a-date-picker>
<span>{{ i18n "pages.inbounds.telegramDesc" }}</span> <a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
</template> </a-form-item>
<a-icon type="question-circle" theme="filled"></a-icon> </td>
</a-tooltip> </tr>
</span> <tr v-if="client.expiryTime != 0">
<a-input v-model.trim="client.tgId"></a-input> <td>
</a-form-item> <span>{{ i18n "pages.client.renew" }}</span>
<a-form-item> <a-tooltip>
<span slot="label"> <template slot="title">
<span>{{ i18n "pages.inbounds.IPLimit" }}</span> <span>{{ i18n "pages.client.renewDesc" }}</span>
<a-tooltip> </template>
<template slot="title"> <a-icon type="question-circle" theme="filled"></a-icon>
<span>{{ i18n "pages.inbounds.IPLimitDesc" }}</span> </a-tooltip>
</template> </td>
<a-icon type="question-circle" theme="filled"></a-icon> <td>
</a-tooltip> <a-form-item>
</span> <a-input-number v-model.number="client.reset" :min="0"></a-input-number>
<a-input-number v-model="client.limitIp" min="0"></a-input-number> </a-form-item>
</a-form-item> </td>
<a-form-item v-if="client.email && client.limitIp > 0 && isEdit"> </tr>
<span slot="label"> </table>
<span>{{ i18n "pages.inbounds.IPLimitlog" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.IPLimitlogDesc" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.IPLimitlogclear" }}</span>
</template>
<span style="color: #FF4D4F">
<a-icon type="delete" @click="clearDBClientIps(client.email)"></a-icon>
</span>
</a-tooltip>
</span>
<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>
</a-form>
</a-form-item>
<br>
<a-form-item v-if="inbound.xtls" label="Flow">
<a-select v-model="client.flow" style="width: 200px" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="">{{ i18n "none" }}</a-select-option>
<a-select-option v-for="key in XTLS_FLOW_CONTROL" :value="key">[[ key ]]</a-select-option>
</a-select>
</a-form-item>
<a-form-item v-else-if="inbound.canEnableTlsFlow()" label="Flow">
<a-select v-model="client.flow" style="width: 200px" :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>
<span slot="label">
<span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB)
<a-tooltip>
<template slot="title">
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-input-number v-model="client._totalGB" :min="0"></a-input-number>
<template v-if="isEdit && clientStats">
<br>
<span> {{ i18n "usage" }}:</span>
<a-tag :color="clientUsageColor(clientStats, app.trafficDiff)">
[[ sizeFormat(clientStats.up) ]] /
[[ sizeFormat(clientStats.down) ]]
([[ sizeFormat(clientStats.up + clientStats.down) ]])
</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-tooltip>
</template>
</a-form-item>
<br>
<a-form-item label='{{ i18n "pages.client.delayedStart" }}'>
<a-switch v-model="delayedStart" @click="client._expiryTime=0"></a-switch>
</a-form-item>
<br>
<a-form-item label='{{ i18n "pages.client.expireDays" }}' v-if="delayedStart">
<a-input-number v-model="delayedExpireDays" :min="0"></a-input-number>
</a-form-item>
<a-form-item v-else>
<span slot="label">
<span>{{ i18n "pages.inbounds.expireDate" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme"
v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
<a-tag color="red" v-if="isEdit && isExpiry">Expired</a-tag>
</a-form-item>
<a-form-item v-if="client.expiryTime != 0">
<span slot="label">
<span>{{ i18n "pages.client.renew" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.client.renewDesc" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
<a-input-number v-model.number="client.reset" :min="0"></a-input-number>
</a-form-item>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,63 +1,94 @@
{{define "form/inbound"}} {{define "form/inbound"}}
<!-- base --> <!-- base -->
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "enable" }}'> <table width="100%" class="ant-table-tbody">
<a-switch v-model="dbInbound.enable"></a-switch> <tr>
</a-form-item> <td>{{ i18n "enable" }}</td>
<br> <td>
<a-form-item label='{{ i18n "remark" }}'> <a-form-item>
<a-input v-model.trim="dbInbound.remark"></a-input> <a-switch v-model="dbInbound.enable"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='{{ i18n "protocol" }}'> </td>
<a-select v-model="inbound.protocol" style="width: 160px;" :disabled="isEdit" :dropdown-class-name="themeSwitcher.currentTheme"> </tr>
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option> <tr>
</a-select> <td>{{ i18n "remark" }}</td>
</a-form-item> <td>
<a-form-item> <a-form-item>
<span slot="label"> <a-input v-model.trim="dbInbound.remark" style="width: 250px;"></a-input>
{{ i18n "monitor" }} </a-form-item>
<a-tooltip> </td>
</tr>
<tr>
<td>{{ i18n "protocol" }}</td>
<td>
<a-form-item>
<a-select v-model="inbound.protocol" style="width: 250px;" :disabled="isEdit" :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
</a-select>
</a-form-item>
</td>
</tr>
<tr>
<td>{{ i18n "monitor" }}
<a-tooltip>
<template slot="title"> <template slot="title">
<span>{{ i18n "pages.inbounds.monitorDesc" }}</span> <span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
</template> </template>
<a-icon type="question-circle" theme="filled"></a-icon> <a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip> </a-tooltip>
</span> </td>
<a-input v-model.trim="inbound.listen"></a-input> <td>
</a-form-item> <a-form-item>
<br> <a-input v-model.trim="inbound.listen" style="width: 250px;"></a-input>
<a-form-item label='{{ i18n "pages.inbounds.port" }}'> </a-form-item>
<a-input-number v-model="inbound.port"></a-input-number> </td>
</a-form-item> </tr>
<br> <tr>
<a-form-item> <td>{{ i18n "pages.inbounds.port" }}</td>
<span slot="label"> <td>
<span>{{ i18n "pages.inbounds.totalFlow" }}</span> (GB) <a-form-item>
<a-tooltip> <a-input-number v-model.number="inbound.port"></a-input-number>
<template slot="title"> </a-form-item>
0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span> </td>
</template> </tr>
<a-icon type="question-circle" theme="filled"></a-icon> <tr>
</a-tooltip> <td>
</span> <span>{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
<a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number> <a-tooltip>
</a-form-item> <template slot="title">
<a-form-item> 0 <span>{{ i18n "pages.inbounds.meansNoLimit" }}</span>
<span slot="label"> </template>
<span>{{ i18n "pages.inbounds.expireDate" }}</span> <a-icon type="question-circle" theme="filled"></a-icon>
<a-tooltip> </a-tooltip>
<template slot="title"> </td>
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span> <td>
</template> <a-form-item>
<a-icon type="question-circle" theme="filled"></a-icon> <a-input-number v-model="dbInbound.totalGB" :min="0"></a-input-number>
</a-tooltip> </a-form-item>
</span> </td>
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss" </tr>
:dropdown-class-name="themeSwitcher.currentTheme" <tr>
v-model="dbInbound._expiryTime" style="width: 250px;"></a-date-picker> <td>
</a-form-item> <span>{{ i18n "pages.inbounds.expireDate" }}</span>
<a-tooltip>
<template slot="title">
<span>{{ i18n "pages.inbounds.leaveBlankToNeverExpire" }}</span>
</template>
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</td>
<td>
<a-form-item>
<a-date-picker :show-time="{ format: 'HH:mm:ss' }" format="YYYY-MM-DD HH:mm:ss"
:dropdown-class-name="themeSwitcher.currentTheme"
v-model="dbInbound._expiryTime" style="width: 250px;"></a-date-picker>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
<!-- vmess settings --> <!-- vmess settings -->
<template v-if="inbound.protocol === Protocols.VMESS"> <template v-if="inbound.protocol === Protocols.VMESS">
{{template "form/vmess"}} {{template "form/vmess"}}

View file

@ -1,22 +1,42 @@
{{define "form/dokodemo"}} {{define "form/dokodemo"}}
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "pages.inbounds.targetAddress"}}'> <table width="100%" class="ant-table-tbody">
<a-input v-model.trim="inbound.settings.address"></a-input> <tr>
</a-form-item> <td>{{ i18n "pages.inbounds.targetAddress"}}</td>
<a-form-item label='{{ i18n "pages.inbounds.destinationPort"}}'> <td>
<a-input-number v-model="inbound.settings.port"></a-input-number> <a-form-item>
</a-form-item> <a-input v-model.trim="inbound.settings.address"></a-input>
<br> </a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.network"}}'> </td>
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme"> </tr>
<a-select-option value="tcp,udp">TCP+UDP</a-select-option> <tr>
<a-select-option value="tcp">TCP</a-select-option> <td>{{ i18n "pages.inbounds.destinationPort"}}</td>
<a-select-option value="udp">UDP</a-select-option> <td>
</a-select> <a-form-item>
</a-form-item> <a-input-number v-model.number="inbound.settings.port"></a-input-number>
<br> </a-form-item>
<a-form-item label="FollowRedirect"> </td>
<a-switch v-model="inbound.settings.followRedirect"></a-switch> </tr>
</a-form-item> <tr>
<td>{{ i18n "pages.inbounds.network"}}</td>
<td>
<a-form-item>
<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>
</a-select>
</a-form-item>
</td>
</tr>
<tr>
<td>FollowRedirect</td>
<td>
<a-form-item>
<a-switch v-model="inbound.settings.followRedirect"></a-switch>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,19 +1,21 @@
{{define "form/http"}} {{define "form/http"}}
<a-form layout="inline"> <a-form layout="inline">
<a-form-item> <table style="width: 100%; text-align: center; margin-bottom: 10px;">
<a-row> <tr>
<a-button type="primary" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button> <td width="45%">{{ i18n "username" }}</td>
</a-row> <td width="45%">{{ i18n "password" }}</td>
<a-input-group v-for="(account, index) in inbound.settings.accounts"> <td><a-button size="small" @click="inbound.settings.addAccount(new Inbound.HttpSettings.HttpAccount())">+</a-button></td>
<a-input style="width: 45%" v-model.trim="account.user" </tr>
addon-before='{{ i18n "username" }}'></a-input> </table>
<a-input style="width: 55%" v-model.trim="account.pass" <a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
addon-before='{{ i18n "password" }}'> <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" }}'>
<template slot="addonAfter"> <template slot="addonAfter">
<a-button type="primary" size="small" @click="inbound.settings.delAccount(index)">-</a-button> <a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
</template> </template>
</a-input> </a-input>
</a-input-group> </a-input-group>
</a-form-item>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,7 +1,7 @@
{{define "form/shadowsocks"}} {{define "form/shadowsocks"}}
<a-form layout="inline" style="padding: 10px 0px;"> <a-form layout="inline">
<template v-if="inbound.isSSMultiUser"> <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" }}'> <a-collapse-panel header='{{ i18n "pages.inbounds.client" }}'>
{{template "form/client"}} {{template "form/client"}}
</a-collapse-panel> </a-collapse-panel>
@ -20,24 +20,40 @@
</table> </table>
</a-collapse-panel> </a-collapse-panel>
</a-collapse> </a-collapse>
</template> </template>
</a-form> <table width="100%" class="ant-table-tbody">
<a-form layout="inline"> <tr>
<a-form-item label='{{ i18n "encryption" }}'> <td>{{ i18n "encryption" }}</td>
<a-select v-model="inbound.settings.method" style="width: 250px;" :dropdown-class-name="themeSwitcher.currentTheme" @change="SSMethodChange"> <td>
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option> <a-form-item>
</a-select> <a-select v-model="inbound.settings.method" style="width: 250px;" @change="SSMethodChange" :dropdown-class-name="themeSwitcher.currentTheme">
</a-form-item> <a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
<a-form-item v-if="inbound.isSS2022" label='{{ i18n "password" }}'> </a-select>
<a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon> </a-form-item>
<a-input v-model.trim="inbound.settings.password" style="width: 250px;"></a-input> </td>
</a-form-item> </tr>
<a-form-item label='{{ i18n "pages.inbounds.network" }}'> <tr v-if="inbound.isSS2022">
<a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="themeSwitcher.currentTheme"> <td>{{ i18n "password" }}
<a-select-option value="tcp,udp">TCP+UDP</a-select-option> <a-icon @click="inbound.settings.password = RandomUtil.randomShadowsocksPassword()" type="sync"> </a-icon>
<a-select-option value="tcp">TCP</a-select-option> </td>
<a-select-option value="udp">UDP</a-select-option> <td>
</a-select> <a-form-item>
</a-form-item> <a-input v-model.trim="inbound.settings.password" style="width: 250px"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>{{ i18n "pages.inbounds.network" }}</td>
<td>
<a-form-item>
<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>
</a-select>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,33 +1,52 @@
{{define "form/socks"}} {{define "form/socks"}}
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "password" }}'> <table width="100%" class="ant-table-tbody">
<a-switch :checked="inbound.settings.auth === 'password'" <tr>
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch> <td style="width: 30%;">{{ i18n "password" }}</td>
</a-form-item> <td>
<br> <a-form-item>
<template v-if="inbound.settings.auth === 'password'"> <a-switch :checked="inbound.settings.auth === 'password'"
<a-form-item> @change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
<a-row> </a-form-item>
<a-button type="primary" size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button> </td>
</a-row> </tr>
<a-input-group v-for="(account, index) in inbound.settings.accounts"> <tr v-if="inbound.settings.auth === 'password'">
<a-input style="width: 45%" v-model.trim="account.user" <td colspan="2">
addon-before='{{ i18n "username" }}'></a-input> <table style="width: 100%; text-align: center; margin-bottom: 10px;">
<a-input style="width: 55%" v-model.trim="account.pass" <tr>
addon-before='{{ i18n "password" }}'> <td width="45%">{{ i18n "username" }}</td>
<template slot="addonAfter"> <td width="45%">{{ i18n "password" }}</td>
<a-button type="primary" size="small" @click="inbound.settings.delAccount(index)">-</a-button> <td><a-button size="small" @click="inbound.settings.addAccount(new Inbound.SocksSettings.SocksAccount())">+</a-button></td>
</template> </tr>
</a-input> </table>
</a-input-group> <a-input-group compact v-for="(account, index) in inbound.settings.accounts" style="margin-bottom: 10px;">
</a-form-item> <a-input style="width: 50%" v-model.trim="account.user" placeholder='{{ i18n "username" }}'>
</template> <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
<br> </a-input>
<a-form-item label='{{ i18n "pages.inbounds.enable" }} udp'> <a-input style="width: 50%" v-model.trim="account.pass" placeholder='{{ i18n "password" }}'>
<a-switch v-model="inbound.settings.udp"></a-switch> <template slot="addonAfter">
</a-form-item> <a-button size="small" @click="inbound.settings.delAccount(index)">-</a-button>
<a-form-item v-if="inbound.settings.udp" label="IP"> </template>
<a-input v-model.trim="inbound.settings.ip"></a-input> </a-input>
</a-form-item> </a-input-group>
</td>
</tr>
<tr>
<td>{{ i18n "pages.inbounds.enable" }} udp</td>
<td>
<a-form-item>
<a-switch v-model="inbound.settings.udp"></a-switch>
</a-form-item>
</td>
</tr>
<tr v-if="inbound.settings.udp">
<td>IP</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.settings.ip"></a-input>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,10 +1,22 @@
{{define "form/streamGRPC"}} {{define "form/streamGRPC"}}
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label="ServiceName"> <table width="100%" class="ant-table-tbody">
<a-input v-model.trim="inbound.stream.grpc.serviceName"></a-input> <tr>
</a-form-item> <td>serviceName</td>
<a-form-item label="Multi Mode"> <td>
<a-switch v-model="inbound.stream.grpc.multiMode"></a-switch> <a-form-item>
</a-form-item> <a-input v-model.trim="inbound.stream.grpc.serviceName" style="width: 250px;"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>MultiMode</td>
<td>
<a-form-item>
<a-switch v-model="inbound.stream.grpc.multiMode"></a-switch>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,12 +1,24 @@
{{define "form/streamHTTP"}} {{define "form/streamHTTP"}}
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "path" }}'> <table width="100%" class="ant-table-tbody">
<a-input v-model.trim="inbound.stream.http.path"></a-input> <tr>
</a-form-item> <td>{{ i18n "path" }}</td>
<a-form-item label="Host"> <td>
<a-row v-for="(host, index) in inbound.stream.http.host"> <a-form-item>
<a-input v-model.trim="inbound.stream.http.host[index]"></a-input> <a-input v-model.trim="inbound.stream.http.path" style="width: 250px;"></a-input>
</a-row> </a-form-item>
</a-form-item> </td>
</tr>
<tr>
<td>host</td>
<td>
<a-form-item>
<a-row v-for="(host, index) in inbound.stream.http.host">
<a-input v-model.trim="inbound.stream.http.host[index]" style="width: 250px;"></a-input>
</a-row>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,47 +1,86 @@
{{define "form/streamKCP"}} {{define "form/streamKCP"}}
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "camouflage" }}'> <table width="100%" class="ant-table-tbody">
<a-select v-model="inbound.stream.kcp.type" style="width: 280px;" :dropdown-class-name="themeSwitcher.currentTheme"> <tr>
<a-select-option value="none">None (Not Camouflage)</a-select-option> <td>{{ i18n "camouflage" }}</td>
<a-select-option value="srtp">SRTP (Camouflage Video Call)</a-select-option> <td>
<a-select-option value="utp">UTP (Camouflage BT Download)</a-select-option> <a-form-item>
<a-select-option value="wechat-video">Wechat-Video (Camouflage WeChat Video)</a-select-option> <a-select v-model="inbound.stream.kcp.type" style="width: 250px;"
<a-select-option value="dtls">DTLS (Camouflage DTLS 1.2 Packages)</a-select-option> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="wireguard">Wireguard (Camouflage Wireguard Packages)</a-select-option> <a-select-option value="none">None (Not Camouflage)</a-select-option>
</a-select> <a-select-option value="srtp">SRTP (Camouflage Video Call)</a-select-option>
</a-form-item> <a-select-option value="utp">UTP (Camouflage BT Download)</a-select-option>
<br> <a-select-option value="wechat-video">Wechat-Video (Camouflage WeChat Video)</a-select-option>
<a-form-item label='{{ i18n "password" }}'> <a-select-option value="dtls">DTLS (Camouflage DTLS 1.2 Packages)</a-select-option>
<a-icon @click="inbound.stream.kcp.seed = RandomUtil.randomSeq(10)" type="sync"> </a-icon> <a-select-option value="wireguard">Wireguard (Camouflage Wireguard Packages)</a-select-option>
<a-input v-model="inbound.stream.kcp.seed" style="width: 150px;" ></a-input> </a-select>
</a-form-item> </a-form-item>
<br> </td>
<a-form-item label="MTU"> </tr>
<a-input-number v-model="inbound.stream.kcp.mtu"></a-input-number> <tr>
</a-form-item> <td>{{ i18n "password" }}</td>
<br> <td>
<a-form-item label="TTI (ms)"> <a-form-item>
<a-input-number v-model="inbound.stream.kcp.tti"></a-input-number> <a-input v-model="inbound.stream.kcp.seed" style="width: 250px;"></a-input>
</a-form-item> </a-form-item>
<br> </td>
<a-form-item label="Uplink Capacity (MB/S)"> </tr>
<a-input-number v-model="inbound.stream.kcp.upCap"></a-input-number> <tr>
</a-form-item> <td>MTU</td>
<br> <td>
<a-form-item label="Downlink Capacity (MB/S)"> <a-form-item>
<a-input-number v-model="inbound.stream.kcp.downCap"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.mtu"></a-input-number>
</a-form-item> </a-form-item>
<br> </td>
<a-form-item label="Congestion"> </tr>
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch> <tr>
</a-form-item> <td>TTI (ms)</td>
<br> <td>
<a-form-item label="Read Buffer Size (MB)"> <a-form-item>
<a-input-number v-model="inbound.stream.kcp.readBuffer"></a-input-number> <a-input-number v-model.number="inbound.stream.kcp.tti"></a-input-number>
</a-form-item> </a-form-item>
<br> </td>
<a-form-item label="Write Buffer Size (MB)"> </tr>
<a-input-number v-model="inbound.stream.kcp.writeBuffer"></a-input-number> <tr>
</a-form-item> <td>Uplink Capacity (MB/S)</td>
<td>
<a-form-item>
<a-input-number v-model.number="inbound.stream.kcp.upCap"></a-input-number>
</a-form-item>
</td>
</tr>
<tr>
<td>Downlink Capacity (MB/S)</td>
<td>
<a-form-item>
<a-input-number v-model.number="inbound.stream.kcp.downCap"></a-input-number>
</a-form-item>
</td>
</tr>
<tr>
<td>Congestion</td>
<td>
<a-form-item>
<a-switch v-model="inbound.stream.kcp.congestion"></a-switch>
</a-form-item>
</td>
</tr>
<tr>
<td>Read Buffer Size (MB)</td>
<td>
<a-form-item>
<a-input-number v-model.number="inbound.stream.kcp.readBuffer"></a-input-number>
</a-form-item>
</td>
</tr>
<tr>
<td>Write Buffer Size (MB)</td>
<td>
<a-form-item>
<a-input-number v-model.number="inbound.stream.kcp.writeBuffer"></a-input-number>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -1,25 +1,43 @@
{{define "form/streamQUIC"}} {{define "form/streamQUIC"}}
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "pages.inbounds.stream.quic.encryption" }}'> <table width="100%" class="ant-table-tbody">
<a-select v-model="inbound.stream.quic.security" style="width: 165px;" :dropdown-class-name="themeSwitcher.currentTheme"> <tr>
<a-select-option value="none">none</a-select-option> <td>{{ i18n "pages.inbounds.stream.quic.encryption" }}</td>
<a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option> <td>
<a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option> <a-form-item>
</a-select> <a-select v-model="inbound.stream.quic.security" style="width: 250px;"
</a-form-item> :dropdown-class-name="themeSwitcher.currentTheme">
<a-form-item label='{{ i18n "password" }}'> <a-select-option value="none">none</a-select-option>
<a-icon @click="inbound.stream.quic.key = RandomUtil.randomSeq(10)" type="sync"> </a-icon> <a-select-option value="aes-128-gcm">aes-128-gcm</a-select-option>
<a-input v-model.trim="inbound.stream.quic.key" style="width: 150px;"></a-input> <a-select-option value="chacha20-poly1305">chacha20-poly1305</a-select-option>
</a-form-item> </a-select>
<a-form-item label='{{ i18n "camouflage" }}'> </a-form-item>
<a-select v-model="inbound.stream.quic.type" style="width: 280px;" :dropdown-class-name="themeSwitcher.currentTheme"> </td>
<a-select-option value="none">none (not camouflage)</a-select-option> </tr>
<a-select-option value="srtp">srtp (camouflage video call)</a-select-option> <tr>
<a-select-option value="utp">utp (camouflage BT download)</a-select-option> <td>{{ i18n "password" }}</td>
<a-select-option value="wechat-video">wechat-video (camouflage WeChat video)</a-select-option> <td>
<a-select-option value="dtls">dtls (camouflage DTLS 1.2 packages)</a-select-option> <a-form-item>
<a-select-option value="wireguard">wireguard (camouflage wireguard packages)</a-select-option> <a-input v-model.trim="inbound.stream.quic.key" style="width: 250px;"></a-input>
</a-select> </a-form-item>
</a-form-item> </td>
</tr>
<tr>
<td>{{ i18n "camouflage" }}</td>
<td>
<a-form-item>
<a-select v-model="inbound.stream.quic.type" style="width: 280px;"
:dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option value="none">none (not camouflage)</a-select-option>
<a-select-option value="srtp">srtp (camouflage video call)</a-select-option>
<a-select-option value="utp">utp (camouflage BT download)</a-select-option>
<a-select-option value="wechat-video">wechat-video (camouflage WeChat video)</a-select-option>
<a-select-option value="dtls">dtls (camouflage DTLS 1.2 packages)</a-select-option>
<a-select-option value="wireguard">wireguard (camouflage wireguard packages)</a-select-option>
</a-select>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -2,7 +2,8 @@
<!-- select stream network --> <!-- select stream network -->
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label='{{ i18n "transmission" }}'> <a-form-item label='{{ i18n "transmission" }}'>
<a-select v-model="inbound.stream.network" @change="streamNetworkChange" :dropdown-class-name="themeSwitcher.currentTheme" style="width: 100px;"> <a-select v-model="inbound.stream.network" @change="streamNetworkChange"
:dropdown-class-name="themeSwitcher.currentTheme" style="width: 100px;">
<a-select-option value="tcp">TCP</a-select-option> <a-select-option value="tcp">TCP</a-select-option>
<a-select-option value="kcp">KCP</a-select-option> <a-select-option value="kcp">KCP</a-select-option>
<a-select-option value="ws">WS</a-select-option> <a-select-option value="ws">WS</a-select-option>

View file

@ -1,7 +1,7 @@
{{define "form/streamTCP"}} {{define "form/streamTCP"}}
<!-- tcp type --> <!-- tcp type -->
<a-form layout="inline"> <a-form layout="inline">
<a-form-item label="AcceptProxyProtocol"> <a-form-item label="Accept Proxy Protocol" v-if="inbound.canEnableTls()">
<a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch> <a-switch v-model="inbound.stream.tcp.acceptProxyProtocol"></a-switch>
</a-form-item> </a-form-item>
<a-form-item label='HTTP {{ i18n "camouflage" }}'> <a-form-item label='HTTP {{ i18n "camouflage" }}'>
@ -14,68 +14,100 @@
<!-- tcp request --> <!-- tcp request -->
<a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline"> <a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline">
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}'> <table width="100%" class="ant-table-tbody">
<a-input v-model.trim="inbound.stream.tcp.request.version"></a-input> <tr>
</a-form-item> <td>{{ i18n "pages.inbounds.stream.tcp.requestVersion" }}</td>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}'> <td>
<a-input v-model.trim="inbound.stream.tcp.request.method"></a-input> <a-form-item>
</a-form-item> <a-input v-model.trim="inbound.stream.tcp.request.version" style="width: 200px;"></a-input>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.requestPath" }}'> </a-form-item>
<a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button> </td>
<a-row v-for="(path, index) in inbound.stream.tcp.request.path"> </tr>
<a-input v-model.trim="inbound.stream.tcp.request.path[index]" style="width: 200px;"> <tr>
<a-button size="small" slot="addonAfter" <td>{{ i18n "pages.inbounds.stream.tcp.requestMethod" }}</td>
@click="inbound.stream.tcp.request.removePath(index)" <td>
v-if="inbound.stream.tcp.request.path.length>1">-</a-button> <a-form-item>
</a-input> <a-input v-model.trim="inbound.stream.tcp.request.method" style="width: 200px;"></a-input>
</a-row> </a-form-item>
</a-form-item> </td>
<br> </tr>
<a-form-item> <tr>
<a-row> <td style="vertical-align: top; padding-top: 10px;">{{ i18n "pages.inbounds.stream.tcp.requestPath" }}
<span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span> <a-button size="small" @click="inbound.stream.tcp.request.addPath('/')">+</a-button>
<a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.request.addHeader('Host', 'xxx.com')">+</a-button> </td>
</a-row> <td>
<a-input-group v-for="(header, index) in inbound.stream.tcp.request.headers"> <a-form-item>
<a-input style="width: 50%" v-model.trim="header.name" <a-row v-for="(path, index) in inbound.stream.tcp.request.path">
addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input> <a-input v-model.trim="inbound.stream.tcp.request.path[index]" style="width: 200px;">
<a-input style="width: 50%" v-model.trim="header.value" <a-button size="small" slot="addonAfter"
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'> @click="inbound.stream.tcp.request.removePath(index)"
<template slot="addonAfter"> v-if="inbound.stream.tcp.request.path.length>1">-</a-button>
<a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button> </a-input>
</template> </a-row>
</a-input> </a-form-item>
</a-input-group> </td>
</a-form-item> </tr>
<tr>
</a-form> <td colspan="2" width="100%">
<a-form-item>
<!-- tcp response --> <span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span>
<a-form v-if="inbound.stream.tcp.type === 'http'" layout="inline"> <a-button size="small" style="margin-left: 10px" @click="inbound.stream.tcp.request.addHeader('', '')">+</a-button>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}'> <a-input-group compact v-for="(header, index) in inbound.stream.tcp.request.headers">
<a-input v-model.trim="inbound.stream.tcp.response.version"></a-input> <a-input style="width: 50%" v-model.trim="header.name" placeholder='{{ i18n "pages.inbounds.stream.general.name" }}'>
</a-form-item> <template slot="addonBefore" style="margin: 0;">[[ index+1 ]]</template>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}'> </a-input>
<a-input v-model.trim="inbound.stream.tcp.response.status"></a-input> <a-input style="width: 50%" v-model.trim="header.value" placeholder='{{ i18n "pages.inbounds.stream.general.value" }}'>
</a-form-item> <a-button slot="addonAfter" size="small" @click="inbound.stream.tcp.request.removeHeader(index)">-</a-button>
<a-form-item label='{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}'> </a-input>
<a-input v-model.trim="inbound.stream.tcp.response.reason"></a-input> </a-input-group>
</a-form-item> </a-form-item>
<a-form-item> </td>
<a-row> </tr>
<span>{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}:</span> <!-- tcp response -->
<a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">+</a-button> <tr>
</a-row> <td>{{ i18n "pages.inbounds.stream.tcp.responseVersion" }}</td>
<a-input-group v-for="(header, index) in inbound.stream.tcp.response.headers"> <td>
<a-input style="width: 50%" v-model.trim="header.name" <a-form-item>
addon-before='{{ i18n "pages.inbounds.stream.general.name" }}'></a-input> <a-input v-model.trim="inbound.stream.tcp.response.version" style="width: 200px;"></a-input>
<a-input style="width: 50%" v-model.trim="header.value" </a-form-item>
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'> </td>
<template slot="addonAfter"> </tr>
<a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button> <tr>
</template> <td>{{ i18n "pages.inbounds.stream.tcp.responseStatus" }}</td>
</a-input> <td>
</a-input-group> <a-form-item>
</a-form-item> <a-input v-model.trim="inbound.stream.tcp.response.status" style="width: 200px;"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>{{ i18n "pages.inbounds.stream.tcp.responseStatusDescription" }}</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.stream.tcp.response.reason" style="width: 200px;"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td colspan="2" width="100%">
<a-form-item>
<span>{{ i18n "pages.inbounds.stream.tcp.responseHeader" }}:</span>
<a-button size="small" style="margin-left: 10px"
@click="inbound.stream.tcp.response.addHeader('Content-Type', 'application/octet-stream')">+</a-button>
<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" }}'>
<template slot="addonAfter">
<a-button size="small" @click="inbound.stream.tcp.response.removeHeader(index)">-</a-button>
</template>
</a-input>
</a-input-group>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}

View file

@ -11,15 +11,17 @@
<a-form-item> <a-form-item>
<a-row> <a-row>
<span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span> <span>{{ i18n "pages.inbounds.stream.general.requestHeader" }}:</span>
<a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.ws.addHeader('Host', '')">+</a-button> <a-button type="primary" size="small" style="margin-left: 10px"
@click="inbound.stream.ws.addHeader('Host', '')">+</a-button>
</a-row> </a-row>
<a-input-group v-for="(header, index) in inbound.stream.ws.headers"> <a-input-group v-for="(header, index) in inbound.stream.ws.headers">
<a-input style="width: 50%" v-model.trim="header.name" <a-input style="width: 50%" v-model.trim="header.name"
addon-before='{{ i18n "pages.inbounds.stream.general.name"}}'></a-input> addon-before='{{ i18n "pages.inbounds.stream.general.name"}}'></a-input>
<a-input style="width: 50%" v-model.trim="header.value" <a-input style="width: 50%" v-model.trim="header.value"
addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'> addon-before='{{ i18n "pages.inbounds.stream.general.value" }}'>
<template slot="addonAfter"> <template slot="addonAfter">
<a-button type="primary" size="small" style="margin-left: 10px" @click="inbound.stream.ws.removeHeader(index)">-</a-button> <a-button type="primary" size="small" style="margin-left: 10px"
@click="inbound.stream.ws.removeHeader(index)">-</a-button>
</template> </template>
</a-input> </a-input>
</a-input-group> </a-input-group>

View file

@ -2,187 +2,407 @@
<!-- tls enable --> <!-- tls enable -->
<a-form layout="inline" v-if="inbound.canEnableTls()"> <a-form layout="inline" v-if="inbound.canEnableTls()">
<a-divider style="margin:0;"></a-divider> <a-divider style="margin:0;"></a-divider>
<a-form-item label='{{ i18n "security" }}'> <table width="100%" class="ant-table-tbody">
<a-radio-group v-model="inbound.stream.security" button-style="solid"> <tr>
<a-radio-button value="none">{{ i18n "none" }}</a-radio-button> <td>
<a-tooltip> <span>{{ i18n "security" }}</span>
<template slot="title"> </td>
<span>{{ i18n "pages.inbounds.xtlsDesc" }}</span> <td>
</template> <a-radio-group v-model="inbound.stream.security" button-style="solid">
<a-radio-button v-if="inbound.canEnableXtls()" value="xtls">XTLS</a-radio-button> <a-radio-button value="none">{{ i18n "none" }}</a-radio-button>
</a-tooltip> <a-tooltip>
<a-tooltip> <template slot="title">
<template slot="title"> <span>{{ i18n "pages.inbounds.xtlsDesc" }}</span>
<span>{{ i18n "pages.inbounds.realityDesc" }}</span> </template>
</template> <a-radio-button v-if="inbound.canEnableXtls()" value="xtls">XTLS</a-radio-button>
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button> </a-tooltip>
</a-tooltip> <a-tooltip>
<a-radio-button value="tls">TLS</a-radio-button> <template slot="title">
</a-radio-group> <span>{{ i18n "pages.inbounds.realityDesc" }}</span>
</a-form-item> </template>
<a-radio-button v-if="inbound.canEnableReality()" value="reality">Reality</a-radio-button>
</a-tooltip>
<a-radio-button value="tls">TLS</a-radio-button>
</a-radio-group>
</td>
</tr>
</table>
</a-form> </a-form>
<!-- tls settings --> <!-- tls settings -->
<a-form v-if="inbound.tls" layout="inline"> <a-form v-if="inbound.tls" layout="inline">
<a-form-item label="CipherSuites"> <table width="100%" class="ant-table-tbody">
<a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px" :dropdown-class-name="themeSwitcher.currentTheme"> <tr>
<a-select-option value="">auto</a-select-option> <td>
<a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value ]]</a-select-option> <span>CipherSuites</span>
</a-select> </td>
</a-form-item> <td>
<a-form-item label="Min/Max Version"> <a-form-item>
<a-input-group compact> <a-select v-model="inbound.stream.tls.cipherSuites" style="width: 300px"
<a-select style="width: 60px" v-model="inbound.stream.tls.minVersion" :dropdown-class-name="themeSwitcher.currentTheme"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option> <a-select-option value="">auto</a-select-option>
</a-select> <a-select-option v-for="key,value in TLS_CIPHER_OPTION" :value="key">[[ value
<a-select style="width: 60px" v-model="inbound.stream.tls.maxVersion" :dropdown-class-name="themeSwitcher.currentTheme"> ]]</a-select-option>
<a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option> </a-select>
</a-select> </a-form-item>
</a-input-group> </td>
</a-form-item> </tr>
<a-form-item label="SNI" placeholder="Server Name Indication"> <tr>
<a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input> <td>
</a-form-item> <span>Min/Max Version</span>
<a-form-item label="uTLS"> </td>
<a-select v-model="inbound.stream.tls.settings.fingerprint" <td>
style="width: 170px" :dropdown-class-name="themeSwitcher.currentTheme"> <a-form-item>
<a-select-option value=''>None</a-select-option> <a-input-group compact>
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> <a-select style="width: 60px" v-model="inbound.stream.tls.minVersion"
</a-select> :dropdown-class-name="themeSwitcher.currentTheme">
</a-form-item> <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
<a-form-item label="Alpn"> </a-select>
<a-select <a-select style="width: 60px" v-model="inbound.stream.tls.maxVersion"
mode="multiple" :dropdown-class-name="themeSwitcher.currentTheme">
style="width: 250px" <a-select-option v-for="key in TLS_VERSION_OPTION" :value="key">[[ key ]]</a-select-option>
:dropdown-class-name="themeSwitcher.currentTheme" </a-select>
v-model="inbound.stream.tls.alpn"> </a-input-group>
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option> </a-form-item>
</a-select> </td>
</a-form-item> </tr>
<br> <tr>
<a-form-item label="Allow insecure"> <td>
<a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch> <span>SNI</span>
</a-form-item> </td>
<br> <td>
<a-form-item label="Reject Unknown SNI"> <a-form-item>
<a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch> <a-input v-model.trim="inbound.stream.tls.server" style="width: 250px"></a-input>
</a-form-item> </a-form-item>
<template v-for="cert,index in inbound.stream.tls.certs"> </td>
<a-form-item label='{{ i18n "certificate" }}'> </tr>
<a-radio-group v-model="cert.useFile" button-style="solid"> <tr>
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button> <td>
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button> <span>uTLS</span>
</a-radio-group> </td>
<a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()" style="margin-left: 10px">+</a-button> <td>
<a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small" @click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button> <a-form-item>
</a-form-item> <a-select v-model="inbound.stream.tls.settings.fingerprint" style="width: 170px"
<template v-if="cert.useFile"> :dropdown-class-name="themeSwitcher.currentTheme">
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'> <a-select-option value=''>None</a-select-option>
<a-input v-model.trim="cert.certFile" style="width:300px;"></a-input> <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
</a-form-item> </a-select>
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'> </a-form-item>
<a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input> </td>
</a-form-item> </tr>
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button> <tr>
</template> <td>
<template v-else> <span>Alpn</span>
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'> </td>
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input> <td>
</a-form-item> <a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'> <a-select mode="multiple" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme"
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input> v-model="inbound.stream.tls.alpn">
</a-form-item> <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
</template> </a-select>
<br> </a-form-item>
<a-form-item label="ocspStapling"> </td>
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number> </tr>
</a-form-item> <tr>
</template> <td>
<span>Allow insecure</span>
</td>
<td>
<a-form-item>
<a-switch v-model="inbound.stream.tls.settings.allowInsecure"></a-switch>
</a-form-item>
</td>
</tr>
<tr>
<td>
<span>Reject Unknown SNI</span>
</td>
<td>
<a-form-item>
<a-switch v-model="inbound.stream.tls.rejectUnknownSni"></a-switch>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.tls.certs">
<td>
<span>{{ i18n "certificate" }}</span>
</td>
<td>
<a-form-item>
<a-radio-group v-model="cert.useFile" button-style="solid">
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
</a-radio-group>
<a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.tls.addCert()"
style="margin-left: 10px">+</a-button>
<a-button v-if="inbound.stream.tls.certs.length>1" type="primary" size="small"
@click="inbound.stream.tls.removeCert(index)" style="margin-left: 10px">-</a-button>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.tls.certs" v-if="cert.useFile">
<td>
<span>{{ i18n "pages.inbounds.publicKeyPath" }}</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="cert.certFile" style="width:300px;"></a-input>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.tls.certs" v-if="cert.useFile">
<td>
<span>{{ i18n "pages.inbounds.keyPath" }}</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.tls.certs" v-if="cert.useFile">
<td>
<a-button type="primary" icon="import" @click="setDefaultCertData(index)">{{ i18n
"pages.inbounds.setDefaultCert" }}</a-button>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.tls.certs" v-else>
<td>
<span>{{ i18n "pages.inbounds.publicKeyContent" }}</span>
</td>
<td>
<a-form-item>
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.tls.certs" v-else>
<td>
<span>{{ i18n "pages.inbounds.keyContent" }}</span>
</td>
<td>
<a-form-item>
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.tls.certs">
<td>
<span>ocspStapling</span>
</td>
<td>
<a-form-item>
<a-input-number v-model.number="cert.ocspStapling" :min="0"></a-input-number>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
<!-- xtls settings --> <!-- xtls settings -->
<a-form v-else-if="inbound.xtls" layout="inline"> <a-form v-else-if="inbound.xtls" layout="inline">
<a-form-item label="SNI" placeholder="Server Name Indication"> <table width="100%" class="ant-table-tbody">
<a-input v-model.trim="inbound.stream.xtls.server" style="width: 250px"></a-input> <tr>
</a-form-item> <td>
<a-form-item label="Alpn"> <span>SNI</span>
<a-select </td>
mode="multiple" <td>
style="width: 250px" <a-form-item>
:dropdown-class-name="themeSwitcher.currentTheme" <a-input v-model.trim="inbound.stream.xtls.server" style="width: 250px"></a-input>
v-model="inbound.stream.xtls.alpn"> </a-form-item>
<a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option> </td>
</a-select> </tr>
</a-form-item> <tr>
<a-form-item label="Allow insecure"> <td>
<a-switch v-model="inbound.stream.xtls.settings.allowInsecure"></a-switch> <span>Alpn</span>
</a-form-item> </td>
<template v-for="cert,index in inbound.stream.xtls.certs"> <td>
<a-form-item label='{{ i18n "certificate" }}'> <a-form-item>
<a-radio-group v-model="cert.useFile" button-style="solid"> <a-select mode="multiple" style="width: 250px" :dropdown-class-name="themeSwitcher.currentTheme"
<a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button> v-model="inbound.stream.xtls.alpn">
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button> <a-select-option v-for="alpn in ALPN_OPTION" :value="alpn">[[ alpn ]]</a-select-option>
</a-radio-group> </a-select>
<a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.xtls.addCert()" style="margin-left: 10px">+</a-button> </a-form-item>
<a-button v-if="inbound.stream.xtls.certs.length>1" type="primary" size="small" @click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px">-</a-button> </td>
</a-form-item> </tr>
<template v-if="cert.useFile"> <tr>
<a-form-item label='{{ i18n "pages.inbounds.publicKeyPath" }}'> <td>
<a-input v-model.trim="cert.certFile" style="width:300px;"></a-input> <span>Allow insecure</span>
</a-form-item> </td>
<a-form-item label='{{ i18n "pages.inbounds.keyPath" }}'> <td>
<a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input> <a-form-item>
</a-form-item> <a-switch v-model="inbound.stream.xtls.settings.allowInsecure"></a-switch>
<a-button type="primary" icon="import" @click="setDefaultCertXtls(index)">{{ i18n "pages.inbounds.setDefaultCert" }}</a-button> </a-form-item>
</template> </td>
<template v-else> </tr>
<a-form-item label='{{ i18n "pages.inbounds.publicKeyContent" }}'> <tr v-for="cert,index in inbound.stream.xtls.certs">
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input> <td>
</a-form-item> <span>{{ i18n "certificate" }}</span>
<a-form-item label='{{ i18n "pages.inbounds.keyContent" }}'> </td>
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input> <td>
</a-form-item> <a-form-item>
</template> <a-radio-group v-model="cert.useFile" button-style="solid">
</template> <a-radio-button :value="true">{{ i18n "pages.inbounds.certificatePath" }}</a-radio-button>
<a-radio-button :value="false">{{ i18n "pages.inbounds.certificateContent" }}</a-radio-button>
</a-radio-group>
<a-button v-if="index === 0" type="primary" size="small" @click="inbound.stream.xtls.addCert()"
style="margin-left: 10px">+</a-button>
<a-button v-if="inbound.stream.xtls.certs.length>1" type="primary" size="small"
@click="inbound.stream.xtls.removeCert(index)" style="margin-left: 10px">-</a-button>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.xtls.certs" v-if="cert.useFile">
<td>
<span>{{ i18n "pages.inbounds.publicKeyPath" }}</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="cert.certFile" style="width:300px;"></a-input>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.xtls.certs" v-if="cert.useFile">
<td>
<span>{{ i18n "pages.inbounds.keyPath" }}</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="cert.keyFile" style="width:300px;"></a-input>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.xtls.certs" v-if="cert.useFile">
<td>
<a-button type="primary" icon="import" @click="setDefaultCertXtls(index)">{{ i18n
"pages.inbounds.setDefaultCert" }}</a-button>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.xtls.certs" v-else>
<td>
<span>{{ i18n "pages.inbounds.publicKeyContent" }}</span>
</td>
<td>
<a-form-item>
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.cert"></a-input>
</a-form-item>
</td>
</tr>
<tr v-for="cert,index in inbound.stream.xtls.certs" v-else>
<td>
<span>{{ i18n "pages.inbounds.keyContent" }}</span>
</td>
<td>
<a-form-item>
<a-input type="textarea" :rows="3" style="width:300px;" v-model="cert.key"></a-input>
</a-form-item>
</td>
</tr>
</table>
</a-form> </a-form>
<!-- reality settings --> <!-- reality settings -->
<a-form v-else-if="inbound.reality" layout="inline"> <a-form v-else-if="inbound.reality" layout="inline">
<a-form-item label="Show"> <table width="100%" class="ant-table-tbody">
<a-switch v-model="inbound.stream.reality.show"> <tr>
</a-switch> <td>
</a-form-item> <span>Show</span>
<a-form-item label="xVer"> </td>
<a-input-number v-model="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input-number> <td>
</a-form-item> <a-form-item>
<a-form-item label="uTLS"> <a-switch v-model="inbound.stream.reality.show"></a-switch>
<a-select v-model="inbound.stream.reality.settings.fingerprint" </a-form-item>
style="width: 135px" :dropdown-class-name="themeSwitcher.currentTheme"> </td>
<a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option> </tr>
</a-select> <tr>
</a-form-item> <td>
<a-form-item label="Dest"> <span>xVer</span>
<a-input v-model.trim="inbound.stream.reality.dest" style="width: 300px"></a-input> </td>
</a-form-item> <td>
<a-form-item label="Server Names"> <a-form-item>
<a-input v-model.trim="inbound.stream.reality.serverNames" style="width: 300px"></a-input> <a-input-number v-model="inbound.stream.reality.xver" :min="0" style="width: 60px"></a-input-number>
</a-form-item> </a-form-item>
<a-form-item label="ShortIds"> </td>
<a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"> </a-icon> </tr>
<a-input v-model.trim="inbound.stream.reality.shortIds" style="width: 150px;"></a-input> <tr>
</a-form-item> <td>
<br> <span>uTLS</span>
<a-form-item label="SpiderX"> </td>
<a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width: 150px;"></a-input> <td>
</a-form-item> <a-form-item>
<a-form-item label="Private Key"> <a-select v-model="inbound.stream.reality.settings.fingerprint" style="width: 135px"
<a-input v-model.trim="inbound.stream.reality.privateKey" style="width: 300px"></a-input> :dropdown-class-name="themeSwitcher.currentTheme">
</a-form-item> <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
<a-form-item label="Public Key"> </a-select>
<a-input v-model.trim="inbound.stream.reality.settings.publicKey" style="width: 300px"></a-input> </a-form-item>
</a-form-item> </td>
<a-form-item> </tr>
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Key</a-button> <tr>
</a-form-item> <td>
<span>Dest</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.stream.reality.dest" style="width: 300px"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>
<span>Server Names</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.stream.reality.serverNames" style="width: 300px"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>
<span>ShortIds</span> <a-icon @click="inbound.stream.reality.shortIds = RandomUtil.randomShortId()" type="sync"> </a-icon>
</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.stream.reality.shortIds" style="width: 150px;"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>
<span>SpiderX</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.stream.reality.settings.spiderX" style="width: 150px;"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>
<span>Private Key</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.stream.reality.privateKey" style="width: 300px"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td>
<span>Public Key</span>
</td>
<td>
<a-form-item>
<a-input v-model.trim="inbound.stream.reality.settings.publicKey" style="width: 300px"></a-input>
</a-form-item>
</td>
</tr>
<tr>
<td colspan="2">
<a-button type="primary" icon="import" @click="getNewX25519Cert">Get New Key</a-button>
</td>
</tr>
</table>
</a-form> </a-form>
{{end}} {{end}}