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
path: root/web/html
diff options
context:
space:
mode:
Diffstat (limited to 'web/html')
-rw-r--r--web/html/common/head.html17
-rw-r--r--web/html/common/js.html22
-rw-r--r--web/html/common/prompt_modal.html67
-rw-r--r--web/html/common/qrcode_modal.html120
-rw-r--r--web/html/common/text_modal.html58
-rw-r--r--web/html/login.html123
-rw-r--r--web/html/xui/common_sider.html73
-rw-r--r--web/html/xui/component/inbound_info.html94
-rw-r--r--web/html/xui/component/setting.html32
-rw-r--r--web/html/xui/form/inbound.html106
-rw-r--r--web/html/xui/form/protocol/dokodemo.html17
-rw-r--r--web/html/xui/form/protocol/http.html10
-rw-r--r--web/html/xui/form/protocol/shadowsocks.html19
-rw-r--r--web/html/xui/form/protocol/socks.html24
-rw-r--r--web/html/xui/form/protocol/trojan.html141
-rw-r--r--web/html/xui/form/protocol/vless.html155
-rw-r--r--web/html/xui/form/protocol/vmess.html119
-rw-r--r--web/html/xui/form/sniffing.html16
-rw-r--r--web/html/xui/form/stream/stream_grpc.html7
-rw-r--r--web/html/xui/form/stream/stream_http.html12
-rw-r--r--web/html/xui/form/stream/stream_kcp.html38
-rw-r--r--web/html/xui/form/stream/stream_quic.html24
-rw-r--r--web/html/xui/form/stream/stream_settings.html45
-rw-r--r--web/html/xui/form/stream/stream_tcp.html86
-rw-r--r--web/html/xui/form/stream/stream_ws.html33
-rw-r--r--web/html/xui/form/tls_settings.html60
-rw-r--r--web/html/xui/inbound_info_modal.html61
-rw-r--r--web/html/xui/inbound_modal.html178
-rw-r--r--web/html/xui/inbounds.html457
-rw-r--r--web/html/xui/inbounds_client_row.html22
-rw-r--r--web/html/xui/index.html334
-rw-r--r--web/html/xui/setting.html196
32 files changed, 2766 insertions, 0 deletions
diff --git a/web/html/common/head.html b/web/html/common/head.html
new file mode 100644
index 00000000..f34ce62f
--- /dev/null
+++ b/web/html/common/head.html
@@ -0,0 +1,17 @@
+{{define "head"}}
+<head>
+ <meta charset="UTF-8">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" href="{{ .base_path }}assets/ant-design-vue@1.7.2/antd.min.css">
+ <link rel="stylesheet" href="{{ .base_path }}assets/element-ui@2.15.0/theme-chalk/display.css">
+ <link rel="stylesheet" href="{{ .base_path }}assets/css/custom.css?{{ .cur_ver }}">
+ <style>
+ [v-cloak] {
+ display: none;
+ }
+ </style>
+ <title>{{ i18n .title}}</title>
+</head>
+{{end}} \ No newline at end of file
diff --git a/web/html/common/js.html b/web/html/common/js.html
new file mode 100644
index 00000000..d400196b
--- /dev/null
+++ b/web/html/common/js.html
@@ -0,0 +1,22 @@
+{{define "js"}}
+<script src="{{ .base_path }}assets/vue@2.6.12/vue.min.js"></script>
+<script src="{{ .base_path }}assets/moment/moment.min.js"></script>
+<script src="{{ .base_path }}assets/ant-design-vue@1.7.2/antd.min.js"></script>
+<script src="{{ .base_path }}assets/base64/base64.min.js"></script>
+<script src="{{ .base_path }}assets/axios/axios.min.js"></script>
+<script src="{{ .base_path }}assets/qs/qs.min.js"></script>
+<script src="{{ .base_path }}assets/qrcode/qrious.min.js"></script>
+<script src="{{ .base_path }}assets/clipboard/clipboard.min.js"></script>
+<script src="{{ .base_path }}assets/uri/URI.min.js"></script>
+<script src="{{ .base_path }}assets/js/axios-init.js?{{ .cur_ver }}"></script>
+<script src="{{ .base_path }}assets/js/util/common.js?{{ .cur_ver }}"></script>
+<script src="{{ .base_path }}assets/js/util/date-util.js?{{ .cur_ver }}"></script>
+<script src="{{ .base_path }}assets/js/util/utils.js?{{ .cur_ver }}"></script>
+<script src="{{ .base_path }}assets/js/model/xray.js?{{ .cur_ver }}"></script>
+<script src="{{ .base_path }}assets/js/model/models.js?{{ .cur_ver }}"></script>
+<script src="{{ .base_path }}assets/js/langs.js"></script>
+<script>
+ const basePath = '{{ .base_path }}';
+ axios.defaults.baseURL = basePath;
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/common/prompt_modal.html b/web/html/common/prompt_modal.html
new file mode 100644
index 00000000..91c26615
--- /dev/null
+++ b/web/html/common/prompt_modal.html
@@ -0,0 +1,67 @@
+{{define "promptModal"}}
+<a-modal id="prompt-modal" v-model="promptModal.visible" :title="promptModal.title"
+ :closable="true" @ok="promptModal.ok" :mask-closable="false"
+ :ok-text="promptModal.okText" cancel-text='{{ i18n "cancel" }}'>
+ <a-input id="prompt-modal-input" :type="promptModal.type"
+ v-model="promptModal.value"
+ :autosize="{minRows: 10, maxRows: 20}"
+ @keydown.enter.native="promptModal.keyEnter"
+ @keydown.ctrl.83="promptModal.ctrlS"></a-input>
+</a-modal>
+
+<script>
+
+ const promptModal = {
+ title: '',
+ type: '',
+ value: '',
+ okText: '{{ i18n "sure"}}',
+ visible: false,
+ keyEnter(e) {
+ if (this.type !== 'textarea') {
+ e.preventDefault();
+ this.ok();
+ }
+ },
+ ctrlS(e) {
+ if (this.type === 'textarea') {
+ e.preventDefault();
+ promptModal.confirm(promptModal.value);
+ }
+ },
+ ok() {
+ promptModal.close();
+ promptModal.confirm(promptModal.value);
+ },
+ confirm() {},
+ open({
+ title='',
+ type='text',
+ value='',
+ okText='{{ i18n "sure"}}',
+ confirm=() => {},
+ }) {
+ this.title = title;
+ this.type = type;
+ this.value = value;
+ this.okText = okText;
+ this.confirm = confirm;
+ this.visible = true;
+ promptModalApp.$nextTick(() => {
+ document.querySelector('#prompt-modal-input').focus();
+ });
+ },
+ close() {
+ this.visible = false;
+ }
+ };
+
+ const promptModalApp = new Vue({
+ el: '#prompt-modal',
+ data: {
+ promptModal: promptModal,
+ },
+ });
+
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html
new file mode 100644
index 00000000..c80f8a0e
--- /dev/null
+++ b/web/html/common/qrcode_modal.html
@@ -0,0 +1,120 @@
+{{define "qrcodeModal"}}
+<a-modal id="qrcode-modal" v-model="qrModal.visible" :title="qrModal.title"
+ :closable="true" width="300px" :ok-text="qrModal.okText"
+ cancel-text='{{ i18n "close" }}' :ok-button-props="{attrs:{id:'qr-modal-ok-btn'}}">
+ <a-tag color="green" style="margin-bottom: 10px;display: block;text-align: center;" >click on QR Code to Copy</a-tag>
+ <canvas v-if="qrModal.inbound.protocol != Protocols.VMESS && qrModal.inbound.protocol != Protocols.VLESS" id="qrCode" style="width: 100%; height: 100%;"></canvas>
+
+ <template v-if="qrModal.inbound.protocol === Protocols.VMESS" v-for="(vmess, index) in qrModal.inbound.settings.vmesses">
+ <a-tag color="red" style="margin-bottom: 10px;display: block;text-align: center;" v-text="vmess.email"></a-tag>
+ <canvas @click="copyTextToClipboard(`qrCode-vmess-${vmess.id}`,index)" :id="`qrCode-vmess-${vmess.id}`" style="width: 100%; height: 100%;"></canvas>
+ <a-divider style="height: 2px; background-color: #7e7e7e" />
+ </template>
+
+ <template v-if="qrModal.inbound.protocol === Protocols.VLESS" v-for="(vless, index) in qrModal.inbound.settings.vlesses">
+ <a-tag color="red" style="margin-bottom: 10px;display: block;text-align: center;" v-text="vless.email"></a-tag>
+ <canvas @click="copyTextToClipboard(`qrCode-vless-${vless.id}`,index)" :id="`qrCode-vless-${vless.id}`" style="width: 100%; height: 100%;"></canvas>
+ <a-divider style="height: 2px; background-color: #7e7e7e" />
+ </template>
+</a-modal>
+
+<script>
+
+ const qrModal = {
+ title: '',
+ content: '',
+ inbound: new Inbound(),
+ dbInbound: new DBInbound(),
+ okText: '',
+ copyText: '',
+ qrcode: null,
+ clipboard: null,
+ visible: false,
+ show: function (title='', content='', dbInbound=new DBInbound(),okText='{{ i18n "copy" }}', copyText='') {
+ this.title = title;
+ this.content = content;
+ this.dbInbound = dbInbound;
+ this.inbound = dbInbound.toInbound();
+ this.okText = okText;
+ if (ObjectUtil.isEmpty(copyText)) {
+ this.copyText = content;
+ } else {
+ this.copyText = copyText;
+ }
+ this.visible = true;
+ qrModalApp.$nextTick(() => {
+ if (this.clipboard === null) {
+ this.clipboard = new ClipboardJS('#qr-modal-ok-btn', {
+ text: () => this.copyText,
+ });
+ this.clipboard.on('success', () => app.$message.success('{{ i18n "copied" }}'));
+ }
+ if (this.qrcode === null) {
+ this.qrcode = new QRious({
+ element: document.querySelector('#qrCode'),
+ size: 260,
+ value: content,
+ });
+ } else {
+ this.qrcode.value = content;
+ }
+ });
+ },
+ close: function () {
+ this.visible = false;
+ },
+ };
+
+ const qrModalApp = new Vue({
+ el: '#qrcode-modal',
+ data: {
+ qrModal: qrModal,
+ },
+ methods: {
+ setQrCode(elmentId,index) {
+ content = qrModal.inbound.genLink(qrModal.dbInbound.address,qrModal.dbInbound.remark,index)
+
+ new QRious({
+ element: document.querySelector('#'+elmentId),
+ size: 260,
+ value: content,
+ });
+ },
+ copyTextToClipboard(elmentId,index) {
+ link = qrModal.inbound.genLink(qrModal.dbInbound.address,qrModal.dbInbound.remark,index)
+ this.qrModal.copyText = link
+
+ this.qrModal.clipboard = new ClipboardJS('#' + elmentId, {
+ text: () => link,
+ });
+ this.qrModal.clipboard.on('success', () => {
+ app.$message.success('{{ i18n "copied" }}')
+ this.qrModal.clipboard.destroy();
+ });
+
+
+ }
+ },
+ updated() {
+ switch (qrModal.inbound.protocol) {
+ case Protocols.VMESS:
+ vmesses = qrModal.inbound.settings.vmesses
+ for (const index in vmesses) {
+ this.setQrCode("qrCode-vmess-" + vmesses[index].id ,index)
+ }
+ break;
+ case Protocols.VLESS:
+ vlesses = qrModal.inbound.settings.vlesses
+
+ for (const index in vlesses) {
+ this.setQrCode("qrCode-vless-" + vlesses[index].id ,index)
+ }
+ break;
+ default: return null;
+ }
+
+ }
+ });
+
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/common/text_modal.html b/web/html/common/text_modal.html
new file mode 100644
index 00000000..0ae04a88
--- /dev/null
+++ b/web/html/common/text_modal.html
@@ -0,0 +1,58 @@
+{{define "textModal"}}
+<a-modal id="text-modal" v-model="txtModal.visible" :title="txtModal.title"
+ :closable="true" ok-text='{{ i18n "copy" }}' cancel-text='{{ i18n "close" }}'
+ :ok-button-props="{attrs:{id:'txt-modal-ok-btn'}}">
+ <a-button v-if="!ObjectUtil.isEmpty(txtModal.fileName)" type="primary" style="margin-bottom: 10px;"
+ @click="downloader.download(txtModal.fileName, txtModal.content)">
+ {{ i18n "download" }} [[ txtModal.fileName ]]
+ </a-button>
+ <a-input type="textarea" v-model="txtModal.content"
+ :autosize="{ minRows: 10, maxRows: 20}"></a-input>
+</a-modal>
+
+<script>
+
+ const txtModal = {
+ title: '',
+ content: '',
+ fileName: '',
+ qrcode: null,
+ clipboard: null,
+ visible: false,
+ show: function (title='', content='', fileName='') {
+ this.title = title;
+ this.content = content;
+ this.fileName = fileName;
+ this.visible = true;
+ textModalApp.$nextTick(() => {
+ if (this.clipboard === null) {
+ this.clipboard = new ClipboardJS('#txt-modal-ok-btn', {
+ text: () => this.content,
+ });
+ this.clipboard.on('success', () => app.$message.success('{{ i18n "copied" }}'));
+ }
+ if (this.qrcode === null) {
+ this.qrcode = new QRious({
+ element: document.querySelector('#qrCode'),
+ size: 260,
+ value: content,
+ });
+ } else {
+ this.qrcode.value = content;
+ }
+ });
+ },
+ close: function () {
+ this.visible = false;
+ },
+ };
+
+ const textModalApp = new Vue({
+ el: '#text-modal',
+ data: {
+ txtModal: txtModal,
+ },
+ });
+
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/login.html b/web/html/login.html
new file mode 100644
index 00000000..5138f15e
--- /dev/null
+++ b/web/html/login.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="en">
+{{template "head" .}}
+<style>
+
+ #app {
+ padding-top: 100px;
+ }
+
+ h1 {
+ text-align: center;
+ color: #fff;
+ margin: 20px 0 50px 0;
+ }
+
+ .ant-btn, .ant-input {
+ height: 50px;
+ border-radius: 30px;
+ }
+
+ .ant-input-affix-wrapper .ant-input-prefix {
+ left: 23px;
+ }
+
+ .ant-input-affix-wrapper .ant-input:not(:first-child) {
+ padding-left: 50px;
+ }
+
+ .selectLang{
+ display: flex;
+ text-align: center;
+ justify-content: center;
+ }
+
+</style>
+<body>
+<a-layout id="app" v-cloak>
+ <transition name="list" appear>
+ <a-layout-content>
+ <a-row type="flex" justify="center">
+ <a-col :xs="22" :sm="20" :md="16" :lg="12" :xl="8">
+ <h1>{{ i18n "pages.login.title" }}</h1>
+ </a-col>
+ </a-row>
+ <a-row type="flex" justify="center">
+ <a-col :xs="22" :sm="20" :md="16" :lg="12" :xl="8">
+ <a-form>
+ <a-form-item>
+ <a-input v-model.trim="user.username" placeholder='{{ i18n "username" }}'
+ @keydown.enter.native="login" autofocus>
+ <a-icon slot="prefix" type="user" style="color: rgba(0,0,0,.25)"/>
+ </a-input>
+ </a-form-item>
+ <a-form-item>
+ <a-input type="password" v-model.trim="user.password"
+ placeholder='{{ i18n "password" }}' @keydown.enter.native="login">
+ <a-icon slot="prefix" type="lock" style="color: rgba(0,0,0,.25)"/>
+ </a-input>
+ </a-form-item>
+ <a-form-item>
+ <a-button block @click="login" :loading="loading">{{ i18n "login" }}</a-button>
+ </a-form-item>
+ <a-form-item>
+
+ <a-row justify="center" class="selectLang">
+ <a-col :span="4"><span>Language : </span></a-col>
+
+ <a-col :span="6">
+ <a-select
+ ref="selectLang"
+ v-model="lang"
+ @change="setLang(lang)"
+ >
+ <a-select-option :value="l.value" label="China" v-for="l in supportLangs" >
+ <span role="img" aria-label="l.name" v-text="l.icon"></span>
+ &nbsp;&nbsp;<span v-text="l.name"></span>
+ </a-select-option>
+ </a-select>
+ </a-col>
+
+ </a-row>
+
+
+
+ </a-form-item>
+ </a-form>
+ </a-col>
+ </a-row>
+ </a-layout-content>
+ </transition>
+</a-layout>
+{{template "js" .}}
+<script>
+ const leftColor = RandomUtil.randomIntRange(0x222222, 0xFFFFFF / 2).toString(16);
+ const rightColor = RandomUtil.randomIntRange(0xFFFFFF / 2, 0xDDDDDD).toString(16);
+ const deg = RandomUtil.randomIntRange(0, 360);
+ const background = `linear-gradient(${deg}deg, #${leftColor} 10%, #${rightColor} 100%)`;
+ document.querySelector('#app').style.background = background;
+ const app = new Vue({
+ delimiters: ['[[', ']]'],
+ el: '#app',
+ data: {
+ loading: false,
+ user: new User(),
+ lang : ""
+ },
+ created(){
+ this.lang = getLang();
+ },
+ methods: {
+ async login() {
+ this.loading = true;
+ const msg = await HttpUtil.post('/login', this.user);
+ this.loading = false;
+ if (msg.success) {
+ location.href = basePath + 'xui/';
+ }
+ }
+ }
+ });
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/web/html/xui/common_sider.html b/web/html/xui/common_sider.html
new file mode 100644
index 00000000..f4499eeb
--- /dev/null
+++ b/web/html/xui/common_sider.html
@@ -0,0 +1,73 @@
+{{define "menuItems"}}
+<a-menu-item key="{{ .base_path }}xui/">
+ <a-icon type="dashboard"></a-icon>
+ <span>{{ i18n "menu.dashboard"}}</span>
+</a-menu-item>
+<a-menu-item key="{{ .base_path }}xui/inbounds">
+ <a-icon type="user"></a-icon>
+ <span>{{ i18n "menu.inbounds"}}</span>
+</a-menu-item>
+<a-menu-item key="{{ .base_path }}xui/setting">
+ <a-icon type="setting"></a-icon>
+ <span>{{ i18n "menu.setting"}}</span>
+</a-menu-item>
+<!--<a-menu-item key="{{ .base_path }}xui/clients">-->
+<!-- <a-icon type="laptop"></a-icon>-->
+<!-- <span>client</span>-->
+<!--</a-menu-item>-->
+<a-sub-menu>
+ <template slot="title">
+ <a-icon type="link"></a-icon>
+ <span>others</span>
+ </template>
+ <a-menu-item key="https://github.com/mhsanaei/3x-ui/">
+ <a-icon type="github"></a-icon>
+ <span>Github</span>
+ </a-menu-item>
+ <a-menu-item key="https://t.me/xxxuiforever">
+ <a-icon type="usergroup-add"></a-icon>
+ <span>Telegram Group</span>
+ </a-menu-item>
+</a-sub-menu>
+<a-menu-item key="{{ .base_path }}logout">
+ <a-icon type="logout"></a-icon>
+ <span>{{ i18n "menu.logout"}}</span>
+</a-menu-item>
+{{end}}
+
+
+{{define "commonSider"}}
+<a-layout-sider id="sider" collapsible breakpoint="md" collapsed-width="0">
+ <a-menu theme="dark" mode="inline" :selected-keys="['{{ .request_uri }}']"
+ @click="({key}) => key.startsWith('http') ? window.open(key) : location.href = key">
+ {{template "menuItems" .}}
+ </a-menu>
+</a-layout-sider>
+<a-drawer id="sider-drawer" placement="left" :closable="false"
+ @close="siderDrawer.close()"
+ :visible="siderDrawer.visible" :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 theme="light" 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 siderDrawer = {
+ visible: false,
+ show() {
+ this.visible = true;
+ },
+ close() {
+ this.visible = false;
+ },
+ change() {
+ this.visible = !this.visible;
+ }
+ };
+
+</script>
+{{end}}
diff --git a/web/html/xui/component/inbound_info.html b/web/html/xui/component/inbound_info.html
new file mode 100644
index 00000000..cbf156d1
--- /dev/null
+++ b/web/html/xui/component/inbound_info.html
@@ -0,0 +1,94 @@
+{{define "inboundInfoStream"}}
+<p>{{ i18n "transmission" }}: <a-tag color="green">[[ inbound.network ]]</a-tag></p>
+
+<template v-if="inbound.isTcp || inbound.isWs || inbound.isH2">
+ <p v-if="inbound.host">host: <a-tag color="green">[[ inbound.host ]]</a-tag></p>
+ <p v-else>{{ i18n "host" }}: <a-tag color="orange">{{ i18n "none" }}</a-tag></p>
+
+ <p v-if="inbound.path">path: <a-tag color="green">[[ inbound.path ]]</a-tag></p>
+ <p v-else>{{ i18n "path" }}: <a-tag color="orange">{{ i18n "none" }}</a-tag></p>
+</template>
+
+<template v-if="inbound.isQuic">
+ <p>quic {{ i18n "encryption" }}: <a-tag color="green">[[ inbound.quicSecurity ]]</a-tag></p>
+ <p>quic {{ i18n "password" }}: <a-tag color="green">[[ inbound.quicKey ]]</a-tag></p>
+ <p>quic {{ i18n "camouflage" }}: <a-tag color="green">[[ inbound.quicType ]]</a-tag></p>
+</template>
+
+<template v-if="inbound.isKcp">
+ <p>kcp {{ i18n "encryption" }}: <a-tag color="green">[[ inbound.kcpType ]]</a-tag></p>
+ <p>kcp {{ i18n "password" }}: <a-tag color="green">[[ inbound.kcpSeed ]]</a-tag></p>
+</template>
+
+<template v-if="inbound.isGrpc">
+ <p>grpc serviceName: <a-tag color="green">[[ inbound.serviceName ]]</a-tag></p>
+</template>
+
+<template v-if="inbound.tls || inbound.xtls">
+ <p v-if="inbound.tls">tls: <a-tag color="green">{{ i18n "turnOn" }}</a-tag></p>
+ <p v-if="inbound.xtls">xtls: <a-tag color="green">{{ i18n "turnOn" }}</a-tag></p>
+</template>
+<template v-else>
+ <p>tls: <a-tag color="red">{{ i18n "closure" }}</a-tag></p>
+</template>
+<p v-if="inbound.tls">
+ tls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
+</p>
+<p v-if="inbound.xtls">
+ xtls {{ i18n "domainName" }}: <a-tag :color="inbound.serverName ? 'green' : 'orange'">[[ inbound.serverName ? inbound.serverName : '' ]]</a-tag>
+</p>
+{{end}}
+
+
+{{define "component/inboundInfoComponent"}}
+<div>
+ <p>{{ i18n "protocol"}}: <a-tag color="green">[[ dbInbound.protocol ]]</a-tag></p>
+ <p>{{ i18n "pages.inbounds.address"}}: <a-tag color="blue">[[ dbInbound.address ]]</a-tag></p>
+ <p>{{ i18n "pages.inbounds.port"}}: <a-tag color="green">[[ dbInbound.port ]]</a-tag></p>
+
+ <template v-if="dbInbound.isVMess" v-for="(vmess, index) in inbound.settings.vmesses">
+ <p>uuid: <a-tag color="green">[[ vmess.id ]]</a-tag></p>
+ <p>alterId: <a-tag color="green">[[ vmess.alterId ]]</a-tag></p>
+ <a-divider style="height: 2px; background-color: #7e7e7e" />
+ </template>
+
+ <template v-if="dbInbound.isVLess" v-for="(vless, index) in inbound.settings.vlesses">
+ <p>uuid: <a-tag color="green">[[ vless.id ]]</a-tag></p>
+ <p v-if="inbound.isXTls">flow: <a-tag color="green">[[ vless.flow ]]</a-tag></p>
+ <a-divider style="height: 2px; background-color: #7e7e7e" />
+ </template>
+
+ <template v-if="dbInbound.isTrojan">
+ <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
+ </template>
+
+ <template v-if="dbInbound.isSS">
+ <p>{{ i18n "encryption"}}: <a-tag color="green">[[ inbound.method ]]</a-tag></p>
+ <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
+ </template>
+
+ <template v-if="dbInbound.isSocks">
+ <p>{{ i18n "username"}}: <a-tag color="green">[[ inbound.username ]]</a-tag></p>
+ <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
+ </template>
+
+ <template v-if="dbInbound.isHTTP">
+ <p>{{ i18n "username"}}: <a-tag color="green">[[ inbound.username ]]</a-tag></p>
+ <p>{{ i18n "password"}}: <a-tag color="green">[[ inbound.password ]]</a-tag></p>
+ </template>
+
+ <template v-if="dbInbound.isVMess || dbInbound.isVLess || dbInbound.isTrojan || dbInbound.isSS">
+ {{template "inboundInfoStream"}}
+ </template>
+</div>
+{{end}}
+
+{{define "component/inboundInfo"}}
+<script>
+ Vue.component('inbound-info', {
+ delimiters: ['[[', ']]'],
+ props: ["dbInbound", "inbound"],
+ template: `{{template "component/inboundInfoComponent"}}`,
+ });
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/xui/component/setting.html b/web/html/xui/component/setting.html
new file mode 100644
index 00000000..9f8e8cbc
--- /dev/null
+++ b/web/html/xui/component/setting.html
@@ -0,0 +1,32 @@
+{{define "component/settingListItem"}}
+<a-list-item style="padding: 20px">
+ <a-row>
+ <a-col :lg="24" :xl="12">
+ <a-list-item-meta :title="title" :description="desc"/>
+ </a-col>
+ <a-col :lg="24" :xl="12">
+ <template v-if="type === 'text'">
+ <a-input :value="value" @input="$emit('input', $event.target.value)"></a-input>
+ </template>
+ <template v-else-if="type === 'number'">
+ <a-input type="number" :value="value" @input="$emit('input', $event.target.value)"></a-input>
+ </template>
+ <template v-else-if="type === 'textarea'">
+ <a-textarea :value="value" @input="$emit('input', $event.target.value)" :auto-size="{ minRows: 10, maxRows: 10 }"></a-textarea>
+ </template>
+ <template v-else-if="type === 'switch'">
+ <a-switch :checked="value" @change="value => $emit('input', value)"></a-switch>
+ </template>
+ </a-col>
+ </a-row>
+</a-list-item>
+{{end}}
+
+{{define "component/setting"}}
+<script>
+ Vue.component('setting-list-item', {
+ props: ["type", "title", "desc", "value"],
+ template: `{{template "component/settingListItem"}}`,
+ });
+</script>
+{{end}} \ No newline at end of file
diff --git a/web/html/xui/form/inbound.html b/web/html/xui/form/inbound.html
new file mode 100644
index 00000000..1a2e7ca1
--- /dev/null
+++ b/web/html/xui/form/inbound.html
@@ -0,0 +1,106 @@
+{{define "form/inbound"}}
+<!-- base -->
+<a-form layout="inline">
+ <a-form-item label='{{ i18n "remark" }}'>
+ <a-input v-model.trim="dbInbound.remark"></a-input>
+ </a-form-item>
+ <a-form-item label='{{ i18n "enable" }}'>
+ <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-option v-for="p in Protocols" :key="p" :value="p">[[ p ]]</a-select-option>
+ </a-select>
+ </a-form-item>
+ <a-form-item>
+ <span slot="label">
+ {{ i18n "monitor" }}
+ <a-tooltip>
+ <template slot="title">
+ <span>{{ i18n "pages.inbounds.monitorDesc" }}</span>
+ </template>
+ <a-icon type="question-circle" theme="filled"></a-icon>
+ </a-tooltip>
+ </span>
+ <a-input v-model.trim="inbound.listen"></a-input>
+ </a-form-item>
+ <a-form-item label='{{ i18n "pages.inbounds.port" }}'>
+ <a-input type="number" v-model.number="inbound.port"></a-input>
+ </a-form-item>
+ <a-form-item>
+ <span slot="label">

contacts: admin@thfree.ru

mirrors for living