Files
x-ui/web/html/xui/inbounds.html
2021-05-28 17:32:51 +08:00

288 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
{{template "head" .}}
<style>
.ant-layout-content {
margin: 24px 16px;
}
.ant-col-sm-24 {
margin-top: 10px;
}
</style>
<body>
<a-layout id="app" v-cloak>
{{ template "commonSider" . }}
<a-layout id="content-layout">
<a-layout-content>
<a-spin :spinning="spinning" :delay="500" tip="loading">
<transition name="list" appear>
<a-tag v-if="true" color="red" style="margin-bottom: 10px">
Please go to the panel settings as soon as possible to modify the username and password, otherwise there may be a risk of leaking account information
</a-tag>
</transition>
<transition name="list" appear>
<a-card hoverable style="margin-bottom: 20px;">
<a-row>
<a-col :xs="24" :sm="24" :lg="12">
upload / download
<a-tag color="green">[[ sizeFormat(total.up) ]] / [[ sizeFormat(total.down) ]]</a-tag>
</a-col>
<a-col :xs="24" :sm="24" :lg="12">
total traffic
<a-popconfirm title="Are you sure you want to reset all traffic to 0? It\'s unrecoverable"
@confirm="resetAllTraffic()"
ok-text="confirm" cancel-text="cancel">
<a-tag color="green">[[ sizeFormat(total.up + total.down) ]]</a-tag>
</a-popconfirm>
</a-col>
<a-col :xs="24" :sm="24" :lg="12">
number of accounts
<a-tag color="green">[[ dbInbounds.length ]]</a-tag>
</a-col>
</a-row>
</a-card>
</transition>
<transition name="list" appear>
<a-card hoverable>
<div slot="title">
<a-button type="primary" icon="plus" @click="openAddInbound"></a-button>
</div>
<a-input v-model="searchKey" placeholder="search" autofocus style="max-width: 300px"></a-input>
<a-table :columns="columns" :row-key="dbInbound => dbInbound.id"
:data-source="dbInbounds"
:loading="spinning" :scroll="{ x: 1500 }"
:pagination="false"
style="margin-top: 20px"
@change="() => getDBInbounds()">
<template slot="protocol" slot-scope="text, dbInbound">
<a-tag color="blue">[[ dbInbound.protocol ]]</a-tag>
</template>
<template slot="settings" slot-scope="text, dbInbound">
<a-button type="link">查看</a-button>
</template>
<template slot="streamSettings" slot-scope="text, dbInbound">
<a-button type="link">查看</a-button>
</template>
<template slot="enable" slot-scope="text, dbInbound">
<a-tag v-if="dbInbound.enable" color="green">启用</a-tag>
<a-tag v-else color="red">禁用</a-tag>
</template>
<template slot="expiryTime" slot-scope="text, dbInbound">
<span v-if="dbInbound.expiryTime > 0" color="red">[[ DateUtil.formatMillis(dbInbound.expiryTime) ]]</span>
<span v-else>无限期</span>
</template>
<template slot="action" slot-scope="text, dbInbound">
<a-button v-if="dbInbound.hasLink()" type="primary" icon="qrcode" @click="showQrcode(dbInbound)"></a-button>
<a-button type="primary" icon="edit" @click="openEditInbound(dbInbound)"></a-button>
<a-button type="danger" icon="delete" @click="delInbound(dbInbound)"></a-button>
</template>
</a-table>
</a-card>
</transition>
</a-spin>
</a-layout-content>
</a-layout>
</a-layout>
{{template "js" .}}
<script>
const columns = [{
title: "id",
align: 'center',
dataIndex: "id",
width: 60,
}, {
title: "protocol",
align: 'center',
width: 60,
scopedSlots: { customRender: 'protocol' },
}, {
title: "port",
align: 'center',
dataIndex: "port",
width: 60,
}, {
title: "settings",
align: 'center',
width: 60,
scopedSlots: { customRender: 'settings' },
}, {
title: "streamSettings",
align: 'center',
width: 60,
scopedSlots: { customRender: 'streamSettings' },
}, {
title: "enable",
align: 'center',
width: 60,
scopedSlots: { customRender: 'enable' },
}, {
title: "expiryTime",
align: 'center',
width: 60,
scopedSlots: { customRender: 'expiryTime' },
}, {
title: "action",
align: 'center',
width: 60,
scopedSlots: { customRender: 'action' },
}];
const app = new Vue({
delimiters: ['[[', ']]'],
el: '#app',
data: {
ip: location.hostname,
spinning: false,
dbInbounds: [],
searchKey: '',
},
methods: {
loading(spinning=true) {
this.spinning = spinning;
},
async getDBInbounds() {
this.loading();
const msg = await HttpUtil.post('/xui/inbounds');
this.loading(false);
if (!msg.success) {
return;
}
this.setInbounds(msg.obj);
},
setInbounds(dbInbounds) {
this.dbInbounds.splice(0);
for (const inbound of dbInbounds) {
this.dbInbounds.push(new DBInbound(inbound));
}
},
searchInbounds(key) {
if (ObjectUtil.isEmpty(key)) {
this.searchedInbounds = this.dbInbounds.slice();
} else {
this.searchedInbounds.splice(0, this.searchedInbounds.length);
this.dbInbounds.forEach(inbound => {
if (ObjectUtil.deepSearch(inbound, key)) {
this.searchedInbounds.push(inbound);
}
});
}
},
openAddInbound() {
inModal.show({
title: 'add account',
okText: 'add',
confirm: async (inbound, dbInbound) => {
inModal.loading();
await this.addInbound(inbound, dbInbound);
inModal.close();
}
});
},
openEditInbound(dbInbound) {
const inbound = dbInbound.toInbound();
inModal.show({
title: 'update account',
okText: 'update',
inbound: inbound,
dbInbound: dbInbound,
confirm: async (inbound, dbInbound) => {
inModal.loading();
await this.updateInbound(inbound, dbInbound);
inModal.close();
}
});
},
async addInbound(inbound, dbInbound) {
const data = {
remark: dbInbound.remark,
enable: dbInbound.enable,
listen: inbound.listen,
port: inbound.port,
protocol: inbound.protocol,
settings: inbound.settings.toString(),
stream_settings: inbound.stream.toString(),
sniffing: inbound.canSniffing() ? inbound.sniffing.toString() : '{}',
};
await this.submit('/xui/inbound/add', data, inModal);
},
async updateInbound(inbound, dbInbound) {
const data = {
remark: dbInbound.remark,
enable: dbInbound.enable,
listen: inbound.listen,
port: inbound.port,
protocol: inbound.protocol,
settings: inbound.settings.toString(),
stream_settings: inbound.stream.toString(),
sniffing: inbound.canSniffing() ? inbound.sniffing.toString() : '{}',
};
await this.submit(`/xui/inbound/update/${dbInbound.id}`, data, inModal);
},
delInbound(dbInbound) {
this.$confirm({
title: 'delete account',
content: 'Cannot be restored after deletion, confirm deletion?',
okText: 'delete',
cancelText: 'cancel',
onOk: () => this.submit('/xui/inbound/del/' + dbInbound.id),
});
},
showQrcode(dbInbound) {
let address = location.hostname;
if (!ObjectUtil.isEmpty(dbInbound.listen) || dbInbound.listen !== "0.0.0.0") {
address = dbInbound.listen;
}
const link = dbInbound.genLink(address);
qrModal.show('二维码', link);
},
resetTraffic(inbound) {
this.submit(`/xui/reset_traffic/${inbound.id}`);
},
resetAllTraffic() {
this.submit('/xui/reset_all_traffic');
},
setEnable(inbound, enable) {
let data = {enable: enable};
this.submit(`/xui/inbound/update/${inbound.id}`, data);
},
async submit(url, data, modal) {
const msg = await HttpUtil.postWithModal(url, data, modal);
if (msg.success) {
this.getDBInbounds();
}
},
},
watch: {
searchKey(value) {
this.searchInbounds(value);
}
},
mounted() {
this.getDBInbounds();
},
computed: {
total() {
let down = 0, up = 0;
for (let i = 0; i < this.dbInbounds.length; ++i) {
down += this.dbInbounds[i].down;
up += this.dbInbounds[i].up;
}
return {
down: down,
up: up,
};
}
},
});
</script>
{{template "inboundModal"}}
{{template "promptModal"}}
{{template "qrcodeModal"}}
{{template "textModal"}}
</body>
</html>