mirror of
https://github.com/fatedier/frp.git
synced 2025-01-03 13:31:04 +07:00
new frps dashboard
This commit is contained in:
parent
8b2cde3a30
commit
d8683a0079
5
web/frps/.babelrc
Normal file
5
web/frps/.babelrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["es2015", { "modules": false }]
|
||||||
|
]
|
||||||
|
}
|
6
web/frps/.gitignore
vendored
Normal file
6
web/frps/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
npm-debug.log
|
||||||
|
.idea
|
||||||
|
.vscode/settings.json
|
9
web/frps/Makefile
Normal file
9
web/frps/Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
.PHONY: dist build
|
||||||
|
install:
|
||||||
|
@npm install
|
||||||
|
|
||||||
|
dev: install
|
||||||
|
@npm run dev
|
||||||
|
|
||||||
|
build:
|
||||||
|
@npm run build
|
46
web/frps/package.json
Normal file
46
web/frps/package.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "frps-dashboard",
|
||||||
|
"description": "A dashboard for frp server.",
|
||||||
|
"author": "fatedier",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "webpack-dev-server -d --inline --hot --env.dev",
|
||||||
|
"build": "rimraf dist && webpack -p --progress --hide-modules"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bootstrap": "^3.3.7",
|
||||||
|
"echarts": "^3.5.0",
|
||||||
|
"element-ui": "^1.2.5",
|
||||||
|
"humanize-plus": "^1.8.2",
|
||||||
|
"vue": "^2.2.4",
|
||||||
|
"vue-resource": "^1.2.1",
|
||||||
|
"vue-router": "^2.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^6.6.0",
|
||||||
|
"babel-core": "^6.21.0",
|
||||||
|
"babel-eslint": "^7.1.1",
|
||||||
|
"babel-loader": "^6.4.0",
|
||||||
|
"babel-preset-es2015": "^6.13.2",
|
||||||
|
"css-loader": "^0.27.0",
|
||||||
|
"eslint": "^3.12.2",
|
||||||
|
"eslint-config-enough": "^0.2.2",
|
||||||
|
"eslint-loader": "^1.6.3",
|
||||||
|
"file-loader": "^0.10.1",
|
||||||
|
"html-loader": "^0.4.5",
|
||||||
|
"html-webpack-plugin": "^2.24.1",
|
||||||
|
"less": "^2.7.2",
|
||||||
|
"less-loader": "^3.0.0",
|
||||||
|
"postcss-loader": "^1.3.3",
|
||||||
|
"rimraf": "^2.5.4",
|
||||||
|
"style-loader": "^0.13.2",
|
||||||
|
"url-loader": "^0.5.8",
|
||||||
|
"vue-loader": "^11.1.4",
|
||||||
|
"vue-template-compiler": "^2.1.8",
|
||||||
|
"webpack": "^2.2.0-rc.4",
|
||||||
|
"webpack-dev-server": "beta"
|
||||||
|
}
|
||||||
|
}
|
5
web/frps/postcss.config.js
Normal file
5
web/frps/postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('autoprefixer')()
|
||||||
|
]
|
||||||
|
}
|
78
web/frps/src/App.vue
Normal file
78
web/frps/src/App.vue
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app">
|
||||||
|
<header class="grid-content header-color">
|
||||||
|
<el-row>
|
||||||
|
<a class="brand" href="#">frp</a>
|
||||||
|
</el-row>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col id="side-nav" :xs="24" :md="4">
|
||||||
|
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect">
|
||||||
|
<el-menu-item index="/">Overview</el-menu-item>
|
||||||
|
<el-submenu index="/proxies">
|
||||||
|
<template slot="title">Proxies</template>
|
||||||
|
<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/http">HTTP</el-menu-item>
|
||||||
|
<el-menu-item index="/proxies/https">HTTPS</el-menu-item>
|
||||||
|
</el-submenu>
|
||||||
|
<el-menu-item index="">Help</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :xs="24" :md="20">
|
||||||
|
<div id="content">
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</section>
|
||||||
|
<footer></footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
handleSelect(key, path) {
|
||||||
|
if (key == '') {
|
||||||
|
window.open("http://github.com/fatedier/frp")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: #fafafa;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
width: 100%;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-color {
|
||||||
|
background: #58B7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
color: #fff;
|
||||||
|
background-color: transparent;
|
||||||
|
margin-left: 20px;
|
||||||
|
float: left;
|
||||||
|
line-height: 25px;
|
||||||
|
font-size: 25px;
|
||||||
|
padding: 15px 15px;
|
||||||
|
height: 30px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
BIN
web/frps/src/assets/favicon.ico
Normal file
BIN
web/frps/src/assets/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
140
web/frps/src/components/Overview.vue
Normal file
140
web/frps/src/components/Overview.vue
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-row>
|
||||||
|
<el-col :md="12">
|
||||||
|
<div class="source">
|
||||||
|
<el-form label-position="left" class="server_info">
|
||||||
|
<el-form-item label="Http Port">
|
||||||
|
<span>{{ vhost_http_port }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Https Port">
|
||||||
|
<span>{{ vhost_https_port }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Auth Timeout">
|
||||||
|
<span>{{ auth_timeout }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Subdomain Host">
|
||||||
|
<span>{{ subdomain_host }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Max PoolCount">
|
||||||
|
<span>{{ max_pool_count }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="HeartBeat Timeout">
|
||||||
|
<span>{{ heart_beat_timeout }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Client Counts">
|
||||||
|
<span>{{ client_counts }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Current Conns">
|
||||||
|
<span>{{ cur_conns }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Proxy Counts">
|
||||||
|
<span>{{ proxy_counts }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :md="12">
|
||||||
|
<div id="traffic" style="width: 400px;height:250px;margin-bottom: 30px;"></div>
|
||||||
|
<div id="proxies" style="width: 400px;height:250px;"></div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {DrawTrafficChart, DrawProxyChart} from "../utils/chart.js"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
vhost_http_port: "",
|
||||||
|
vhost_https_port: "",
|
||||||
|
auth_timeout: "",
|
||||||
|
subdomain_host: "",
|
||||||
|
max_pool_count: "",
|
||||||
|
heart_beat_timeout: "",
|
||||||
|
client_counts: "",
|
||||||
|
cur_conns: "",
|
||||||
|
proxy_counts: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': 'fetchData'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
fetch('/api/serverinfo')
|
||||||
|
.then(res => {
|
||||||
|
return res.json()
|
||||||
|
}).then(json => {
|
||||||
|
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.auth_timeout = json.auth_timeout
|
||||||
|
this.subdomain_host = json.subdomain_host
|
||||||
|
this.max_pool_count = json.max_pool_count
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.source {
|
||||||
|
border: 1px solid #eaeefb;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: .2s;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server_info {
|
||||||
|
margin-left: 40px;
|
||||||
|
font-size: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server_info label {
|
||||||
|
width: 150px;
|
||||||
|
color: #99a9bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server_info .el-form-item {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
142
web/frps/src/components/ProxiesHttp.vue
Normal file
142
web/frps/src/components/ProxiesHttp.vue
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||||
|
<el-table-column type="expand">
|
||||||
|
<template scope="props">
|
||||||
|
<el-popover
|
||||||
|
ref="popover4"
|
||||||
|
placement="right"
|
||||||
|
width="600"
|
||||||
|
style="margin-left:0px"
|
||||||
|
trigger="click">
|
||||||
|
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<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-item label="Name">
|
||||||
|
<span>{{ props.row.name }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Type">
|
||||||
|
<span>{{ props.row.type }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Domains">
|
||||||
|
<span>{{ props.row.custom_domains }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SubDomain">
|
||||||
|
<span>{{ props.row.subdomain }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="locations">
|
||||||
|
<span>{{ props.row.locations }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="HostRewrite">
|
||||||
|
<span>{{ props.row.host_header_rewrite }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Encryption">
|
||||||
|
<span>{{ props.row.encryption }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Compression">
|
||||||
|
<span>{{ props.row.compression }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Name"
|
||||||
|
prop="name"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Port"
|
||||||
|
prop="port"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Connections"
|
||||||
|
prop="conns"
|
||||||
|
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 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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Humanize from "humanize-plus";
|
||||||
|
import Traffic from './Traffic.vue'
|
||||||
|
import {
|
||||||
|
HttpProxy
|
||||||
|
} from "../utils/proxy.js"
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
proxies: null,
|
||||||
|
vhost_http_port: "",
|
||||||
|
subdomain_host: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': 'fetchData'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatTrafficIn(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_in)
|
||||||
|
},
|
||||||
|
formatTrafficOut(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_out)
|
||||||
|
},
|
||||||
|
fetchData() {
|
||||||
|
fetch('/api/serverinfo')
|
||||||
|
.then(res => {
|
||||||
|
return res.json()
|
||||||
|
}).then(json => {
|
||||||
|
this.vhost_http_port = json.vhost_http_port
|
||||||
|
this.subdomain_host = json.subdomain_host
|
||||||
|
if (this.vhost_http_port == null || this.vhost_http_port == 0) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
fetch('/api/proxy/http')
|
||||||
|
.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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
137
web/frps/src/components/ProxiesHttps.vue
Normal file
137
web/frps/src/components/ProxiesHttps.vue
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||||
|
<el-table-column type="expand">
|
||||||
|
<template scope="props">
|
||||||
|
<el-popover
|
||||||
|
ref="popover4"
|
||||||
|
placement="right"
|
||||||
|
width="600"
|
||||||
|
style="margin-left:0px"
|
||||||
|
trigger="click">
|
||||||
|
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<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-item label="Name">
|
||||||
|
<span>{{ props.row.name }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Type">
|
||||||
|
<span>{{ props.row.type }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Domains">
|
||||||
|
<span>{{ props.row.custom_domains }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="SubDomain">
|
||||||
|
<span>{{ props.row.subdomain }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Encryption">
|
||||||
|
<span>{{ props.row.encryption }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Compression">
|
||||||
|
<span>{{ props.row.compression }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Name"
|
||||||
|
prop="name"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Port"
|
||||||
|
prop="port"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Connections"
|
||||||
|
prop="conns"
|
||||||
|
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 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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Humanize from "humanize-plus";
|
||||||
|
import Traffic from './Traffic.vue'
|
||||||
|
import {
|
||||||
|
HttpsProxy
|
||||||
|
} from "../utils/proxy.js"
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
proxies: null,
|
||||||
|
vhost_https_port: "",
|
||||||
|
subdomain_host: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': 'fetchData'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatTrafficIn(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_in)
|
||||||
|
},
|
||||||
|
formatTrafficOut(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_out)
|
||||||
|
},
|
||||||
|
fetchData() {
|
||||||
|
fetch('/api/serverinfo')
|
||||||
|
.then(res => {
|
||||||
|
return res.json()
|
||||||
|
}).then(json => {
|
||||||
|
this.vhost_https_port = json.vhost_https_port
|
||||||
|
this.subdomain_host = json.subdomain_host
|
||||||
|
if (this.vhost_https_port == null || this.vhost_https_port == 0) {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
fetch('/api/proxy/https')
|
||||||
|
.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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
118
web/frps/src/components/ProxiesTcp.vue
Normal file
118
web/frps/src/components/ProxiesTcp.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||||
|
<el-table-column type="expand">
|
||||||
|
<template scope="props">
|
||||||
|
<el-popover
|
||||||
|
ref="popover4"
|
||||||
|
placement="right"
|
||||||
|
width="600"
|
||||||
|
style="margin-left:0px"
|
||||||
|
trigger="click">
|
||||||
|
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
||||||
|
</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-form label-position="left" inline class="demo-table-expand">
|
||||||
|
<el-form-item label="Name">
|
||||||
|
<span>{{ props.row.name }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Type">
|
||||||
|
<span>{{ props.row.type }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Addr">
|
||||||
|
<span>{{ props.row.addr }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Encryption">
|
||||||
|
<span>{{ props.row.encryption }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Compression">
|
||||||
|
<span>{{ props.row.compression }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Name"
|
||||||
|
prop="name"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Port"
|
||||||
|
prop="port"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Connections"
|
||||||
|
prop="conns"
|
||||||
|
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 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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Humanize from 'humanize-plus'
|
||||||
|
import Traffic from './Traffic.vue'
|
||||||
|
import { TcpProxy } from '../utils/proxy.js'
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
proxies: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': 'fetchData'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatTrafficIn(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_in)
|
||||||
|
},
|
||||||
|
formatTrafficOut(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_out)
|
||||||
|
},
|
||||||
|
fetchData() {
|
||||||
|
fetch('/api/proxy/tcp')
|
||||||
|
.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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
120
web/frps/src/components/ProxiesUdp.vue
Normal file
120
web/frps/src/components/ProxiesUdp.vue
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||||
|
<el-table-column type="expand">
|
||||||
|
<template scope="props">
|
||||||
|
<el-popover
|
||||||
|
ref="popover4"
|
||||||
|
placement="right"
|
||||||
|
width="600"
|
||||||
|
style="margin-left:0px"
|
||||||
|
trigger="click">
|
||||||
|
<my-traffic-chart :proxy_name="props.row.name"></my-traffic-chart>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<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-item label="Name">
|
||||||
|
<span>{{ props.row.name }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Type">
|
||||||
|
<span>{{ props.row.type }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Addr">
|
||||||
|
<span>{{ props.row.addr }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Encryption">
|
||||||
|
<span>{{ props.row.encryption }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Compression">
|
||||||
|
<span>{{ props.row.compression }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Name"
|
||||||
|
prop="name"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Port"
|
||||||
|
prop="port"
|
||||||
|
sortable>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
label="Connections"
|
||||||
|
prop="conns"
|
||||||
|
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 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>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Humanize from "humanize-plus";
|
||||||
|
import Traffic from './Traffic.vue'
|
||||||
|
import {
|
||||||
|
UdpProxy
|
||||||
|
} from "../utils/proxy.js"
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
proxies: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route': 'fetchData'
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
formatTrafficIn(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_in)
|
||||||
|
},
|
||||||
|
formatTrafficOut(row, column) {
|
||||||
|
return Humanize.fileSize(row.traffic_out)
|
||||||
|
},
|
||||||
|
fetchData() {
|
||||||
|
fetch('/api/proxy/udp')
|
||||||
|
.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>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
36
web/frps/src/components/Traffic.vue
Normal file
36
web/frps/src/components/Traffic.vue
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<div :id="proxy_name" style="width: 600px;height:400px;"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {DrawProxyTrafficChart} from '../utils/chart.js'
|
||||||
|
export default {
|
||||||
|
props: ['proxy_name'],
|
||||||
|
created() {
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
//watch: {
|
||||||
|
//'$route': 'fetchData'
|
||||||
|
//},
|
||||||
|
methods: {
|
||||||
|
fetchData() {
|
||||||
|
let url = '/api/proxy/traffic/' + this.proxy_name
|
||||||
|
fetch(url)
|
||||||
|
.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'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
15
web/frps/src/index.html
Normal file
15
web/frps/src/index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!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>
|
17
web/frps/src/main.js
Normal file
17
web/frps/src/main.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import ElementUI from 'element-ui'
|
||||||
|
import 'element-ui/lib/theme-default/index.css'
|
||||||
|
import './utils/less/custom.less'
|
||||||
|
|
||||||
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
|
||||||
|
Vue.use(ElementUI)
|
||||||
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
router,
|
||||||
|
template: '<App/>',
|
||||||
|
components: { App }
|
||||||
|
})
|
33
web/frps/src/router/index.js
Normal file
33
web/frps/src/router/index.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Router from 'vue-router'
|
||||||
|
import Overview from '../components/Overview.vue'
|
||||||
|
import ProxiesTcp from '../components/ProxiesTcp.vue'
|
||||||
|
import ProxiesUdp from '../components/ProxiesUdp.vue'
|
||||||
|
import ProxiesHttp from '../components/ProxiesHttp.vue'
|
||||||
|
import ProxiesHttps from '../components/ProxiesHttps.vue'
|
||||||
|
|
||||||
|
Vue.use(Router)
|
||||||
|
|
||||||
|
export default new Router({
|
||||||
|
routes: [{
|
||||||
|
path: '/',
|
||||||
|
name: 'Overview',
|
||||||
|
component: Overview
|
||||||
|
}, {
|
||||||
|
path: '/proxies/tcp',
|
||||||
|
name: 'ProxiesTcp',
|
||||||
|
component: ProxiesTcp
|
||||||
|
}, {
|
||||||
|
path: '/proxies/udp',
|
||||||
|
name: 'ProxiesUdp',
|
||||||
|
component: ProxiesUdp
|
||||||
|
}, {
|
||||||
|
path: '/proxies/http',
|
||||||
|
name: 'ProxiesHttp',
|
||||||
|
component: ProxiesHttp
|
||||||
|
}, {
|
||||||
|
path: '/proxies/https',
|
||||||
|
name: 'ProxiesHttps',
|
||||||
|
component: ProxiesHttps
|
||||||
|
}]
|
||||||
|
})
|
187
web/frps/src/utils/chart.js
Normal file
187
web/frps/src/utils/chart.js
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import Humanize from "humanize-plus"
|
||||||
|
import echarts from "echarts/lib/echarts"
|
||||||
|
|
||||||
|
import "echarts/theme/macarons"
|
||||||
|
import "echarts/lib/chart/bar"
|
||||||
|
import "echarts/lib/chart/pie"
|
||||||
|
import "echarts/lib/component/tooltip"
|
||||||
|
import "echarts/lib/component/title"
|
||||||
|
|
||||||
|
function DrawTrafficChart(elementId, trafficIn, trafficOut) {
|
||||||
|
let myChart = echarts.init(document.getElementById(elementId), 'macarons');
|
||||||
|
myChart.showLoading()
|
||||||
|
|
||||||
|
let option = {
|
||||||
|
title: {
|
||||||
|
text: 'Network Traffic',
|
||||||
|
subtext: 'today',
|
||||||
|
x: 'center'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: function(v) {
|
||||||
|
return Humanize.fileSize(v.data.value) + " (" + v.percent + "%)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: '55%',
|
||||||
|
center: ['50%', '60%'],
|
||||||
|
data: [{
|
||||||
|
value: trafficIn,
|
||||||
|
name: 'Traffic In'
|
||||||
|
}, {
|
||||||
|
value: trafficOut,
|
||||||
|
name: 'Traffic Out'
|
||||||
|
}, ],
|
||||||
|
itemStyle: {
|
||||||
|
emphasis: {
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
myChart.setOption(option);
|
||||||
|
myChart.hideLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
function DrawProxyChart(elementId, serverInfo) {
|
||||||
|
if (serverInfo.proxy_type_count.tcp == null) {
|
||||||
|
serverInfo.proxy_type_count.tcp = 0
|
||||||
|
}
|
||||||
|
if (serverInfo.proxy_type_count.udp == null) {
|
||||||
|
serverInfo.proxy_type_count.udp = 0
|
||||||
|
}
|
||||||
|
if (serverInfo.proxy_type_count.http == null) {
|
||||||
|
serverInfo.proxy_type_count.http = 0
|
||||||
|
}
|
||||||
|
if (serverInfo.proxy_type_count.https == null) {
|
||||||
|
serverInfo.proxy_type_count.https = 0
|
||||||
|
}
|
||||||
|
let myChart = echarts.init(document.getElementById(elementId), 'macarons')
|
||||||
|
myChart.showLoading()
|
||||||
|
|
||||||
|
let option = {
|
||||||
|
title: {
|
||||||
|
text: 'Proxies',
|
||||||
|
subtext: 'now',
|
||||||
|
x: 'center'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: function(v) {
|
||||||
|
return v.data.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
type: 'pie',
|
||||||
|
radius: '55%',
|
||||||
|
center: ['50%', '60%'],
|
||||||
|
data: [{
|
||||||
|
value: serverInfo.proxy_type_count.tcp,
|
||||||
|
name: 'TCP'
|
||||||
|
}, {
|
||||||
|
value: serverInfo.proxy_type_count.udp,
|
||||||
|
name: 'UDP'
|
||||||
|
}, {
|
||||||
|
value: serverInfo.proxy_type_count.http,
|
||||||
|
name: 'HTTP'
|
||||||
|
}, {
|
||||||
|
value: serverInfo.proxy_type_count.https,
|
||||||
|
name: 'HTTPS'
|
||||||
|
}],
|
||||||
|
itemStyle: {
|
||||||
|
emphasis: {
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
myChart.setOption(option);
|
||||||
|
myChart.hideLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7 days
|
||||||
|
function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
|
||||||
|
let params = {
|
||||||
|
width: '600px',
|
||||||
|
height: '400px'
|
||||||
|
}
|
||||||
|
|
||||||
|
let myChart = echarts.init(document.getElementById(elementId), 'macarons', params);
|
||||||
|
myChart.showLoading()
|
||||||
|
|
||||||
|
trafficInArr = trafficInArr.reverse()
|
||||||
|
trafficOutArr = trafficOutArr.reverse()
|
||||||
|
let now = new Date()
|
||||||
|
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6)
|
||||||
|
let dates = new Array()
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
dates.push(now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate())
|
||||||
|
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
let option = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
},
|
||||||
|
formatter: function(data) {
|
||||||
|
let html = ''
|
||||||
|
if (data.length > 0) {
|
||||||
|
html += data[0].name + '<br/>'
|
||||||
|
}
|
||||||
|
for (let v of data) {
|
||||||
|
let colorEl = '<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/>'
|
||||||
|
}
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
data: ['Traffic In', 'Traffic Out']
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '3%',
|
||||||
|
right: '4%',
|
||||||
|
bottom: '3%',
|
||||||
|
containLabel: true
|
||||||
|
},
|
||||||
|
xAxis: [{
|
||||||
|
type: 'category',
|
||||||
|
data: dates
|
||||||
|
}],
|
||||||
|
yAxis: [{
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
formatter: function(value) {
|
||||||
|
return Humanize.fileSize(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
series: [{
|
||||||
|
name: 'Traffic In',
|
||||||
|
type: 'bar',
|
||||||
|
data: trafficInArr
|
||||||
|
}, {
|
||||||
|
|
||||||
|
name: 'Traffic Out',
|
||||||
|
type: 'bar',
|
||||||
|
data: trafficOutArr
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
myChart.setOption(option);
|
||||||
|
myChart.hideLoading()
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
DrawTrafficChart,
|
||||||
|
DrawProxyChart,
|
||||||
|
DrawProxyTrafficChart
|
||||||
|
}
|
22
web/frps/src/utils/less/custom.less
Normal file
22
web/frps/src/utils/less/custom.less
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
@color: red;
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
span {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-table-expand {
|
||||||
|
font-size: 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
width: 90px;
|
||||||
|
color: #99a9bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
88
web/frps/src/utils/proxy.js
Normal file
88
web/frps/src/utils/proxy.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
class BaseProxy {
|
||||||
|
constructor(proxyStats) {
|
||||||
|
this.name = proxyStats.name
|
||||||
|
if (proxyStats.conf != null) {
|
||||||
|
this.encryption = proxyStats.conf.use_encryption
|
||||||
|
this.compression = proxyStats.conf.use_compression
|
||||||
|
} else {
|
||||||
|
this.encryption = ""
|
||||||
|
this.compression = ""
|
||||||
|
}
|
||||||
|
this.conns = proxyStats.cur_conns
|
||||||
|
this.traffic_in = proxyStats.today_traffic_in
|
||||||
|
this.traffic_out = proxyStats.today_traffic_out
|
||||||
|
this.status = proxyStats.status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TcpProxy extends BaseProxy {
|
||||||
|
constructor(proxyStats) {
|
||||||
|
super(proxyStats)
|
||||||
|
this.type = "tcp"
|
||||||
|
if (proxyStats.conf != null) {
|
||||||
|
this.addr = proxyStats.conf.bind_addr + ":" + proxyStats.conf.remote_port
|
||||||
|
this.port = proxyStats.conf.remote_port
|
||||||
|
} else {
|
||||||
|
this.addr = ""
|
||||||
|
this.port = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UdpProxy extends BaseProxy {
|
||||||
|
constructor(proxyStats) {
|
||||||
|
super(proxyStats)
|
||||||
|
this.type = "udp"
|
||||||
|
if (proxyStats.conf != null) {
|
||||||
|
this.addr = proxyStats.conf.bind_addr + ":" + proxyStats.conf.remote_port
|
||||||
|
this.port = proxyStats.conf.remote_port
|
||||||
|
} else {
|
||||||
|
this.addr = ""
|
||||||
|
this.port = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HttpProxy extends BaseProxy {
|
||||||
|
constructor(proxyStats, port, subdomain_host) {
|
||||||
|
super(proxyStats)
|
||||||
|
this.type = "http"
|
||||||
|
this.port = port
|
||||||
|
if (proxyStats.conf != null) {
|
||||||
|
this.custom_domains = proxyStats.conf.custom_domains
|
||||||
|
this.host_header_rewrite = proxyStats.conf.host_header_rewrite
|
||||||
|
this.locations = proxyStats.conf.locations
|
||||||
|
if (proxyStats.conf.sub_domain != "") {
|
||||||
|
this.subdomain = proxyStats.conf.sub_domain + "." + subdomain_host
|
||||||
|
} else {
|
||||||
|
this.subdomain = ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.custom_domains = ""
|
||||||
|
this.host_header_rewrite = ""
|
||||||
|
this.subdomain = ""
|
||||||
|
this.locations = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HttpsProxy extends BaseProxy {
|
||||||
|
constructor(proxyStats, port, subdomain_host) {
|
||||||
|
super(proxyStats)
|
||||||
|
this.type = "https"
|
||||||
|
this.port = port
|
||||||
|
if (proxyStats.conf != null) {
|
||||||
|
this.custom_domains = proxyStats.conf.custom_domains
|
||||||
|
if (proxyStats.conf.sub_domain != "") {
|
||||||
|
this.subdomain = proxyStats.conf.sub_domain + "." + subdomain_host
|
||||||
|
} else {
|
||||||
|
this.subdomain = ""
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.custom_domains = ""
|
||||||
|
this.subdomain = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {BaseProxy, TcpProxy, UdpProxy, HttpProxy, HttpsProxy}
|
2
web/frps/src/vendor.js
Normal file
2
web/frps/src/vendor.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import ElementUI from 'element-ui'
|
93
web/frps/webpack.config.js
Normal file
93
web/frps/webpack.config.js
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
const path = require('path')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
|
var url = require('url')
|
||||||
|
var publicPath = ''
|
||||||
|
|
||||||
|
module.exports = (options = {}) => ({
|
||||||
|
entry: {
|
||||||
|
vendor: './src/vendor',
|
||||||
|
index: './src/main.js'
|
||||||
|
},
|
||||||
|
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$/,
|
||||||
|
use: ['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'
|
||||||
|
})
|
||||||
|
],
|
||||||
|
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'
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user