dashboard upgrade vue4

This commit is contained in:
hbrwang 2020-11-23 15:26:56 +08:00
parent 7adeeedd55
commit c8fc59b999
30 changed files with 10204 additions and 10514 deletions

View File

@ -1,14 +0,0 @@
{
"presets": [
["es2015", { "modules": false }]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}

5
web/frps/.editorconfig Normal file
View File

@ -0,0 +1,5 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -0,0 +1,2 @@
# just a flag
ENV = 'development'

2
web/frps/.env.production Normal file
View File

@ -0,0 +1,2 @@
# just a flag
ENV = 'production'

27
web/frps/.gitignore vendored
View File

@ -1,6 +1,25 @@
.DS_Store .DS_Store
node_modules/ node_modules
dist/ /dist
npm-debug.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea .idea
.vscode/settings.json .vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.package-lock.json

8
web/frps/.prettierrc Normal file
View File

@ -0,0 +1,8 @@
{
"bracketSpacing": true,
"printWidth": 160,
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"arrowParens": "avoid"
}

View File

@ -4,4 +4,4 @@ build:
@npm run build @npm run build
dev: install dev: install
@npm run dev @npm run serve

24
web/frps/README.md Normal file
View File

@ -0,0 +1,24 @@
# frps
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn serve
```
### Compiles and minifies for production
```
yarn build
```
### Lints and fixes files
```
yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

3
web/frps/babel.config.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
presets: ['@vue/cli-plugin-babel/preset']
}

File diff suppressed because it is too large Load Diff

View File

@ -4,45 +4,32 @@
"author": "fatedier", "author": "fatedier",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "webpack-dev-server -d --inline --hot --env.dev", "serve": "vue-cli-service serve",
"build": "rimraf dist && webpack -p --progress --hide-modules" "build": "vue-cli-service build"
}, },
"dependencies": { "dependencies": {
"bootstrap": "^3.3.7", "echarts": "^4.9.0",
"echarts": "^3.5.0", "element-ui": "^2.14.1",
"element-ui": "^2.3.8",
"humanize-plus": "^1.8.2", "humanize-plus": "^1.8.2",
"vue": "^2.5.16", "core-js": "^3.7.0",
"vue-resource": "^1.2.1", "vue": "^2.6.12",
"vue-router": "^2.3.0", "vue-router": "^3.4.9",
"whatwg-fetch": "^2.0.3" "whatwg-fetch": "^3.5.0"
},
"engines": {
"node": ">=6"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "^6.6.0", "@vue/cli-plugin-babel": "~4.5.9",
"babel-core": "^6.21.0", "@vue/cli-plugin-router": "~4.5.9",
"babel-eslint": "^7.1.1", "@vue/cli-plugin-vuex": "~4.5.9",
"babel-loader": "^6.4.0", "@vue/cli-service": "~4.5.9",
"babel-plugin-component": "^1.1.1", "node-sass": "^5.0.0",
"babel-preset-es2015": "^6.13.2", "sass-loader": "^10.1.0",
"css-loader": "^0.27.0", "less": "^3.12.2",
"eslint": "^3.12.2", "less-loader": "^7.1.0",
"eslint-config-enough": "^0.2.2", "vue-template-compiler": "^2.6.12"
"eslint-loader": "^1.6.3", },
"file-loader": "^0.10.1", "browserslist": [
"html-loader": "^0.4.5", "> 1%",
"html-webpack-plugin": "^2.24.1", "last 2 versions",
"less": "^3.0.4", "not dead"
"less-loader": "^4.1.0", ]
"postcss-loader": "^1.3.3",
"rimraf": "^2.5.4",
"style-loader": "^0.13.2",
"url-loader": "^1.0.1",
"vue-loader": "^15.0.10",
"vue-template-compiler": "^2.1.8",
"webpack": "^2.2.0-rc.4",
"webpack-dev-server": "^3.1.4"
}
} }

View File

@ -1,5 +0,0 @@
module.exports = {
plugins: [
require('autoprefixer')()
]
}

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -1,80 +1,79 @@
<template> <template>
<div id="app"> <div id="app">
<header class="grid-content header-color"> <header class="grid-content header-color">
<el-row> <el-row>
<a class="brand" href="#">frp</a> <a class="brand" href="#">frp</a>
</el-row> </el-row>
</header> </header>
<section> <section>
<el-row> <el-row>
<el-col id="side-nav" :xs="24" :md="4"> <el-col id="side-nav" :xs="24" :md="4">
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect"> <el-menu default-active="1" mode="vertical" theme="light" router @select="handleSelect">
<el-menu-item index="/">Overview</el-menu-item> <el-menu-item index="/">Overview</el-menu-item>
<el-submenu index="/proxies"> <el-submenu index="/proxies">
<template slot="title">Proxies</template> <template slot="title">Proxies</template>
<el-menu-item index="/proxies/tcp">TCP</el-menu-item> <el-menu-item index="/proxies/tcp">TCP</el-menu-item>
<el-menu-item index="/proxies/udp">UDP</el-menu-item> <el-menu-item index="/proxies/udp">UDP</el-menu-item>
<el-menu-item index="/proxies/http">HTTP</el-menu-item> <el-menu-item index="/proxies/http">HTTP</el-menu-item>
<el-menu-item index="/proxies/https">HTTPS</el-menu-item> <el-menu-item index="/proxies/https">HTTPS</el-menu-item>
<el-menu-item index="/proxies/stcp">STCP</el-menu-item> <el-menu-item index="/proxies/stcp">STCP</el-menu-item>
</el-submenu> </el-submenu>
<el-menu-item index="">Help</el-menu-item> <el-menu-item index="">Help</el-menu-item>
</el-menu> </el-menu>
</el-col> </el-col>
<el-col :xs="24" :md="20"> <el-col :xs="24" :md="20">
<div id="content"> <div id="content">
<router-view></router-view> <router-view />
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
</section> </section>
<footer></footer> </div>
</div>
</template> </template>
<script> <script>
export default { export default {
methods: { methods: {
handleSelect(key, path) { handleSelect(key, path) {
if (key == '') { if (key === '') {
window.open("https://github.com/fatedier/frp") window.open('https://github.com/fatedier/frp')
} }
}
}
} }
}
}
</script> </script>
<style> <style>
body { body {
background-color: #fafafa; background-color: #fafafa;
margin: 0px; margin: 0px;
font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,sans-serif; font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, sans-serif;
} }
header { header {
width: 100%; width: 100%;
height: 60px; height: 60px;
} }
.header-color { .header-color {
background: #58B7FF; background: #58b7ff;
} }
#content { #content {
margin-top: 20px; margin-top: 20px;
padding-right: 40px; padding-right: 40px;
} }
.brand { .brand {
color: #fff; color: #fff;
background-color: transparent; background-color: transparent;
margin-left: 20px; margin-left: 20px;
float: left; float: left;
line-height: 25px; line-height: 25px;
font-size: 25px; font-size: 25px;
padding: 15px 15px; padding: 15px 15px;
height: 30px; height: 30px;
text-decoration: none; text-decoration: none;
} }
</style> </style>

View File

@ -1,166 +1,169 @@
<template> <template>
<div> <div>
<el-row> <el-row>
<el-col :md="12"> <el-col :md="12">
<div class="source"> <div class="source">
<el-form label-position="left" class="server_info"> <el-form label-position="left" class="server_info">
<el-form-item label="Version"> <el-form-item label="Version">
<span>{{ version }}</span> <span>{{ version }}</span>
</el-form-item> </el-form-item>
<el-form-item label="BindPort"> <el-form-item label="BindPort">
<span>{{ bind_port }}</span> <span>{{ bind_port }}</span>
</el-form-item> </el-form-item>
<el-form-item label="BindUdpPort"> <el-form-item label="BindUdpPort">
<span>{{ bind_udp_port }}</span> <span>{{ bind_udp_port }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Http Port"> <el-form-item label="Http Port">
<span>{{ vhost_http_port }}</span> <span>{{ vhost_http_port }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Https Port"> <el-form-item label="Https Port">
<span>{{ vhost_https_port }}</span> <span>{{ vhost_https_port }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Subdomain Host"> <el-form-item label="Subdomain Host">
<span>{{ subdomain_host }}</span> <span>{{ subdomain_host }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Max PoolCount"> <el-form-item label="Max PoolCount">
<span>{{ max_pool_count }}</span> <span>{{ max_pool_count }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Max Ports Per Client"> <el-form-item label="Max Ports Per Client">
<span>{{ max_ports_per_client }}</span> <span>{{ max_ports_per_client }}</span>
</el-form-item> </el-form-item>
<el-form-item label="HeartBeat Timeout"> <el-form-item label="HeartBeat Timeout">
<span>{{ heart_beat_timeout }}</span> <span>{{ heart_beat_timeout }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Client Counts"> <el-form-item label="Client Counts">
<span>{{ client_counts }}</span> <span>{{ client_counts }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Current Connections"> <el-form-item label="Current Connections">
<span>{{ cur_conns }}</span> <span>{{ cur_conns }}</span>
</el-form-item> </el-form-item>
<el-form-item label="Proxy Counts"> <el-form-item label="Proxy Counts">
<span>{{ proxy_counts }}</span> <span>{{ proxy_counts }}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</el-col> </el-col>
<el-col :md="12"> <el-col :md="12">
<div id="traffic" style="width: 400px;height:250px;margin-bottom: 30px;"></div> <div id="traffic" style="width: 400px; height: 250px; margin-bottom: 30px" />
<div id="proxies" style="width: 400px;height:250px;"></div> <div id="proxies" style="width: 400px; height: 250px" />
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>
<script> <script>
import {DrawTrafficChart, DrawProxyChart} from '../utils/chart.js' import { DrawTrafficChart, DrawProxyChart } from '../utils/chart.js'
export default { export default {
data() { data() {
return { return {
version: '', version: '',
bind_port: '', bind_port: '',
bind_udp_port: '', bind_udp_port: '',
vhost_http_port: '', vhost_http_port: '',
vhost_https_port: '', vhost_https_port: '',
subdomain_host: '', subdomain_host: '',
max_pool_count: '', max_pool_count: '',
max_ports_per_client: '', max_ports_per_client: '',
heart_beat_timeout: '', heart_beat_timeout: '',
client_counts: '', client_counts: '',
cur_conns: '', cur_conns: '',
proxy_counts: '' proxy_counts: ''
}
},
created() {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData() {
fetch('/api/serverinfo', {credentials: 'include'})
.then(res => {
return res.json()
}).then(json => {
this.version = json.version
this.bind_port = json.bind_port
this.bind_udp_port = json.bind_udp_port
if (this.bind_udp_port == 0) {
this.bind_udp_port = "disable"
}
this.vhost_http_port = json.vhost_http_port
if (this.vhost_http_port == 0) {
this.vhost_http_port = "disable"
}
this.vhost_https_port = json.vhost_https_port
if (this.vhost_https_port == 0) {
this.vhost_https_port = "disable"
}
this.subdomain_host = json.subdomain_host
this.max_pool_count = json.max_pool_count
this.max_ports_per_client = json.max_ports_per_client
if (this.max_ports_per_client == 0) {
this.max_ports_per_client = "no limit"
}
this.heart_beat_timeout = json.heart_beat_timeout
this.client_counts = json.client_counts
this.cur_conns = json.cur_conns
this.proxy_counts = 0
if (json.proxy_type_count != null) {
if (json.proxy_type_count.tcp != null) {
this.proxy_counts += json.proxy_type_count.tcp
}
if (json.proxy_type_count.udp != null) {
this.proxy_counts += json.proxy_type_count.udp
}
if (json.proxy_type_count.http != null) {
this.proxy_counts += json.proxy_type_count.http
}
if (json.proxy_type_count.https != null) {
this.proxy_counts += json.proxy_type_count.https
}
if (json.proxy_type_count.stcp != null) {
this.proxy_counts += json.proxy_type_count.stcp
}
if (json.proxy_type_count.xtcp != null) {
this.proxy_counts += json.proxy_type_count.xtcp
}
}
DrawTrafficChart('traffic', json.total_traffic_in, json.total_traffic_out)
DrawProxyChart('proxies', json)
}).catch( err => {
this.$message({
showClose: true,
message: 'Get server info from frps failed!',
type: 'warning'
})
})
}
}
} }
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
fetchData() {
fetch('/api/serverinfo', { credentials: 'include' })
.then(res => {
return res.json()
})
.then(json => {
this.version = json.version
this.bind_port = json.bind_port
this.bind_udp_port = json.bind_udp_port
if (this.bind_udp_port === 0) {
this.bind_udp_port = 'disable'
}
this.vhost_http_port = json.vhost_http_port
if (this.vhost_http_port === 0) {
this.vhost_http_port = 'disable'
}
this.vhost_https_port = json.vhost_https_port
if (this.vhost_https_port === 0) {
this.vhost_https_port = 'disable'
}
this.subdomain_host = json.subdomain_host
this.max_pool_count = json.max_pool_count
this.max_ports_per_client = json.max_ports_per_client
if (this.max_ports_per_client === 0) {
this.max_ports_per_client = 'no limit'
}
this.heart_beat_timeout = json.heart_beat_timeout
this.client_counts = json.client_counts
this.cur_conns = json.cur_conns
this.proxy_counts = 0
if (json.proxy_type_count != null) {
if (json.proxy_type_count.tcp != null) {
this.proxy_counts += json.proxy_type_count.tcp
}
if (json.proxy_type_count.udp != null) {
this.proxy_counts += json.proxy_type_count.udp
}
if (json.proxy_type_count.http != null) {
this.proxy_counts += json.proxy_type_count.http
}
if (json.proxy_type_count.https != null) {
this.proxy_counts += json.proxy_type_count.https
}
if (json.proxy_type_count.stcp != null) {
this.proxy_counts += json.proxy_type_count.stcp
}
if (json.proxy_type_count.xtcp != null) {
this.proxy_counts += json.proxy_type_count.xtcp
}
}
DrawTrafficChart('traffic', json.total_traffic_in, json.total_traffic_out)
DrawProxyChart('proxies', json)
})
.catch(err => {
this.$message({
showClose: true,
message: 'Get server info from frps failed!',
type: 'warning'
})
return err
})
}
}
}
</script> </script>
<style> <style>
.source { .source {
border: 1px solid #eaeefb; border: 1px solid #eaeefb;
border-radius: 4px; border-radius: 4px;
transition: .2s; transition: 0.2s;
padding: 24px; padding: 24px;
} }
.server_info { .server_info {
margin-left: 40px; margin-left: 40px;
font-size: 0px; font-size: 0px;
} }
.server_info label { .server_info label {
width: 150px; width: 150px;
color: #99a9bf; color: #99a9bf;
} }
.server_info .el-form-item { .server_info .el-form-item {
margin-right: 0; margin-right: 0;
margin-bottom: 0; margin-bottom: 0;
width: 100%; width: 100%;
} }
</style> </style>

View File

@ -1,18 +1,13 @@
<template> <template>
<div> <div>
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%"> <el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
<el-table-column type="expand"> <el-table-column type="expand">
<template slot-scope="props"> <template slot-scope="props">
<el-popover <el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
ref="popover4" <my-traffic-chart :proxy-name="props.row.name" />
placement="right"
width="600"
style="margin-left:0px"
trigger="click">
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
</el-popover> </el-popover>
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom:10px">Traffic Statistics</el-button> <el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
<el-form label-position="left" inline class="demo-table-expand"> <el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="Name"> <el-form-item label="Name">
@ -45,103 +40,78 @@
<el-form-item label="Last Close"> <el-form-item label="Last Close">
<span>{{ props.row.last_close_time }}</span> <span>{{ props.row.last_close_time }}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="Name" prop="name" sortable />
label="Name" <el-table-column label="Port" prop="port" sortable />
prop="name" <el-table-column label="Connections" prop="conns" sortable />
sortable> <el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
</el-table-column> <el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
<el-table-column <el-table-column label="status" prop="status" sortable>
label="Port" <template slot-scope="scope">
prop="port" <el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
sortable> <el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
</el-table-column> </template>
<el-table-column </el-table-column>
label="Connections" </el-table>
prop="conns" </div>
sortable>
</el-table-column>
<el-table-column
label="Traffic In"
prop="traffic_in"
:formatter="formatTrafficIn"
sortable>
</el-table-column>
<el-table-column
label="Traffic Out"
prop="traffic_out"
:formatter="formatTrafficOut"
sortable>
</el-table-column>
<el-table-column
label="status"
prop="status"
sortable>
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template> </template>
<script> <script>
import Humanize from 'humanize-plus'; import Humanize from 'humanize-plus'
import Traffic from './Traffic.vue' import Traffic from './Traffic.vue'
import { import { HttpProxy } from '../utils/proxy.js'
HttpProxy export default {
} from '../utils/proxy.js' components: {
export default { 'my-traffic-chart': Traffic
data() { },
return { data() {
proxies: null, return {
vhost_http_port: "", proxies: [],
subdomain_host: "" vhost_http_port: '',
} subdomain_host: ''
}
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
formatTrafficIn(row, column) {
return Humanize.fileSize(row.traffic_in)
}, },
created() { formatTrafficOut(row, column) {
this.fetchData() return Humanize.fileSize(row.traffic_out)
}, },
watch: { fetchData() {
'$route': 'fetchData' fetch('/api/serverinfo', { credentials: 'include' })
}, .then(res => {
methods: { return res.json()
formatTrafficIn(row, column) { })
return Humanize.fileSize(row.traffic_in) .then(json => {
}, this.vhost_http_port = json.vhost_http_port
formatTrafficOut(row, column) { this.subdomain_host = json.subdomain_host
return Humanize.fileSize(row.traffic_out) if (this.vhost_http_port == null || this.vhost_http_port === 0) {
}, return
fetchData() { } else {
fetch('/api/serverinfo', {credentials: 'include'}) fetch('/api/proxy/http', { credentials: 'include' })
.then(res => { .then(res => {
return res.json() return res.json()
}).then(json => { })
this.vhost_http_port = json.vhost_http_port .then(json => {
this.subdomain_host = json.subdomain_host this.proxies = []
if (this.vhost_http_port == null || this.vhost_http_port == 0) { for (const proxyStats of json.proxies) {
return this.proxies.push(new HttpProxy(proxyStats, this.vhost_http_port, this.subdomain_host))
} else { }
fetch('/api/proxy/http', {credentials: 'include'}) })
.then(res => { }
return res.json() })
}).then(json => {
this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new HttpProxy(proxyStats, this.vhost_http_port, this.subdomain_host))
}
})
}
})
}
},
components: {
'my-traffic-chart': Traffic
} }
} }
}
</script> </script>
<style> <style>

View File

@ -1,18 +1,13 @@
<template> <template>
<div> <div>
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%"> <el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
<el-table-column type="expand"> <el-table-column type="expand">
<template slot-scope="props"> <template slot-scope="props">
<el-popover <el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
ref="popover4" <my-traffic-chart :proxy-name="props.row.name" />
placement="right"
width="600"
style="margin-left:0px"
trigger="click">
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
</el-popover> </el-popover>
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom:10px">Traffic Statistics</el-button> <el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
<el-form label-position="left" inline class="demo-table-expand"> <el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="Name"> <el-form-item label="Name">
@ -39,104 +34,78 @@
<el-form-item label="Last Close"> <el-form-item label="Last Close">
<span>{{ props.row.last_close_time }}</span> <span>{{ props.row.last_close_time }}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="Name" prop="name" sortable />
label="Name" <el-table-column label="Port" prop="port" sortable />
prop="name" <el-table-column label="Connections" prop="conns" sortable />
sortable> <el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
</el-table-column> <el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
<el-table-column <el-table-column label="status" prop="status" sortable>
label="Port" <template slot-scope="scope">
prop="port" <el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
sortable> <el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
</el-table-column> </template>
<el-table-column </el-table-column>
label="Connections" </el-table>
prop="conns" </div>
sortable>
</el-table-column>
<el-table-column
label="Traffic In"
prop="traffic_in"
:formatter="formatTrafficIn"
sortable>
</el-table-column>
<el-table-column
label="Traffic Out"
prop="traffic_out"
:formatter="formatTrafficOut"
sortable>
</el-table-column>
<el-table-column
label="status"
prop="status"
sortable>
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template> </template>
<script> <script>
import Humanize from 'humanize-plus'; import Humanize from 'humanize-plus'
import Traffic from './Traffic.vue' import Traffic from './Traffic.vue'
import { import { HttpsProxy } from '../utils/proxy.js'
HttpsProxy export default {
} from '../utils/proxy.js' components: {
export default { 'my-traffic-chart': Traffic
data() { },
return { data() {
proxies: null, return {
vhost_https_port: '', proxies: [],
subdomain_host: '' vhost_https_port: '',
} subdomain_host: ''
}
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
formatTrafficIn(row, column) {
return Humanize.fileSize(row.traffic_in)
}, },
created() { formatTrafficOut(row, column) {
this.fetchData() return Humanize.fileSize(row.traffic_out)
}, },
watch: { fetchData() {
'$route': 'fetchData' fetch('/api/serverinfo', { credentials: 'include' })
}, .then(res => {
methods: { return res.json()
formatTrafficIn(row, column) { })
return Humanize.fileSize(row.traffic_in) .then(json => {
}, this.vhost_https_port = json.vhost_https_port
formatTrafficOut(row, column) { this.subdomain_host = json.subdomain_host
return Humanize.fileSize(row.traffic_out) if (this.vhost_https_port == null || this.vhost_https_port === 0) {
}, return
fetchData() { } else {
fetch('/api/serverinfo', {credentials: 'include'}) fetch('/api/proxy/https', { credentials: 'include' })
.then(res => { .then(res => {
return res.json() return res.json()
}).then(json => { })
this.vhost_https_port = json.vhost_https_port .then(json => {
this.subdomain_host = json.subdomain_host this.proxies = []
if (this.vhost_https_port == null || this.vhost_https_port == 0) { for (const proxyStats of json.proxies) {
return this.proxies.push(new HttpsProxy(proxyStats, this.vhost_https_port, this.subdomain_host))
} else { }
fetch('/api/proxy/https', {credentials: 'include'}) })
.then(res => { }
return res.json() })
}).then(json => {
this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new HttpsProxy(proxyStats, this.vhost_https_port, this.subdomain_host))
}
})
}
})
}
},
components: {
'my-traffic-chart': Traffic
} }
} }
}
</script> </script>
<style> <style>

View File

@ -1,18 +1,15 @@
<template> <template>
<div> <div>
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%"> <el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
<el-table-column type="expand"> <el-table-column type="expand">
<template slot-scope="props"> <template slot-scope="props">
<el-popover <el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
ref="popover4" <my-traffic-chart :proxy-name="props.row.name" />
placement="right"
width="600"
style="margin-left:0px"
trigger="click">
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
</el-popover> </el-popover>
<el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom:10px" @click="fetchData2">Traffic Statistics</el-button> <el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom: 10px" @click="fetchData2">
Traffic Statistics
</el-button>
<el-form label-position="left" inline class="demo-table-expand"> <el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="Name"> <el-form-item label="Name">
@ -33,83 +30,63 @@
<el-form-item label="Last Close"> <el-form-item label="Last Close">
<span>{{ props.row.last_close_time }}</span> <span>{{ props.row.last_close_time }}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="Name" prop="name" sortable />
label="Name" <el-table-column label="Connections" prop="conns" sortable />
prop="name" <el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
sortable> <el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
</el-table-column> <el-table-column label="status" prop="status" sortable>
<el-table-column <template slot-scope="scope">
label="Connections" <el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
prop="conns" <el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
sortable> </template>
</el-table-column> </el-table-column>
<el-table-column </el-table>
label="Traffic In" </div>
prop="traffic_in"
:formatter="formatTrafficIn"
sortable>
</el-table-column>
<el-table-column
label="Traffic Out"
prop="traffic_out"
:formatter="formatTrafficOut"
sortable>
</el-table-column>
<el-table-column
label="status"
prop="status"
sortable>
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template> </template>
<script> <script>
import Humanize from 'humanize-plus' import Humanize from 'humanize-plus'
import Traffic from './Traffic.vue' import Traffic from './Traffic.vue'
import { StcpProxy } from '../utils/proxy.js' import { StcpProxy } from '../utils/proxy.js'
export default { export default {
data() { components: {
return { 'my-traffic-chart': Traffic
proxies: null },
} data() {
return {
proxies: []
}
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
formatTrafficIn(row, column) {
return Humanize.fileSize(row.traffic_in)
}, },
created() { formatTrafficOut(row, column) {
this.fetchData() return Humanize.fileSize(row.traffic_out)
}, },
watch: { fetchData() {
'$route': 'fetchData' fetch('/api/proxy/stcp', { credentials: 'include' })
}, .then(res => {
methods: { return res.json()
formatTrafficIn(row, column) { })
return Humanize.fileSize(row.traffic_in) .then(json => {
}, this.proxies = []
formatTrafficOut(row, column) { for (const proxyStats of json.proxies) {
return Humanize.fileSize(row.traffic_out) this.proxies.push(new StcpProxy(proxyStats))
}, }
fetchData() { })
fetch('/api/proxy/stcp', {credentials: 'include'})
.then(res => {
return res.json()
}).then(json => {
this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new StcpProxy(proxyStats))
}
})
}
},
components: {
'my-traffic-chart': Traffic
} }
} }
}
</script> </script>
<style> <style>

View File

@ -1,18 +1,15 @@
<template> <template>
<div> <div>
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%"> <el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
<el-table-column type="expand"> <el-table-column type="expand">
<template slot-scope="props"> <template slot-scope="props">
<el-popover <el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
ref="popover4" <my-traffic-chart :proxy-name="props.row.name" />
placement="right"
width="600"
style="margin-left:0px"
trigger="click">
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
</el-popover> </el-popover>
<el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom:10px" @click="fetchData2">Traffic Statistics</el-button> <el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom: 10px" @click="fetchData2">
Traffic Statistics
</el-button>
<el-form label-position="left" inline class="demo-table-expand"> <el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="Name"> <el-form-item label="Name">
@ -36,88 +33,64 @@
<el-form-item label="Last Close"> <el-form-item label="Last Close">
<span>{{ props.row.last_close_time }}</span> <span>{{ props.row.last_close_time }}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="Name" prop="name" sortable />
label="Name" <el-table-column label="Port" prop="port" sortable />
prop="name" <el-table-column label="Connections" prop="conns" sortable />
sortable> <el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
</el-table-column> <el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
<el-table-column <el-table-column label="status" prop="status" sortable>
label="Port" <template slot-scope="scope">
prop="port" <el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
sortable> <el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
</el-table-column> </template>
<el-table-column </el-table-column>
label="Connections" </el-table>
prop="conns" </div>
sortable>
</el-table-column>
<el-table-column
label="Traffic In"
prop="traffic_in"
:formatter="formatTrafficIn"
sortable>
</el-table-column>
<el-table-column
label="Traffic Out"
prop="traffic_out"
:formatter="formatTrafficOut"
sortable>
</el-table-column>
<el-table-column
label="status"
prop="status"
sortable>
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template> </template>
<script> <script>
import Humanize from 'humanize-plus' import Humanize from 'humanize-plus'
import Traffic from './Traffic.vue' import Traffic from './Traffic.vue'
import { TcpProxy } from '../utils/proxy.js' import { TcpProxy } from '../utils/proxy.js'
export default { export default {
data() { components: {
return { 'my-traffic-chart': Traffic
proxies: null },
} data() {
return {
proxies: []
}
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
formatTrafficIn(row, column) {
return Humanize.fileSize(row.traffic_in)
}, },
created() { formatTrafficOut(row, column) {
this.fetchData() return Humanize.fileSize(row.traffic_out)
}, },
watch: { fetchData() {
'$route': 'fetchData' fetch('/api/proxy/tcp', { credentials: 'include' })
}, .then(res => {
methods: { return res.json()
formatTrafficIn(row, column) { })
return Humanize.fileSize(row.traffic_in) .then(json => {
}, this.proxies = []
formatTrafficOut(row, column) { for (const proxyStats of json.proxies) {
return Humanize.fileSize(row.traffic_out) this.proxies.push(new TcpProxy(proxyStats))
}, }
fetchData() { })
fetch('/api/proxy/tcp', {credentials: 'include'})
.then(res => {
return res.json()
}).then(json => {
this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new TcpProxy(proxyStats))
}
})
}
},
components: {
'my-traffic-chart': Traffic
} }
} }
}
</script> </script>
<style> <style>

View File

@ -1,18 +1,13 @@
<template> <template>
<div> <div>
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%"> <el-table :data="proxies" :default-sort="{ prop: 'name', order: 'ascending' }" style="width: 100%">
<el-table-column type="expand"> <el-table-column type="expand">
<template slot-scope="props"> <template slot-scope="props">
<el-popover <el-popover ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click">
ref="popover4" <my-traffic-chart :proxy-name="props.row.name" />
placement="right"
width="600"
style="margin-left:0px"
trigger="click">
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
</el-popover> </el-popover>
<el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom:10px">Traffic Statistics</el-button> <el-button v-popover:popover4 type="primary" size="small" icon="view" style="margin-bottom: 10px">Traffic Statistics</el-button>
<el-form label-position="left" inline class="demo-table-expand"> <el-form label-position="left" inline class="demo-table-expand">
<el-form-item label="Name"> <el-form-item label="Name">
@ -36,90 +31,64 @@
<el-form-item label="Last Close"> <el-form-item label="Last Close">
<span>{{ props.row.last_close_time }}</span> <span>{{ props.row.last_close_time }}</span>
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="Name" prop="name" sortable />
label="Name" <el-table-column label="Port" prop="port" sortable />
prop="name" <el-table-column label="Connections" prop="conns" sortable />
sortable> <el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable />
</el-table-column> <el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable />
<el-table-column <el-table-column label="status" prop="status" sortable>
label="Port" <template slot-scope="scope">
prop="port" <el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag>
sortable> <el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
</el-table-column> </template>
<el-table-column </el-table-column>
label="Connections" </el-table>
prop="conns" </div>
sortable>
</el-table-column>
<el-table-column
label="Traffic In"
prop="traffic_in"
:formatter="formatTrafficIn"
sortable>
</el-table-column>
<el-table-column
label="Traffic Out"
prop="traffic_out"
:formatter="formatTrafficOut"
sortable>
</el-table-column>
<el-table-column
label="status"
prop="status"
sortable>
<template slot-scope="scope">
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template> </template>
<script> <script>
import Humanize from 'humanize-plus'; import Humanize from 'humanize-plus'
import Traffic from './Traffic.vue' import Traffic from './Traffic.vue'
import { import { UdpProxy } from '../utils/proxy.js'
UdpProxy export default {
} from '../utils/proxy.js' components: {
export default { 'my-traffic-chart': Traffic
data() { },
return { data() {
proxies: null return {
} proxies: []
}
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
formatTrafficIn(row, column) {
return Humanize.fileSize(row.traffic_in)
}, },
created() { formatTrafficOut(row, column) {
this.fetchData() return Humanize.fileSize(row.traffic_out)
}, },
watch: { fetchData() {
'$route': 'fetchData' fetch('/api/proxy/udp', { credentials: 'include' })
}, .then(res => {
methods: { return res.json()
formatTrafficIn(row, column) { })
return Humanize.fileSize(row.traffic_in) .then(json => {
}, this.proxies = []
formatTrafficOut(row, column) { for (const proxyStats of json.proxies) {
return Humanize.fileSize(row.traffic_out) this.proxies.push(new UdpProxy(proxyStats))
}, }
fetchData() { })
fetch('/api/proxy/udp', {credentials: 'include'})
.then(res => {
return res.json()
}).then(json => {
this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new UdpProxy(proxyStats))
}
})
}
},
components: {
'my-traffic-chart': Traffic
} }
} }
}
</script> </script>
<style> <style>

View File

@ -1,34 +1,41 @@
<template> <template>
<div :id="proxy_name" style="width: 600px;height:400px;"></div> <div :id="proxyName" style="width: 600px; height: 400px" />
</template> </template>
<script> <script>
import {DrawProxyTrafficChart} from '../utils/chart.js' import { DrawProxyTrafficChart } from '../utils/chart.js'
export default { export default {
props: ['proxy_name'], props: {
created() { proxyName: {
this.fetchData() type: String,
}, required: true
//watch: {
//'$route': 'fetchData'
//},
methods: {
fetchData() {
let url = '/api/traffic/' + this.proxy_name
fetch(url, {credentials: 'include'})
.then(res => {
return res.json()
}).then(json => {
DrawProxyTrafficChart(this.proxy_name, json.traffic_in, json.traffic_out)
}).catch( err => {
this.$message({
showClose: true,
message: 'Get server info from frps failed!' + err,
type: 'warning'
})
})
}
} }
},
created() {
this.fetchData()
},
// watch: {
// '$route': 'fetchData'
// },
methods: {
fetchData() {
const url = '/api/traffic/' + this.proxyName
fetch(url, { credentials: 'include' })
.then(res => {
return res.json()
})
.then(json => {
DrawProxyTrafficChart(this.proxyName, json.traffic_in, json.traffic_out)
})
.catch(err => {
this.$message({
showClose: true,
message: 'Get server info from frps failed!' + err,
type: 'warning'
})
})
}
}
} }
</script> </script>

View File

@ -1,15 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>frps dashboard</title>
</head>
<body>
<div id="app"></div>
<!--<script src="https://code.jquery.com/jquery-3.2.0.min.js"></script>-->
<!--<script src="//cdn.bootcss.com/echarts/3.4.0/echarts.min.js"></script>-->
</body>
</html>

View File

@ -1,19 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
//import ElementUI from 'element-ui' // import ElementUI from 'element-ui'
import { import { Button, Form, FormItem, Row, Col, Table, TableColumn, Popover, Menu, Submenu, MenuItem, Tag } from 'element-ui'
Button,
Form,
FormItem,
Row,
Col,
Table,
TableColumn,
Popover,
Menu,
Submenu,
MenuItem,
Tag
} from 'element-ui'
import lang from 'element-ui/lib/locale/lang/en' import lang from 'element-ui/lib/locale/lang/en'
import locale from 'element-ui/lib/locale' import locale from 'element-ui/lib/locale'
import 'element-ui/lib/theme-chalk/index.css' import 'element-ui/lib/theme-chalk/index.css'
@ -41,8 +28,6 @@ Vue.use(Tag)
Vue.config.productionTip = false Vue.config.productionTip = false
new Vue({ new Vue({
el: '#app', router,
router, render: h => h(App)
template: '<App/>', }).$mount('#app')
components: { App }
})

View File

@ -10,29 +10,36 @@ import ProxiesStcp from '../components/ProxiesStcp.vue'
Vue.use(Router) Vue.use(Router)
export default new Router({ export default new Router({
routes: [{ routes: [
path: '/', {
name: 'Overview', path: '/',
component: Overview name: 'Overview',
}, { component: Overview
path: '/proxies/tcp', },
name: 'ProxiesTcp', {
component: ProxiesTcp path: '/proxies/tcp',
}, { name: 'ProxiesTcp',
path: '/proxies/udp', component: ProxiesTcp
name: 'ProxiesUdp', },
component: ProxiesUdp {
}, { path: '/proxies/udp',
path: '/proxies/http', name: 'ProxiesUdp',
name: 'ProxiesHttp', component: ProxiesUdp
component: ProxiesHttp },
}, { {
path: '/proxies/https', path: '/proxies/http',
name: 'ProxiesHttps', name: 'ProxiesHttp',
component: ProxiesHttps component: ProxiesHttp
}, { },
path: '/proxies/stcp', {
name: 'ProxiesStcp', path: '/proxies/https',
component: ProxiesStcp name: 'ProxiesHttps',
}] component: ProxiesHttps
},
{
path: '/proxies/stcp',
name: 'ProxiesStcp',
component: ProxiesStcp
}
]
}) })

View File

@ -1,199 +1,215 @@
import Humanize from "humanize-plus" import Humanize from 'humanize-plus'
import echarts from "echarts/lib/echarts" import echarts from 'echarts/lib/echarts'
import "echarts/theme/macarons" import 'echarts/theme/macarons'
import "echarts/lib/chart/bar" import 'echarts/lib/chart/bar'
import "echarts/lib/chart/pie" import 'echarts/lib/chart/pie'
import "echarts/lib/component/tooltip" import 'echarts/lib/component/tooltip'
import "echarts/lib/component/title" import 'echarts/lib/component/title'
function DrawTrafficChart(elementId, trafficIn, trafficOut) { function DrawTrafficChart(elementId, trafficIn, trafficOut) {
let myChart = echarts.init(document.getElementById(elementId), 'macarons'); const myChart = echarts.init(document.getElementById(elementId), 'macarons')
myChart.showLoading() myChart.showLoading()
let option = { const option = {
title: { title: {
text: 'Network Traffic', text: 'Network Traffic',
subtext: 'today', subtext: 'today',
x: 'center' x: 'center'
}, },
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
formatter: function(v) { formatter: function(v) {
return Humanize.fileSize(v.data.value) + " (" + v.percent + "%)" return Humanize.fileSize(v.data.value) + ' (' + v.percent + '%)'
} }
}, },
series: [{ series: [
type: 'pie', {
radius: '55%', type: 'pie',
center: ['50%', '60%'], radius: '55%',
data: [{ center: ['50%', '60%'],
value: trafficIn, data: [
name: 'Traffic In' {
}, { value: trafficIn,
value: trafficOut, name: 'Traffic In'
name: 'Traffic Out' },
}, ], {
itemStyle: { value: trafficOut,
emphasis: { name: 'Traffic Out'
shadowBlur: 10, }
shadowOffsetX: 0, ],
shadowColor: 'rgba(0, 0, 0, 0.5)' itemStyle: {
} emphasis: {
} shadowBlur: 10,
}] shadowOffsetX: 0,
}; shadowColor: 'rgba(0, 0, 0, 0.5)'
myChart.setOption(option); }
myChart.hideLoading() }
}
]
}
myChart.setOption(option)
myChart.hideLoading()
} }
function DrawProxyChart(elementId, serverInfo) { function DrawProxyChart(elementId, serverInfo) {
if (serverInfo.proxy_type_count.tcp == null) { if (serverInfo.proxy_type_count.tcp == null) {
serverInfo.proxy_type_count.tcp = 0 serverInfo.proxy_type_count.tcp = 0
} }
if (serverInfo.proxy_type_count.udp == null) { if (serverInfo.proxy_type_count.udp == null) {
serverInfo.proxy_type_count.udp = 0 serverInfo.proxy_type_count.udp = 0
} }
if (serverInfo.proxy_type_count.http == null) { if (serverInfo.proxy_type_count.http == null) {
serverInfo.proxy_type_count.http = 0 serverInfo.proxy_type_count.http = 0
} }
if (serverInfo.proxy_type_count.https == null) { if (serverInfo.proxy_type_count.https == null) {
serverInfo.proxy_type_count.https = 0 serverInfo.proxy_type_count.https = 0
} }
if (serverInfo.proxy_type_count.stcp == null) { if (serverInfo.proxy_type_count.stcp == null) {
serverInfo.proxy_type_count.stcp = 0 serverInfo.proxy_type_count.stcp = 0
} }
if (serverInfo.proxy_type_count.xtcp == null) { if (serverInfo.proxy_type_count.xtcp == null) {
serverInfo.proxy_type_count.xtcp = 0 serverInfo.proxy_type_count.xtcp = 0
} }
let myChart = echarts.init(document.getElementById(elementId), 'macarons') const myChart = echarts.init(document.getElementById(elementId), 'macarons')
myChart.showLoading() myChart.showLoading()
let option = { const option = {
title: { title: {
text: 'Proxies', text: 'Proxies',
subtext: 'now', subtext: 'now',
x: 'center' x: 'center'
}, },
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
formatter: function(v) { formatter: function(v) {
return v.data.value return v.data.value
} }
}, },
series: [{ series: [
type: 'pie', {
radius: '55%', type: 'pie',
center: ['50%', '60%'], radius: '55%',
data: [{ center: ['50%', '60%'],
value: serverInfo.proxy_type_count.tcp, data: [
name: 'TCP' {
}, { value: serverInfo.proxy_type_count.tcp,
value: serverInfo.proxy_type_count.udp, name: 'TCP'
name: 'UDP' },
}, { {
value: serverInfo.proxy_type_count.http, value: serverInfo.proxy_type_count.udp,
name: 'HTTP' name: 'UDP'
}, { },
value: serverInfo.proxy_type_count.https, {
name: 'HTTPS' value: serverInfo.proxy_type_count.http,
}, { name: 'HTTP'
value: serverInfo.proxy_type_count.stcp, },
name: 'STCP' {
}, { value: serverInfo.proxy_type_count.https,
value: serverInfo.proxy_type_count.xtcp, name: 'HTTPS'
name: 'XTCP' },
}], {
itemStyle: { value: serverInfo.proxy_type_count.stcp,
emphasis: { name: 'STCP'
shadowBlur: 10, },
shadowOffsetX: 0, {
shadowColor: 'rgba(0, 0, 0, 0.5)' value: serverInfo.proxy_type_count.xtcp,
} name: 'XTCP'
} }
}] ],
}; itemStyle: {
myChart.setOption(option); emphasis: {
myChart.hideLoading() shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
myChart.setOption(option)
myChart.hideLoading()
} }
// 7 days // 7 days
function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) { function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
let params = { const params = {
width: '600px', width: '600px',
height: '400px' height: '400px'
} }
let myChart = echarts.init(document.getElementById(elementId), 'macarons', params); const myChart = echarts.init(document.getElementById(elementId), 'macarons', params)
myChart.showLoading() myChart.showLoading()
trafficInArr = trafficInArr.reverse() trafficInArr = trafficInArr.reverse()
trafficOutArr = trafficOutArr.reverse() trafficOutArr = trafficOutArr.reverse()
let now = new Date() let now = new Date()
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6) now = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6)
let dates = new Array() const dates = []
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {
dates.push(now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate()) dates.push(now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate())
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1) now = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
} }
let option = { const option = {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'shadow' type: 'shadow'
}, },
formatter: function(data) { formatter: function(data) {
let html = '' let html = ''
if (data.length > 0) { if (data.length > 0) {
html += data[0].name + '<br/>' html += data[0].name + '<br/>'
} }
for (let v of data) { for (const v of data) {
let colorEl = '<span style="display:inline-block;margin-right:5px;' + const colorEl =
'border-radius:10px;width:9px;height:9px;background-color:' + v.color + '"></span>'; '<span style="display:inline-block;margin-right:5px;' + 'border-radius:10px;width:9px;height:9px;background-color:' + v.color + '"></span>'
html += colorEl + v.seriesName + ': ' + Humanize.fileSize(v.value) + '<br/>' html += colorEl + v.seriesName + ': ' + Humanize.fileSize(v.value) + '<br/>'
} }
return html return html
} }
}, },
legend: { legend: {
data: ['Traffic In', 'Traffic Out'] data: ['Traffic In', 'Traffic Out']
}, },
grid: { grid: {
left: '3%', left: '3%',
right: '4%', right: '4%',
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true
}, },
xAxis: [{ xAxis: [
type: 'category', {
data: dates type: 'category',
}], data: dates
yAxis: [{ }
type: 'value', ],
axisLabel: { yAxis: [
formatter: function(value) { {
return Humanize.fileSize(value) type: 'value',
} axisLabel: {
} formatter: function(value) {
}], return Humanize.fileSize(value)
series: [{ }
name: 'Traffic In', }
type: 'bar', }
data: trafficInArr ],
}, { series: [
{
name: 'Traffic Out', name: 'Traffic In',
type: 'bar', type: 'bar',
data: trafficOutArr data: trafficInArr
}] },
}; {
myChart.setOption(option); name: 'Traffic Out',
myChart.hideLoading() type: 'bar',
data: trafficOutArr
}
]
}
myChart.setOption(option)
myChart.hideLoading()
} }
export { export { DrawTrafficChart, DrawProxyChart, DrawProxyTrafficChart }
DrawTrafficChart,
DrawProxyChart,
DrawProxyTrafficChart
}

View File

@ -1,97 +1,97 @@
class BaseProxy { class BaseProxy {
constructor(proxyStats) { constructor(proxyStats) {
this.name = proxyStats.name this.name = proxyStats.name
if (proxyStats.conf != null) { if (proxyStats.conf != null) {
this.encryption = proxyStats.conf.use_encryption this.encryption = proxyStats.conf.use_encryption
this.compression = proxyStats.conf.use_compression this.compression = proxyStats.conf.use_compression
} else { } else {
this.encryption = "" this.encryption = ''
this.compression = "" this.compression = ''
}
this.conns = proxyStats.cur_conns
this.traffic_in = proxyStats.today_traffic_in
this.traffic_out = proxyStats.today_traffic_out
this.last_start_time = proxyStats.last_start_time
this.last_close_time = proxyStats.last_close_time
this.status = proxyStats.status
} }
this.conns = proxyStats.cur_conns
this.traffic_in = proxyStats.today_traffic_in
this.traffic_out = proxyStats.today_traffic_out
this.last_start_time = proxyStats.last_start_time
this.last_close_time = proxyStats.last_close_time
this.status = proxyStats.status
}
} }
class TcpProxy extends BaseProxy { class TcpProxy extends BaseProxy {
constructor(proxyStats) { constructor(proxyStats) {
super(proxyStats) super(proxyStats)
this.type = "tcp" this.type = 'tcp'
if (proxyStats.conf != null) { if (proxyStats.conf != null) {
this.addr = ":" + proxyStats.conf.remote_port this.addr = ':' + proxyStats.conf.remote_port
this.port = proxyStats.conf.remote_port this.port = proxyStats.conf.remote_port
} else { } else {
this.addr = "" this.addr = ''
this.port = "" this.port = ''
}
} }
}
} }
class UdpProxy extends BaseProxy { class UdpProxy extends BaseProxy {
constructor(proxyStats) { constructor(proxyStats) {
super(proxyStats) super(proxyStats)
this.type = "udp" this.type = 'udp'
if (proxyStats.conf != null) { if (proxyStats.conf != null) {
this.addr = ":" + proxyStats.conf.remote_port this.addr = ':' + proxyStats.conf.remote_port
this.port = proxyStats.conf.remote_port this.port = proxyStats.conf.remote_port
} else { } else {
this.addr = "" this.addr = ''
this.port = "" this.port = ''
}
} }
}
} }
class HttpProxy extends BaseProxy { class HttpProxy extends BaseProxy {
constructor(proxyStats, port, subdomain_host) { constructor(proxyStats, port, subdomain_host) {
super(proxyStats) super(proxyStats)
this.type = "http" this.type = 'http'
this.port = port this.port = port
if (proxyStats.conf != null) { if (proxyStats.conf != null) {
this.custom_domains = proxyStats.conf.custom_domains this.custom_domains = proxyStats.conf.custom_domains
this.host_header_rewrite = proxyStats.conf.host_header_rewrite this.host_header_rewrite = proxyStats.conf.host_header_rewrite
this.locations = proxyStats.conf.locations this.locations = proxyStats.conf.locations
if (proxyStats.conf.sub_domain != "") { if (proxyStats.conf.sub_domain !== '') {
this.subdomain = proxyStats.conf.sub_domain + "." + subdomain_host this.subdomain = proxyStats.conf.sub_domain + '.' + subdomain_host
} else { } else {
this.subdomain = "" this.subdomain = ''
} }
} else { } else {
this.custom_domains = "" this.custom_domains = ''
this.host_header_rewrite = "" this.host_header_rewrite = ''
this.subdomain = "" this.subdomain = ''
this.locations = "" this.locations = ''
}
} }
}
} }
class HttpsProxy extends BaseProxy { class HttpsProxy extends BaseProxy {
constructor(proxyStats, port, subdomain_host) { constructor(proxyStats, port, subdomain_host) {
super(proxyStats) super(proxyStats)
this.type = "https" this.type = 'https'
this.port = port this.port = port
if (proxyStats.conf != null) { if (proxyStats.conf != null) {
this.custom_domains = proxyStats.conf.custom_domains this.custom_domains = proxyStats.conf.custom_domains
if (proxyStats.conf.sub_domain != "") { if (proxyStats.conf.sub_domain !== '') {
this.subdomain = proxyStats.conf.sub_domain + "." + subdomain_host this.subdomain = proxyStats.conf.sub_domain + '.' + subdomain_host
} else { } else {
this.subdomain = "" this.subdomain = ''
} }
} else { } else {
this.custom_domains = "" this.custom_domains = ''
this.subdomain = "" this.subdomain = ''
}
} }
}
} }
class StcpProxy extends BaseProxy { class StcpProxy extends BaseProxy {
constructor(proxyStats) { constructor(proxyStats) {
super(proxyStats) super(proxyStats)
this.type = "stcp" this.type = 'stcp'
} }
} }
export {BaseProxy, TcpProxy, UdpProxy, HttpProxy, HttpsProxy, StcpProxy} export { BaseProxy, TcpProxy, UdpProxy, HttpProxy, HttpsProxy, StcpProxy }

16
web/frps/vue.config.js Normal file
View File

@ -0,0 +1,16 @@
module.exports = {
publicPath: './',
devServer: {
host: '127.0.0.1',
port: 8010,
proxy: {
'/api/': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
}

View File

@ -1,107 +0,0 @@
const path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var VueLoaderPlugin = require('vue-loader/lib/plugin')
var url = require('url')
var publicPath = ''
module.exports = (options = {}) => ({
entry: {
vendor: './src/main'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: options.dev ? '[name].js' : '[name].js?[chunkhash]',
chunkFilename: '[id].js?[chunkhash]',
publicPath: options.dev ? '/assets/' : publicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': path.resolve(__dirname, 'src'),
}
},
module: {
rules: [{
test: /\.vue$/,
loader: 'vue-loader'
}, {
test: /\.js$/,
use: ['babel-loader'],
exclude: /node_modules/
}, {
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
root: path.resolve(__dirname, 'src'),
attrs: ['img:src', 'link:href']
}
}]
}, {
test: /\.less$/,
loader: 'style-loader!css-loader!postcss-loader!less-loader'
}, {
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}, {
test: /favicon\.png$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}]
}, {
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
exclude: /favicon\.png$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000
}
}]
}]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor', 'manifest']
}),
new HtmlWebpackPlugin({
favicon: 'src/assets/favicon.ico',
template: 'src/index.html'
}),
new webpack.NormalModuleReplacementPlugin(/element-ui[\/\\]lib[\/\\]locale[\/\\]lang[\/\\]zh-CN/, 'element-ui/lib/locale/lang/en'),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: false,
comments: false,
compress: {
warnings: false
}
}),
new VueLoaderPlugin()
],
devServer: {
host: '127.0.0.1',
port: 8010,
proxy: {
'/api/': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
},
historyApiFallback: {
index: url.parse(options.dev ? '/assets/' : publicPath).pathname
}
}//,
//devtool: options.dev ? '#eval-source-map' : '#source-map'
})

9207
web/frps/yarn.lock Normal file

File diff suppressed because it is too large Load Diff