Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/MHSanaei/3x-ui.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMHSanaei <ho3ein.sanaei@gmail.com>2023-03-17 19:07:49 +0300
committerMHSanaei <ho3ein.sanaei@gmail.com>2023-03-17 19:07:49 +0300
commit96786c94189f3d2f3f04c1915529c786228bdf42 (patch)
tree879085e09a3cd485f3246f46be907fe77eb84a1a /web/html/xui
parentbc56e637376142c370c31b17558fc3778a863bd2 (diff)
alireza
Diffstat (limited to 'web/html/xui')
-rw-r--r--web/html/xui/client_bulk_modal.html160
-rw-r--r--web/html/xui/client_modal.html133
-rw-r--r--web/html/xui/common_sider.html15
-rw-r--r--web/html/xui/form/client.html110
-rw-r--r--web/html/xui/form/inbound.html3
-rw-r--r--web/html/xui/form/protocol/dokodemo.html5
-rw-r--r--web/html/xui/form/protocol/shadowsocks.html4
-rw-r--r--web/html/xui/form/protocol/socks.html2
-rw-r--r--web/html/xui/form/protocol/trojan.html103
-rw-r--r--web/html/xui/form/protocol/vless.html99
-rw-r--r--web/html/xui/form/protocol/vmess.html104
-rw-r--r--web/html/xui/form/stream/stream_quic.html4
-rw-r--r--web/html/xui/form/stream/stream_settings.html4
-rw-r--r--web/html/xui/form/tls_settings.html20
-rw-r--r--web/html/xui/inbound_client_table.html44
-rw-r--r--web/html/xui/inbound_info_modal.html119
-rw-r--r--web/html/xui/inbound_modal.html80
-rw-r--r--web/html/xui/inbounds.html241
-rw-r--r--web/html/xui/index.html18
-rw-r--r--web/html/xui/setting.html120
20 files changed, 977 insertions, 411 deletions
diff --git a/web/html/xui/client_bulk_modal.html b/web/html/xui/client_bulk_modal.html
new file mode 100644
index 00000000..19fd4b18
--- /dev/null
+++ b/web/html/xui/client_bulk_modal.html
@@ -0,0 +1,160 @@
+{{define "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"
+ :class="siderDrawer.isDarkTheme ? darkClass : ''"
+ :ok-text="clientsBulkModal.okText" cancel-text='{{ i18n "close" }}'>
+ <a-form layout="inline">
+ <a-form-item label='{{ i18n "pages.client.method" }}'>
+ <a-select v-model="clientsBulkModal.emailMethod" buttonStyle="solid" style="width: 350px" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
+ <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="4">Random_Prefix+Num@Telegram Username</a-select-option>
+ </a-select>
+ </a-form-item><br />
+ <a-form-item v-if="clientsBulkModal.emailMethod>1">
+ <span slot="label">{{ i18n "pages.client.first" }}</span>
+ <a-input-number v-model="clientsBulkModal.firstNum" :min="1"></a-input-number>
+ </a-form-item>
+ <a-form-item v-if="clientsBulkModal.emailMethod>1">
+ <span slot="label">{{ i18n "pages.client.last" }}</span>
+ <a-input-number v-model="clientsBulkModal.lastNum" :min="clientsBulkModal.firstNum"></a-input-number>
+ </a-form-item>
+ <a-form-item v-if="clientsBulkModal.emailMethod>0">
+ <span slot="label">{{ i18n "pages.client.prefix" }}</span>
+ <a-input v-model="clientsBulkModal.emailPrefix" style="width: 120px"></a-input>
+ </a-form-item>
+ <a-form-item v-if="clientsBulkModal.emailMethod>2">
+ <span slot="label" v-if="clientsBulkModal.emailMethod == 4">tg_uname</span>
+ <span slot="label" v-else>{{ i18n "pages.client.postfix" }}</span>
+ <a-input v-model="clientsBulkModal.emailPostfix" style="width: 120px"></a-input>
+ </a-form-item>
+
+ <a-form-item v-if="clientsBulkModal.emailMethod < 2">
+ <span slot="label">{{ i18n "pages.client.clientCount" }}</span>
+ <a-input-number v-model="clientsBulkModal.quantity" :min="1" :max="100"></a-input-number>
+ </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="clientsBulkModal.totalGB" :min="0"></a-input-number>
+ </a-form-item>
+ <a-form-item>
+ <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' }" format="YYYY-MM-DD HH:mm"
+ :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+ v-model="clientsBulkModal.expiryTime" style="width: 300px;"></a-date-picker>
+ </a-form-item>
+ </a-form>
+</a-modal>
+<script>
+
+ const clientsBulkModal = {
+ visible: false,
+ confirmLoading: false,
+ title: '',
+ okText: '',
+ confirm: null,
+ dbInbound: new DBInbound(),
+ inbound: new Inbound(),
+ clients: [],
+ quantity: 1,
+ totalGB: 0,
+ expiryTime: '',
+ emailMethod: 0,
+ firstNum: 1,
+ lastNum: 1,
+ emailPrefix: "",
+ emailPostfix: "",
+ ok() {
+ method=clientsBulkModal.emailMethod;
+ if(method>1){
+ start=clientsBulkModal.firstNum;
+ end=clientsBulkModal.lastNum + 1;
+ } else {
+ start=0;
+ end=clientsBulkModal.quantity;
+ }
+ prefix = (method>0 && clientsBulkModal.emailPrefix.length>0) ? "_" + clientsBulkModal.emailPrefix : "";
+ useNum=(method>1);
+ postfix = (method>2 && clientsBulkModal.emailPostfix.length>0) ? (method == 4 ? "@" : "") + clientsBulkModal.emailPostfix : "";
+ for (let i = start; i < end; i++) {
+ newClient = clientsBulkModal.newClient(clientsBulkModal.dbInbound.protocol);
+ newClient.email += useNum ? prefix + i.toString() + postfix : prefix + postfix;
+ newClient._totalGB = clientsBulkModal.totalGB;
+ newClient._expiryTime = clientsBulkModal.expiryTime;
+ clientsBulkModal.clients.push(newClient);
+ }
+ ObjectUtil.execute(clientsBulkModal.confirm, clientsBulkModal.inbound, clientsBulkModal.dbInbound);
+ },
+ show({ title='', okText='{{ i18n "sure" }}', dbInbound=null, confirm=(inbound, dbInbound)=>{} }) {
+ this.visible = true;
+ this.title = title;
+ this.okText = okText;
+ this.confirm = confirm;
+ this.quantity = 1;
+ this.totalGB = 0;
+ this.expiryTime = '';
+ this.emailMethod= 0;
+ this.firstNum= 1;
+ this.lastNum= 1;
+ this.emailPrefix= "";
+ this.emailPostfix= "";
+
+ this.dbInbound = new DBInbound(dbInbound);
+ this.inbound = dbInbound.toInbound();
+ this.clients = this.getClients(this.inbound.protocol, this.inbound.settings);
+ },
+ getClients(protocol, clientSettings) {
+ switch(protocol){
+ case Protocols.VMESS: return clientSettings.vmesses;
+ case Protocols.VLESS: return clientSettings.vlesses;
+ case Protocols.TROJAN: return clientSettings.trojans;
+ default: return null;
+ }
+ },
+ newClient(protocol) {
+ switch (protocol) {
+ case Protocols.VMESS: return new Inbound.VmessSettings.Vmess();
+ case Protocols.VLESS: return new Inbound.VLESSSettings.VLESS();
+ case Protocols.TROJAN: return new Inbound.TrojanSettings.Trojan();
+ default: return null;
+ }
+ },
+ close() {
+ clientsBulkModal.visible = false;
+ clientsBulkModal.loading(false);
+ },
+ loading(loading) {
+ clientsBulkModal.confirmLoading = loading;
+ },
+ };
+
+ const clientsBulkModalApp = new Vue({
+ delimiters: ['[[', ']]'],
+ el: '#client-bulk-modal',
+ data: {
+ clientsBulkModal,
+ get inbound() {
+ return this.clientsBulkModal.inbound;
+ },
+ },
+ });
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/xui/client_modal.html b/web/html/xui/client_modal.html
new file mode 100644
index 00000000..17381a88
--- /dev/null
+++ b/web/html/xui/client_modal.html
@@ -0,0 +1,133 @@
+{{define "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="siderDrawer.isDarkTheme ? darkClass : ''"
+ :ok-text="clientModal.okText" cancel-text='{{ i18n "close" }}'>
+ {{template "form/client"}}
+</a-modal>
+<script>
+
+ const clientModal = {
+ visible: false,
+ confirmLoading: false,
+ title: '',
+ okText: '',
+ dbInbound: new DBInbound(),
+ inbound: new Inbound(),
+ clients: [],
+ clientStats: [],
+ index: null,
+ clientIps: null,
+ isExpired: false,
+ ok() {
+ ObjectUtil.execute(clientModal.confirm, clientModal.inbound, clientModal.dbInbound, clientModal.index);
+ },
+ show({ title='', okText='{{ i18n "sure" }}', index=null, dbInbound=null, confirm=(index, dbInbound)=>{}, isEdit=false }) {
+ this.visible = true;
+ this.title = title;
+ this.okText = okText;
+ this.isEdit = isEdit;
+ this.dbInbound = new DBInbound(dbInbound);
+ this.inbound = dbInbound.toInbound();
+ this.clients = this.getClients(this.inbound.protocol, this.inbound.settings);
+ this.index = index === null ? this.clients.length : index;
+ this.isExpired = isEdit ? this.inbound.isExpiry(this.index) : false;
+ if (!isEdit){
+ this.addClient(this.inbound.protocol, this.clients);
+ }
+ this.clientStats = this.dbInbound.clientStats.find(row => row.email === this.clients[this.index].email);
+ this.confirm = confirm;
+ },
+ getClients(protocol, clientSettings) {
+ switch(protocol){
+ case Protocols.VMESS: return clientSettings.vmesses;
+ case Protocols.VLESS: return clientSettings.vlesses;
+ case Protocols.TROJAN: return clientSettings.trojans;
+ default: return null;
+ }
+ },
+ addClient(protocol, clients) {
+ switch (protocol) {
+ case Protocols.VMESS: return clients.push(new Inbound.VmessSettings.Vmess());
+ case Protocols.VLESS: return clients.push(new Inbound.VLESSSettings.VLESS());
+ case Protocols.TROJAN: return clients.push(new Inbound.TrojanSettings.Trojan());
+ default: return null;
+ }
+ },
+ close() {
+ clientModal.visible = false;
+ clientModal.loading(false);
+ },
+ loading(loading) {
+ clientModal.confirmLoading = loading;
+ },
+ };
+
+ const clientModalApp = new Vue({
+ delimiters: ['[[', ']]'],
+ el: '#client-modal',
+ data: {
+ clientModal,
+ get inbound() {
+ return this.clientModal.inbound;
+ },
+ get client() {
+ return this.clientModal.clients[this.clientModal.index];
+ },
+ get clientStats() {
+ return this.clientModal.clientStats;
+ },
+ get isEdit() {
+ return this.clientModal.isEdit;
+ },
+ get isTrafficExhausted() {
+ if(!clientStats) return false
+ if(clientStats.total == 0) return false
+ if(clientStats.up + clientStats.down < clientStats.total) return false
+ return true
+ },
+ get isExpiry() {
+ return this.clientModal.isExpired
+ },
+ get statsColor() {
+ if(!clientStats) return 'blue'
+ if(clientStats.total === 0) return 'blue'
+ else if(clientStats.total > 0 && (clientStats.down+clientStats.up) < clientStats.total) return 'cyan'
+ else return 'red'
+ }
+ },
+ methods: {
+ getNewEmail(client) {
+ var chars = 'abcdefghijklmnopqrstuvwxyz1234567890';
+ var string = '';
+ var len = 6 + Math.floor(Math.random() * 5);
+ for(var ii=0; ii<len; ii++){
+ string += chars[Math.floor(Math.random() * chars.length)];
+ }
+ client.email = string;
+ },
+ async getDBClientIps(email,event) {
+ const msg = await HttpUtil.post('/xui/inbound/clientIps/'+ email);
+ if (!msg.success) {
+ return;
+ }
+ try {
+ ips = JSON.parse(msg.obj)
+ ips = ips.join(",")
+ event.target.value = ips
+ } catch (error) {
+ // text
+ event.target.value = msg.obj
+ }
+ },
+ async clearDBClientIps(email,event) {
+ const msg = await HttpUtil.post('/xui/inbound/clearClientIps/'+ email);
+ if (!msg.success) {
+ return;
+ }
+ event.target.value = ""
+ },
+ },
+ });
+</script>
+{{end}}
diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html
index 9de00175..13c24c34 100644
--- a/web/html/xui/common_sider.html
+++ b/web/html/xui/common_sider.html
@@ -13,14 +13,14 @@
</a-menu-item>
<!--<a-menu-item key="{{ .base_path }}xui/clients">-->
<!-- <a-icon type="laptop"></a-icon>-->
-<!-- <span>client</span>-->
+<!-- <span>Client</span>-->
<!--</a-menu-item>-->
<a-sub-menu>
<template slot="title">
<a-icon type="link"></a-icon>
<span>{{ i18n "menu.link"}}</span>
</template>
- <a-menu-item key="https://github.com/mhsanaei/3x-ui/">
+ <a-menu-item key="https://github.com/mhsanaei/3x-ui/">
<a-icon type="github"></a-icon>
<span>Github</span>
</a-menu-item>
@@ -55,11 +55,12 @@
<a-drawer id="sider-drawer" placement="left" :closable="false"
@close="siderDrawer.close()"
:visible="siderDrawer.visible"
+ :wrap-class-name="siderDrawer.isDarkTheme ? 'ant-drawer-dark' : ''"
:wrap-style="{ padding: 0 }">
<div class="drawer-handle" @click="siderDrawer.change()" slot="handle">
<a-icon :type="siderDrawer.visible ? 'close' : 'menu-fold'"></a-icon>
</div>
- <a-menu mode="inline" selected-keys="">
+ <a-menu :theme="siderDrawer.theme" mode="inline" selected-keys="">
<a-menu-item mode="inline">
<a-icon type="bg-colors"></a-icon>
<a-switch :default-checked="siderDrawer.isDarkTheme"
@@ -68,19 +69,17 @@
@change="siderDrawer.changeTheme()"></a-switch>
</a-menu-item>
</a-menu>
- <a-menu mode="inline" :selected-keys="['{{ .request_uri }}']"
+ <a-menu :theme="siderDrawer.theme" mode="inline" :selected-keys="['{{ .request_uri }}']"
@click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
{{template "menuItems" .}}
</a-menu>
</a-drawer>
<script>
-
-
const darkClass = "ant-card-dark";
const bgDarkStyle = "background-color: #242c3a";
const siderDrawer = {
visible: false,
- collapsed: false,
+ collapsed: false,
isDarkTheme: localStorage.getItem("dark-mode") === 'true' ? true : false,
show() {
this.visible = true;
@@ -90,7 +89,7 @@
},
change() {
this.visible = !this.visible;
- },
+ },
toggleCollapsed() {
this.collapsed = !this.collapsed;
},
diff --git a/web/html/xui/form/client.html b/web/html/xui/form/client.html
new file mode 100644
index 00000000..586f4fd4
--- /dev/null
+++ b/web/html/xui/form/client.html
@@ -0,0 +1,110 @@
+{{define "form/client"}}
+<a-form layout="inline" v-if="client">
+ <template v-if="isEdit">
+ <a-tag v-if="isExpiry || isTrafficExhausted" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
+ </template>
+ <a-form-item>
+ <span slot="label">
+ Email
+ <a-tooltip>
+ <template slot="title">
+ The Email Must Be Completely Unique
+ </template>
+ <a-icon type="sync" @click="getNewEmail(client)"></a-icon>
+ </a-tooltip>
+ </span>
+ <a-input v-model.trim="client.email" style="width: 150px;" ></a-input>
+ </a-form-item>
+ <a-form-item label="Password" v-if="inbound.protocol === Protocols.TROJAN">
+ <a-input v-model.trim="client.password" style="width: 150px;" ></a-input>
+ </a-form-item>
+ <a-form-item label="ID" v-if="inbound.protocol === Protocols.VMESS || inbound.protocol === Protocols.VLESS">
+ <a-input v-model.trim="client.id" style="width: 300px;"></a-input>
+ </a-form-item>
+ <a-form-item label='{{ i18n "additional" }} ID' v-if="inbound.protocol === Protocols.VMESS">
+ <a-input type="number" v-model.number="client.alterId" style="width: 70px;"></a-input>
+ </a-form-item>
+ <a-form-item>
+ <span slot="label">
+ IP Count Limit
+ <a-tooltip>
+ <template slot="title">
+ Disable inbound if more than entered count (0 for disable limit ip)
+ </template>
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </span>
+ <a-input type="number" v-model.number="client.limitIp" min="0" style="width: 70px;" ></a-input>
+ </a-form-item>
+ <a-form-item v-if="client.email && client.limitIp > 0 && isEdit">
+ <span slot="label">
+ IP Log
+ <a-tooltip>
+ <template slot="title">
+ IPs history Log (before enabling inbound after it has been disabled by IP limit, you should clear the log)
+ </template>
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ <a-tooltip>
+ <template slot="title">
+ Clear The Log
+ </template>
+ <span style="color: #FF4D4F">
+ <a-icon type="delete" @click="clearDBClientIps(client.email,$event)"></a-icon>
+ </span>
+ </a-tooltip>
+ </span>
+ <a-form layout="block">
+ <a-textarea readonly @click="getDBClientIps(client.email,$event)" placeholder="Click To Get IPs" :auto-size="{ minRows: 2, maxRows: 10 }">
+ </a-textarea>
+ </a-form>
+ </a-form-item>
+ <a-form-item v-if="inbound.XTLS" label="Flow">
+ <a-select v-model="client.flow" style="width: 150px">
+ <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" layout="inline">
+ <a-select v-model="client.flow" style="width: 150px">
+ <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" style="width: 70px;"></a-input-number>
+ <template v-if="isEdit && clientStats">
+ {{ i18n "usage" }}:
+ <a-tag :color="statsColor">
+ [[ sizeFormat(clientStats.up) ]] /
+ [[ sizeFormat(clientStats.down) ]]
+ ([[ sizeFormat(clientStats.up + clientStats.down) ]])
+ </a-tag>
+ </template>
+ </a-form-item>
+ <a-form-item>
+ <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' }" format="YYYY-MM-DD HH:mm"
+ :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
+ v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
+ <a-tag color="red" v-if="isExpiry">Expired</a-tag>
+ </a-form-item>
+</a-form>
+{{end}} \ No newline at end of file
diff --git a/web/html/xui/form/inbound.html b/web/html/xui/form/inbound.html
index 1a2e7ca1..74fe1384 100644
--- a/web/html/xui/form/inbound.html
+++ b/web/html/xui/form/inbound.html
@@ -8,7 +8,7 @@
<a-switch v-model="dbInbound.enable"></a-switch>
</a-form-item>
<a-form-item label='{{ i18n "protocol" }}'>
- <a-select v-model="inbound.protocol" style="width: 160px;">
+ <a-select v-model="inbound.protocol" style="width: 160px;" :disabled="isEdit" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<a-select-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
</a-select>
</a-form-item>
@@ -50,6 +50,7 @@
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
+ :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''"
v-model="dbInbound._expiryTime" style="width: 300px;"></a-date-picker>
</a-form-item>
</a-form>
diff --git a/web/html/xui/form/protocol/dokodemo.html b/web/html/xui/form/protocol/dokodemo.html
index a4dde06d..dbba6b5b 100644
--- a/web/html/xui/form/protocol/dokodemo.html
+++ b/web/html/xui/form/protocol/dokodemo.html
@@ -7,11 +7,14 @@
<a-input type="number" v-model.number="inbound.settings.port"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.network"}}'>
- <a-select v-model="inbound.settings.network" style="width: 100px;">
+ <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<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>
+ <a-form-item label="FollowRedirect">
+ <a-switch v-model="inbound.settings.followRedirect"></a-switch>
+ </a-form-item>
</a-form>
{{end}} \ No newline at end of file
diff --git a/web/html/xui/form/protocol/shadowsocks.html b/web/html/xui/form/protocol/shadowsocks.html
index 18bcf727..21d614ae 100644
--- a/web/html/xui/form/protocol/shadowsocks.html
+++ b/web/html/xui/form/protocol/shadowsocks.html
@@ -1,7 +1,7 @@
{{define "form/shadowsocks"}}
<a-form layout="inline">
<a-form-item label='{{ i18n "encryption" }}'>
- <a-select v-model="inbound.settings.method" style="width: 165px;">
+ <a-select v-model="inbound.settings.method" style="width: 165px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<a-select-option v-for="method in SSMethods" :value="method">[[ method ]]</a-select-option>
</a-select>
</a-form-item>
@@ -9,7 +9,7 @@
<a-input v-model.trim="inbound.settings.password"></a-input>
</a-form-item>
<a-form-item label='{{ i18n "pages.inbounds.network" }}'>
- <a-select v-model="inbound.settings.network" style="width: 100px;">
+ <a-select v-model="inbound.settings.network" style="width: 100px;" :dropdown-class-name="siderDrawer.isDarkTheme ? 'ant-card-dark' : ''">
<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>
diff --git a/web/html/xui/form/protocol/socks.html b/web/html/xui/form/protocol/socks.html
index 35c1c0b5..5857d413 100644
--- a/web/html/xui/form/protocol/socks.html
+++ b/web/html/xui/form/protocol/socks.html
@@ -1,6 +1,6 @@
{{define "form/socks"}}
<a-form layout="inline">
-<!-- <a-form-item label="密码认证">-->
+<!-- <a-form-item label="Password authentication">-->
<a-form-item label='{{ i18n "password" }}'>
<a-switch :checked="inbound.settings.auth === 'password'"
@change="checked => inbound.settings.auth = checked ? 'password' : 'noauth'"></a-switch>
diff --git a/web/html/xui/form/protocol/trojan.html b/web/html/xui/form/protocol/trojan.html
index 3127e4c5..e1d82572 100644
--- a/web/html/xui/form/protocol/trojan.html
+++ b/web/html/xui/form/protocol/trojan.html
@@ -1,11 +1,7 @@
{{define "form/trojan"}}
<a-form layout="inline">
-<label style="color: green;">{{ i18n "clients"}}</label>
-<a-collapse activeKey="0" v-for="(trojan, index) in inbound.settings.trojans"
-:key="`trojan-${index}`">
-
- <a-collapse-panel :class="getHeaderStyle(trojan.email)" :header="getHeaderText(trojan.email)">
- <a-tag v-if="isExpiry(index) || ((getUpStats(trojan.email) + getDownStats(trojan.email)) > trojan.totalGB && trojan.totalGB != 0)" color="red" style="margin-bottom: 10px;display: block;text-align: center;">Account is (Expired|Traffic Ended) And Disabled</a-tag>
+<a-collapse activeKey="0" v-for="(client, index) in inbound.settings.trojans.slice(0,1)" v-if="!isEdit">
+ <a-collapse-panel header="{{ i18n "pages.inbounds.client" }}">
<a-form layout="inline">
<a-form-item>
<span slot="label">
@@ -14,18 +10,16 @@
<template slot="title">
The Email Must Be Completely Unique
</template>
- <!--Renew Svg Icon-->
- <svg
- @click="getNewEmail(trojan)"
- xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="anticon anticon-question-circle" viewBox="0 0 16 16"> <path d="M11.534 7h3.932a.25.25 0 0 1 .192.41l-1.966 2.36a.25.25 0 0 1-.384 0l-1.966-2.36a.25.25 0 0 1 .192-.41zm-11 2h3.932a.25.25 0 0 0 .192-.41L2.692 6.23a.25.25 0 0 0-.384 0L.342 8.59A.25.25 0 0 0 .534 9z"/> <path fill-rule="evenodd" d="M8 3c-1.552 0-2.94.707-3.857 1.818a.5.5 0 1 1-.771-.636A6.002 6.002 0 0 1 13.917 7H12.9A5.002 5.002 0 0 0 8 3zM3.1 9a5.002 5.002 0 0 0 8.757 2.182.5.5 0 1 1 .771.636A6.002 6.002 0 0 1 2.083 9H3.1z"/> </svg>
+ <a-icon @click="getNewEmail(client)" type="sync"> </a-icon>
</a-tooltip>
</span>
- <a-input v-model.trim="trojan.email" style="width: 150px;"></a-input>
+ <a-input v-model.trim="client.email" style="width: 150px;" ></a-input>
</a-form-item>
- <a-form-item label="Password" >
- <a-input v-model.trim="trojan.password" style="width: 150px;"></a-input>
- </a-form-item>
- <a-form-item>
+ </a-form>
+ <a-form-item label="Password">
+ <a-input v-model.trim="client.password" style="width: 150px;"></a-input>
+ </a-form-item>
+ <a-form-item>
<span slot="label">
IP Count Limit
<a-tooltip>
@@ -35,9 +29,9 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
- <a-input type="number" v-model.number="trojan.limitIp" min="0" style="width: 70px;"></a-input>
- </a-form-item>
- <a-form-item v-if="trojan.email && trojan.limitIp > 0 && isEdit">
+ <a-input type="number" v-model.number="client.limitIp" min="0" style="width: 70px;" ></a-input>
+ </a-form-item>
+ <a-form-item v-if="client.email && client.limitIp > 0 && isEdit">
<span slot="label">
IP log
<a-tooltip>
@@ -51,22 +45,26 @@
clear the log
</template>
<span style="color: #FF4D4F">
- <a-icon type="delete" @click="clearDBClientIps(trojan.email,$event)"></a-icon>
+ <a-icon type="delete" @click="clearDBClientIps(client.email,$event)"></a-icon>
</span>
</a-tooltip>
</span>
<a-form layout="block">
- <a-textarea readonly @click="getDBClientIps(trojan.email,$event)" placeholder="Click To Get IPs" :auto-size="{ minRows: 2, maxRows: 10 }">
+ <a-textarea readonly @click="getDBClientIps(client.email,$event)" placeholder="Click To Get IPs" :auto-size="{ minRows: 2, maxRows: 10 }">
</a-textarea>
</a-form>
- </a-form-item>
- </a-form>
- <a-form-item v-if="inbound.XTLS" label="Flow">
- <a-select v-model="trojan.flow" style="width: 150px">
+ </a-form-item>
+ <a-form-item v-if="inbound.xtls" label="Flow">
+ <a-select v-model="client.flow" style="width: 150px">
<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-if="inbound.tls" label="uTLS" layout="inline">
+ <a-select v-model="inbound.settings.trojans[index].fingerprint" label="uTLS" style="width: 150px">
+ <a-select-option v-for="key in UTLS_FINGERPRINT" :value="key">[[ key ]]</a-select-option>
+ </a-select>
+ </a-form-item>
<a-form-item>
<span slot="label">
<span >{{ i18n "pages.inbounds.totalFlow" }}</span>(GB)
@@ -77,7 +75,7 @@
<a-icon type="question-circle" theme="filled"></a-icon>
</a-tooltip>
</span>
- <a-input-number v-model="trojan._totalGB" :min="0"></a-input-number>
+ <a-input-number v-model="client._totalGB" :min="0"></a-input-number>
</a-form-item>
<a-form-item>
<span slot="label">
@@ -90,39 +88,22 @@
</a-tooltip>
</span>
<a-date-picker :show-time="{ format: 'HH:mm' }" format="YYYY-MM-DD HH:mm"
- v-model="trojan._expiryTime" style="width: 170px;"></a-date-picker>
+ v-model="client._expiryTime" style="width: 170px;"></a-date-picker>
</a-form-item>
- <a-form layout="inline">