revert web code (#2215)

This commit is contained in:
fatedier 2021-01-25 16:04:33 +08:00 committed by GitHub
parent b26080589b
commit ecb6ed9258
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 9003 additions and 21790 deletions

View File

@ -1 +1 @@
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?d2cd6337d30c7b22e836"></script><script type="text/javascript" src="vendor.js?edb271e1d9c81f857840"></script></body> </html> <!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?f30e0e5ff7dbde4611e0"></script><script type="text/javascript" src="vendor.js?a82aed5fb0b844cbdb29"></script></body> </html>

View File

@ -1 +1 @@
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,u){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(r&&r(t,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=n(n.s=u[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var u=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"edb271e1d9c81f857840"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,u.appendChild(i),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]); !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,u){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(r&&r(t,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=n(n.s=u[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var u=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"a82aed5fb0b844cbdb29"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,u.appendChild(i),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.el-form-item span{margin-left:15px}.demo-table-expand{font-size:0}.demo-table-expand label{width:90px;color:#99a9bf}.demo-table-expand .el-form-item{margin-right:0;margin-bottom:0;width:50%}body{background-color:#fafafa;margin:0;font-family:-apple-system,BlinkMacSystemFont,Helvetica Neue,sans-serif}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}.source{border:1px solid #eaeefb;border-radius:4px;transition:.2s;padding:24px}.server_info{margin-left:40px;font-size:0}.server_info label{width:150px;color:#99a9bf}.server_info .el-form-item{margin-right:0;margin-bottom:0;width:100%}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
<!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"><link rel="icon" href="favicon.ico"><title>frps-dashboard</title><link href="css/app.808290ae.css" rel="preload" as="style"><link href="css/chunk-vendors.84bb20f7.css" rel="preload" as="style"><link href="js/app.bb942a48.js" rel="preload" as="script"><link href="js/chunk-vendors.4421b07d.js" rel="preload" as="script"><link href="css/chunk-vendors.84bb20f7.css" rel="stylesheet"><link href="css/app.808290ae.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but frps-dashboard doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.4421b07d.js"></script><script src="js/app.bb942a48.js"></script></body></html> <!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frps dashboard</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?b8b55d8156200869417b"></script><script type="text/javascript" src="vendor.js?3e078a9d741093b909de"></script></body> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,u,c){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in u)Object.prototype.hasOwnProperty.call(u,i)&&(e[i]=u[i]);for(r&&r(t,u,c);s.length;)s.shift()();if(c)for(l=0;l<c.length;l++)f=n(n.s=c[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var u=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=u;var c=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"3e078a9d741093b909de"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,c.appendChild(i),u},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

14
web/frps/.babelrc Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -1,267 +0,0 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
env: {
browser: true,
node: true,
es6: true
},
extends: ['plugin:vue/recommended', 'eslint:recommended'],
// add your custom rules here
// it is base on https://github.com/vuejs/eslint-config-vue
rules: {
'vue/max-attributes-per-line': [
2,
{
singleline: 10,
multiline: {
max: 1,
allowFirstLine: false
}
}
],
'vue/singleline-html-element-content-newline': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/name-property-casing': ['error', 'PascalCase'],
'vue/no-v-html': 'off',
'accessor-pairs': 2,
'arrow-spacing': [
2,
{
before: true,
after: true
}
],
'block-spacing': [2, 'always'],
'brace-style': [
2,
'1tbs',
{
allowSingleLine: true
}
],
camelcase: [
0,
{
properties: 'always'
}
],
'comma-dangle': [2, 'never'],
'comma-spacing': [
2,
{
before: false,
after: true
}
],
'comma-style': [2, 'last'],
'constructor-super': 2,
curly: [2, 'multi-line'],
'dot-location': [2, 'property'],
'eol-last': 2,
eqeqeq: ['error', 'always', { null: 'ignore' }],
'generator-star-spacing': [
2,
{
before: true,
after: true
}
],
'handle-callback-err': [2, '^(err|error)$'],
indent: [
2,
2,
{
SwitchCase: 1
}
],
'jsx-quotes': [2, 'prefer-single'],
'key-spacing': [
2,
{
beforeColon: false,
afterColon: true
}
],
'keyword-spacing': [
2,
{
before: true,
after: true
}
],
'new-cap': [
2,
{
newIsCap: true,
capIsNew: false
}
],
'new-parens': 2,
'no-array-constructor': 2,
'no-caller': 2,
'no-console': 'off',
'no-class-assign': 2,
'no-cond-assign': 2,
'no-const-assign': 2,
'no-control-regex': 0,
'no-delete-var': 2,
'no-dupe-args': 2,
'no-dupe-class-members': 2,
'no-dupe-keys': 2,
'no-duplicate-case': 2,
'no-empty-character-class': 2,
'no-empty-pattern': 2,
'no-eval': 2,
'no-ex-assign': 2,
'no-extend-native': 2,
'no-extra-bind': 2,
'no-extra-boolean-cast': 2,
'no-extra-parens': [2, 'functions'],
'no-fallthrough': 2,
'no-floating-decimal': 2,
'no-func-assign': 2,
'no-implied-eval': 2,
'no-inner-declarations': [2, 'functions'],
'no-invalid-regexp': 2,
'no-irregular-whitespace': 2,
'no-iterator': 2,
'no-label-var': 2,
'no-labels': [
2,
{
allowLoop: false,
allowSwitch: false
}
],
'no-lone-blocks': 2,
'no-mixed-spaces-and-tabs': 2,
'no-multi-spaces': 2,
'no-multi-str': 2,
'no-multiple-empty-lines': [
2,
{
max: 1
}
],
'no-native-reassign': 2,
'no-negated-in-lhs': 2,
'no-new-object': 2,
'no-new-require': 2,
'no-new-symbol': 2,
'no-new-wrappers': 2,
'no-obj-calls': 2,
'no-octal': 2,
'no-octal-escape': 2,
'no-path-concat': 2,
'no-proto': 2,
'no-redeclare': 2,
'no-regex-spaces': 2,
'no-return-assign': [2, 'except-parens'],
'no-self-assign': 2,
'no-self-compare': 2,
'no-sequences': 2,
'no-shadow-restricted-names': 2,
'no-spaced-func': 2,
'no-sparse-arrays': 2,
'no-this-before-super': 2,
'no-throw-literal': 2,
'no-trailing-spaces': 2,
'no-undef': 2,
'no-undef-init': 2,
'no-unexpected-multiline': 2,
'no-unmodified-loop-condition': 2,
'no-unneeded-ternary': [
2,
{
defaultAssignment: false
}
],
'no-unreachable': 2,
'no-unsafe-finally': 2,
'no-unused-vars': [
2,
{
vars: 'all',
args: 'none'
}
],
'no-useless-call': 2,
'no-useless-computed-key': 2,
'no-useless-constructor': 2,
'no-useless-escape': 0,
'no-whitespace-before-property': 2,
'no-with': 2,
'one-var': [
2,
{
initialized: 'never'
}
],
'operator-linebreak': [
2,
'after',
{
overrides: {
'?': 'before',
':': 'before'
}
}
],
'padded-blocks': [2, 'never'],
quotes: [
2,
'single',
{
avoidEscape: true,
allowTemplateLiterals: true
}
],
semi: [2, 'never'],
'semi-spacing': [
2,
{
before: false,
after: true
}
],
'space-before-blocks': [2, 'always'],
// 'space-before-function-paren': [2, 'never'],
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [
2,
{
words: true,
nonwords: false
}
],
'spaced-comment': [
2,
'always',
{
markers: ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
}
],
'template-curly-spacing': [2, 'never'],
'use-isnan': 2,
'valid-typeof': 2,
'wrap-iife': [2, 'any'],
'yield-star-spacing': [2, 'both'],
yoda: [2, 'never'],
'prefer-const': 2,
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
'object-curly-spacing': [
2,
'always',
{
objectsInObjects: false
}
],
'array-bracket-spacing': [2, 'never']
}
}

27
web/frps/.gitignore vendored
View File

@ -1,25 +1,6 @@
.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 .vscode/settings.json
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
package-lock.json

View File

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

View File

@ -1,10 +1,7 @@
.PHONY: dist build .PHONY: dist build
build: install build:
@npm run build @npm run build
dev: install dev: install
@npm run serve @npm run dev
install:
@npm install

View File

@ -1,25 +0,0 @@
# frps
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```

View File

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

View File

@ -4,43 +4,45 @@
"author": "fatedier", "author": "fatedier",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "dev": "webpack-dev-server -d --inline --hot --env.dev",
"build": "vue-cli-service build", "build": "rimraf dist && webpack -p --progress --hide-modules"
"lint": "vue-cli-service lint"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.7.0", "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.6.12", "vue": "^2.5.16",
"vue-router": "^3.4.9", "vue-resource": "^1.2.1",
"vuex": "^3.5.1", "vue-router": "^2.3.0",
"whatwg-fetch": "^3.5.0" "whatwg-fetch": "^2.0.3"
},
"engines": {
"node": ">=6"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "~4.5.9", "autoprefixer": "^6.6.0",
"@vue/cli-plugin-eslint": "~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",
"@vue/eslint-config-standard": "^5.1.2", "babel-preset-es2015": "^6.13.2",
"babel-eslint": "^10.1.0", "css-loader": "^0.27.0",
"eslint": "^7.14.0", "eslint": "^3.12.2",
"eslint-plugin-import": "^2.22.1", "eslint-config-enough": "^0.2.2",
"eslint-plugin-node": "^11.1.0", "eslint-loader": "^1.6.3",
"eslint-plugin-promise": "^4.2.1", "file-loader": "^0.10.1",
"eslint-plugin-standard": "^4.1.0", "html-loader": "^0.4.5",
"eslint-plugin-vue": "^7.1.0", "html-webpack-plugin": "^2.24.1",
"less": "^3.12.2", "less": "^3.0.4",
"less-loader": "^7.1.0", "less-loader": "^4.1.0",
"node-sass": "^5.0.0", "postcss-loader": "^1.3.3",
"sass-loader": "^10.1.0", "rimraf": "^2.5.4",
"vue-template-compiler": "^2.6.12" "style-loader": "^0.13.2",
}, "url-loader": "^1.0.1",
"browserslist": [ "vue-loader": "^15.0.10",
"> 1%", "vue-template-compiler": "^2.1.8",
"last 2 versions", "webpack": "^2.2.0-rc.4",
"not dead" "webpack-dev-server": "^3.1.4"
] }
} }

View File

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

View File

@ -1,17 +0,0 @@
<!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

@ -8,7 +8,7 @@
<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 @select="handleSelect"> <el-menu default-active="1" mode="vertical" theme="light" router="false" @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>
@ -24,28 +24,21 @@
<el-col :xs="24" :md="20"> <el-col :xs="24" :md="20">
<div id="content"> <div id="content">
<router-view v-if="serverInfo" /> <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 {
computed: {
serverInfo() {
return this.$store.state.serverInfo
}
},
async created() {
this.$store.dispatch('fetchServerInfo')
},
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")
} }
} }
} }
@ -65,7 +58,7 @@ header {
} }
.header-color { .header-color {
background: #58b7ff; background: #58B7FF;
} }
#content { #content {

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -44,8 +44,8 @@
</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 id="traffic" style="width: 400px;height:250px;margin-bottom: 30px;"></div>
<div id="proxies" style="width: 400px; height: 250px" /> <div id="proxies" style="width: 400px;height:250px;"></div>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -70,65 +70,71 @@ export default {
proxy_counts: '' proxy_counts: ''
} }
}, },
computed: { created() {
serverInfo() { this.fetchData()
return this.$store.state.serverInfo
}
}, },
mounted() { watch: {
this.initData() '$route': 'fetchData'
}, },
methods: { methods: {
initData() { fetchData() {
console.log(!!this.serverInfo, this.serverInfo) fetch('/api/serverinfo', {credentials: 'include'})
if (!this.serverInfo) return .then(res => {
return res.json()
this.version = this.serverInfo.version }).then(json => {
this.bind_port = this.serverInfo.bind_port this.version = json.version
this.bind_udp_port = this.serverInfo.bind_udp_port this.bind_port = json.bind_port
if (this.bind_udp_port === 0) { this.bind_udp_port = json.bind_udp_port
this.bind_udp_port = 'disable' if (this.bind_udp_port == 0) {
this.bind_udp_port = "disable"
} }
this.vhost_http_port = this.serverInfo.vhost_http_port this.vhost_http_port = json.vhost_http_port
if (this.vhost_http_port === 0) { if (this.vhost_http_port == 0) {
this.vhost_http_port = 'disable' this.vhost_http_port = "disable"
} }
this.vhost_https_port = this.serverInfo.vhost_https_port this.vhost_https_port = json.vhost_https_port
if (this.vhost_https_port === 0) { if (this.vhost_https_port == 0) {
this.vhost_https_port = 'disable' this.vhost_https_port = "disable"
} }
this.subdomain_host = this.serverInfo.subdomain_host this.subdomain_host = json.subdomain_host
this.max_pool_count = this.serverInfo.max_pool_count this.max_pool_count = json.max_pool_count
this.max_ports_per_client = this.serverInfo.max_ports_per_client this.max_ports_per_client = json.max_ports_per_client
if (this.max_ports_per_client === 0) { if (this.max_ports_per_client == 0) {
this.max_ports_per_client = 'no limit' this.max_ports_per_client = "no limit"
} }
this.heart_beat_timeout = this.serverInfo.heart_beat_timeout this.heart_beat_timeout = json.heart_beat_timeout
this.client_counts = this.serverInfo.client_counts this.client_counts = json.client_counts
this.cur_conns = this.serverInfo.cur_conns this.cur_conns = json.cur_conns
this.proxy_counts = 0 this.proxy_counts = 0
if (this.serverInfo.proxy_type_count != null) { if (json.proxy_type_count != null) {
if (this.serverInfo.proxy_type_count.tcp != null) { if (json.proxy_type_count.tcp != null) {
this.proxy_counts += this.serverInfo.proxy_type_count.tcp this.proxy_counts += json.proxy_type_count.tcp
} }
if (this.serverInfo.proxy_type_count.udp != null) { if (json.proxy_type_count.udp != null) {
this.proxy_counts += this.serverInfo.proxy_type_count.udp this.proxy_counts += json.proxy_type_count.udp
} }
if (this.serverInfo.proxy_type_count.http != null) { if (json.proxy_type_count.http != null) {
this.proxy_counts += this.serverInfo.proxy_type_count.http this.proxy_counts += json.proxy_type_count.http
} }
if (this.serverInfo.proxy_type_count.https != null) { if (json.proxy_type_count.https != null) {
this.proxy_counts += this.serverInfo.proxy_type_count.https this.proxy_counts += json.proxy_type_count.https
} }
if (this.serverInfo.proxy_type_count.stcp != null) { if (json.proxy_type_count.stcp != null) {
this.proxy_counts += this.serverInfo.proxy_type_count.stcp this.proxy_counts += json.proxy_type_count.stcp
} }
if (this.serverInfo.proxy_type_count.xtcp != null) { if (json.proxy_type_count.xtcp != null) {
this.proxy_counts += this.serverInfo.proxy_type_count.xtcp this.proxy_counts += json.proxy_type_count.xtcp
} }
} }
DrawTrafficChart('traffic', this.serverInfo.total_traffic_in, this.serverInfo.total_traffic_out) DrawTrafficChart('traffic', json.total_traffic_in, json.total_traffic_out)
DrawProxyChart('proxies', this.serverInfo) DrawProxyChart('proxies', json)
}).catch( err => {
this.$message({
showClose: true,
message: 'Get server info from frps failed!',
type: 'warning'
})
})
} }
} }
} }
@ -138,7 +144,7 @@ export default {
.source { .source {
border: 1px solid #eaeefb; border: 1px solid #eaeefb;
border-radius: 4px; border-radius: 4px;
transition: 0.2s; transition: .2s;
padding: 24px; padding: 24px;
} }

View File

@ -3,8 +3,13 @@
<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 ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click"> <el-popover
<my-traffic-chart :proxy-name="props.row.name" /> 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-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>
@ -43,15 +48,40 @@
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Name" prop="name" sortable /> <el-table-column
<el-table-column label="Port" prop="port" 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
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 slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag> <el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag> <el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -59,27 +89,24 @@
</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 { HttpProxy } from '../utils/proxy.js' import {
HttpProxy
} from '../utils/proxy.js'
export default { export default {
components: {
'my-traffic-chart': Traffic
},
data() { data() {
return { return {
proxies: [], proxies: null,
vhost_http_port: '', vhost_http_port: "",
subdomain_host: '' subdomain_host: ""
} }
}, },
computed: { created() {
serverInfo() { this.fetchData()
return this.$store.state.serverInfo
}
}, },
mounted() { watch: {
this.initData() '$route': 'fetchData'
}, },
methods: { methods: {
formatTrafficIn(row, column) { formatTrafficIn(row, column) {
@ -88,20 +115,34 @@ export default {
formatTrafficOut(row, column) { formatTrafficOut(row, column) {
return Humanize.fileSize(row.traffic_out) return Humanize.fileSize(row.traffic_out)
}, },
async initData() { fetchData() {
if (!this.serverInfo) return fetch('/api/serverinfo', {credentials: 'include'})
this.vhost_http_port = this.serverInfo.vhost_http_port .then(res => {
this.subdomain_host = this.serverInfo.subdomain_host return res.json()
if (this.vhost_http_port == null || this.vhost_http_port === 0) return }).then(json => {
this.vhost_http_port = json.vhost_http_port
const json = await this.$fetch('proxy/http') this.subdomain_host = json.subdomain_host
if (!json) return if (this.vhost_http_port == null || this.vhost_http_port == 0) {
return
this.proxies = [] } else {
for (const proxyStats of json.proxies) { 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)) this.proxies.push(new HttpProxy(proxyStats, this.vhost_http_port, this.subdomain_host))
} }
})
} }
})
}
},
components: {
'my-traffic-chart': Traffic
} }
} }
</script> </script>
<style>
</style>

View File

@ -3,8 +3,13 @@
<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 ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click"> <el-popover
<my-traffic-chart :proxy-name="props.row.name" /> 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-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>
@ -37,43 +42,66 @@
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Name" prop="name" sortable /> <el-table-column
<el-table-column label="Port" prop="port" 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
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 slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag> <el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag> <el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </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 { HttpsProxy } from '../utils/proxy.js' import {
HttpsProxy
} from '../utils/proxy.js'
export default { export default {
components: {
'my-traffic-chart': Traffic
},
data() { data() {
return { return {
proxies: [], proxies: null,
vhost_https_port: '', vhost_https_port: '',
subdomain_host: '' subdomain_host: ''
} }
}, },
computed: { created() {
serverInfo() { this.fetchData()
return this.$store.state.serverInfo
}
}, },
mounted() { watch: {
this.initData() '$route': 'fetchData'
}, },
methods: { methods: {
formatTrafficIn(row, column) { formatTrafficIn(row, column) {
@ -82,21 +110,34 @@ export default {
formatTrafficOut(row, column) { formatTrafficOut(row, column) {
return Humanize.fileSize(row.traffic_out) return Humanize.fileSize(row.traffic_out)
}, },
async initData() { fetchData() {
if (!this.serverInfo) return fetch('/api/serverinfo', {credentials: 'include'})
.then(res => {
this.vhost_https_port = this.serverInfo.vhost_https_port return res.json()
this.subdomain_host = this.serverInfo.subdomain_host }).then(json => {
if (this.vhost_https_port == null || this.vhost_https_port === 0) return this.vhost_https_port = json.vhost_https_port
this.subdomain_host = json.subdomain_host
const json = await this.$fetch('proxy/https') if (this.vhost_https_port == null || this.vhost_https_port == 0) {
if (!json) return return
} else {
this.proxies = [] fetch('/api/proxy/https', {credentials: 'include'})
for (const proxyStats of json.proxies) { .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)) this.proxies.push(new HttpsProxy(proxyStats, this.vhost_https_port, this.subdomain_host))
} }
})
} }
})
}
},
components: {
'my-traffic-chart': Traffic
} }
} }
</script> </script>
<style>
</style>

View File

@ -3,13 +3,16 @@
<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 ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click"> <el-popover
<my-traffic-chart :proxy-name="props.row.name" /> 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-popover>
<el-button v-popover:popover4 type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom: 10px" @click="fetchData2"> <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>
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,14 +36,35 @@
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Name" prop="name" sortable /> <el-table-column
<el-table-column label="Connections" prop="conns" sortable /> label="Name"
<el-table-column label="Traffic In" prop="traffic_in" :formatter="formatTrafficIn" sortable /> prop="name"
<el-table-column label="Traffic Out" prop="traffic_out" :formatter="formatTrafficOut" sortable /> sortable>
<el-table-column label="status" prop="status" 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 slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag> <el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag> <el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -52,16 +76,16 @@ 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 {
components: {
'my-traffic-chart': Traffic
},
data() { data() {
return { return {
proxies: [] proxies: null
} }
}, },
mounted() { created() {
this.initData() this.fetchData()
},
watch: {
'$route': 'fetchData'
}, },
methods: { methods: {
formatTrafficIn(row, column) { formatTrafficIn(row, column) {
@ -70,15 +94,20 @@ export default {
formatTrafficOut(row, column) { formatTrafficOut(row, column) {
return Humanize.fileSize(row.traffic_out) return Humanize.fileSize(row.traffic_out)
}, },
async initData() { fetchData() {
const json = await this.$fetch('proxy/stcp') fetch('/api/proxy/stcp', {credentials: 'include'})
if (!json) return .then(res => {
return res.json()
this.proxies = [] }).then(json => {
for (const proxyStats of json.proxies) { this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new StcpProxy(proxyStats)) this.proxies.push(new StcpProxy(proxyStats))
} }
})
} }
},
components: {
'my-traffic-chart': Traffic
} }
} }
</script> </script>

View File

@ -3,14 +3,17 @@
<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 placement="right" width="600" style="margin-left: 0px" trigger="click"> <el-popover
<my-traffic-chart :proxy-name="props.row.name" /> ref="popover4"
placement="right"
<el-button slot="reference" type="primary" size="small" icon="view" :name="props.row.name" style="margin-bottom: 10px"> width="600"
Traffic Statistics style="margin-left:0px"
</el-button> 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-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">
<span>{{ props.row.name }}</span> <span>{{ props.row.name }}</span>
@ -36,15 +39,40 @@
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Name" prop="name" sortable /> <el-table-column
<el-table-column label="Port" prop="port" 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
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 slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag> <el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag> <el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -56,16 +84,16 @@ 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 {
components: {
'my-traffic-chart': Traffic
},
data() { data() {
return { return {
proxies: [] proxies: null
} }
}, },
mounted() { created() {
this.initData() this.fetchData()
},
watch: {
'$route': 'fetchData'
}, },
methods: { methods: {
formatTrafficIn(row, column) { formatTrafficIn(row, column) {
@ -74,15 +102,20 @@ export default {
formatTrafficOut(row, column) { formatTrafficOut(row, column) {
return Humanize.fileSize(row.traffic_out) return Humanize.fileSize(row.traffic_out)
}, },
async initData() { fetchData() {
const json = await this.$fetch('proxy/tcp') fetch('/api/proxy/tcp', {credentials: 'include'})
if (!json) return .then(res => {
return res.json()
this.proxies = [] }).then(json => {
for (const proxyStats of json.proxies) { this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new TcpProxy(proxyStats)) this.proxies.push(new TcpProxy(proxyStats))
} }
})
} }
},
components: {
'my-traffic-chart': Traffic
} }
} }
</script> </script>

View File

@ -3,8 +3,13 @@
<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 ref="popover4" placement="right" width="600" style="margin-left: 0px" trigger="click"> <el-popover
<my-traffic-chart :proxy-name="props.row.name" /> 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-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>
@ -34,15 +39,40 @@
</el-form> </el-form>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="Name" prop="name" sortable /> <el-table-column
<el-table-column label="Port" prop="port" 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
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 slot-scope="scope"> <template slot-scope="scope">
<el-tag v-if="scope.row.status === 'online'" type="success">{{ scope.row.status }}</el-tag> <el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag> <el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -50,20 +80,22 @@
</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 { UdpProxy } from '../utils/proxy.js' import {
UdpProxy
} from '../utils/proxy.js'
export default { export default {
components: {
'my-traffic-chart': Traffic
},
data() { data() {
return { return {
proxies: [] proxies: null
} }
}, },
mounted() { created() {
this.initData() this.fetchData()
},
watch: {
'$route': 'fetchData'
}, },
methods: { methods: {
formatTrafficIn(row, column) { formatTrafficIn(row, column) {
@ -72,16 +104,23 @@ export default {
formatTrafficOut(row, column) { formatTrafficOut(row, column) {
return Humanize.fileSize(row.traffic_out) return Humanize.fileSize(row.traffic_out)
}, },
async initData() { fetchData() {
const json = await this.$fetch('proxy/udp') fetch('/api/proxy/udp', {credentials: 'include'})
if (!json) return .then(res => {
return res.json()
this.proxies = [] }).then(json => {
for (const proxyStats of json.proxies) { this.proxies = new Array()
for (let proxyStats of json.proxies) {
this.proxies.push(new UdpProxy(proxyStats)) this.proxies.push(new UdpProxy(proxyStats))
} }
})
} }
},
components: {
'my-traffic-chart': Traffic
} }
} }
</script> </script>
<style>
</style>

View File

@ -1,26 +1,36 @@
<template> <template>
<div :id="proxyName" style="width: 600px; height: 400px" /> <div :id="proxy_name" style="width: 600px;height:400px;"></div>
</template> </template>
<script> <script>
import {DrawProxyTrafficChart} from '../utils/chart.js' import {DrawProxyTrafficChart} from '../utils/chart.js'
export default { export default {
props: { props: ['proxy_name'],
proxyName: { created() {
type: String, this.fetchData()
required: true
}
},
mounted() {
this.initData()
}, },
//watch: {
//'$route': 'fetchData'
//},
methods: { methods: {
async initData() { fetchData() {
const json = await this.$fetch(`traffic/${this.proxyName}`) let url = '/api/traffic/' + this.proxy_name
if (!json) return fetch(url, {credentials: 'include'})
.then(res => {
DrawProxyTrafficChart(this.proxyName, json.traffic_in, json.traffic_out) 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> </script>
<style>
</style>

15
web/frps/src/index.html Normal file
View 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>

View File

@ -1,6 +1,19 @@
import Vue from 'vue' import Vue from 'vue'
//import ElementUI from 'element-ui' //import ElementUI from 'element-ui'
import { Button, Form, FormItem, Row, Col, Table, TableColumn, Popover, Menu, Submenu, MenuItem, Tag, Message } from 'element-ui' import {
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'
@ -8,7 +21,6 @@ import './utils/less/custom.less'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
import store from '@/store'
import 'whatwg-fetch' import 'whatwg-fetch'
locale.use(lang) locale.use(lang)
@ -25,15 +37,12 @@ Vue.use(Menu)
Vue.use(Submenu) Vue.use(Submenu)
Vue.use(MenuItem) Vue.use(MenuItem)
Vue.use(Tag) Vue.use(Tag)
Vue.prototype.$message = Message
import fetch from '@/utils/fetch'
Vue.prototype.$fetch = fetch
Vue.config.productionTip = false Vue.config.productionTip = false
new Vue({ new Vue({
el: '#app',
router, router,
store, template: '<App/>',
render: h => h(App) components: { App }
}).$mount('#app') })

View File

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

View File

@ -1,24 +0,0 @@
import Vue from 'vue'
import Vuex from 'vuex'
import fetch from '@/utils/fetch'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
serverInfo: null
},
mutations: {
SET_SERVER_INFO(state, serverInfo) {
state.serverInfo = serverInfo
}
},
actions: {
async fetchServerInfo({ commit }) {
const json = await fetch('serverinfo')
commit('SET_SERVER_INFO', json || null)
return json
}
}
})
export default store

View File

@ -1,17 +1,17 @@
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) {
const myChart = echarts.init(document.getElementById(elementId), 'macarons') let myChart = echarts.init(document.getElementById(elementId), 'macarons');
myChart.showLoading() myChart.showLoading()
const option = { let option = {
title: { title: {
text: 'Network Traffic', text: 'Network Traffic',
subtext: 'today', subtext: 'today',
@ -20,24 +20,20 @@ function DrawTrafficChart(elementId, trafficIn, trafficOut) {
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', type: 'pie',
radius: '55%', radius: '55%',
center: ['50%', '60%'], center: ['50%', '60%'],
data: [ data: [{
{
value: trafficIn, value: trafficIn,
name: 'Traffic In' name: 'Traffic In'
}, }, {
{
value: trafficOut, value: trafficOut,
name: 'Traffic Out' name: 'Traffic Out'
} }, ],
],
itemStyle: { itemStyle: {
emphasis: { emphasis: {
shadowBlur: 10, shadowBlur: 10,
@ -45,10 +41,9 @@ function DrawTrafficChart(elementId, trafficIn, trafficOut) {
shadowColor: 'rgba(0, 0, 0, 0.5)' shadowColor: 'rgba(0, 0, 0, 0.5)'
} }
} }
} }]
] };
} myChart.setOption(option);
myChart.setOption(option)
myChart.hideLoading() myChart.hideLoading()
} }
@ -71,10 +66,10 @@ function DrawProxyChart(elementId, serverInfo) {
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
} }
const myChart = echarts.init(document.getElementById(elementId), 'macarons') let myChart = echarts.init(document.getElementById(elementId), 'macarons')
myChart.showLoading() myChart.showLoading()
const option = { let option = {
title: { title: {
text: 'Proxies', text: 'Proxies',
subtext: 'now', subtext: 'now',
@ -86,37 +81,29 @@ function DrawProxyChart(elementId, serverInfo) {
return v.data.value return v.data.value
} }
}, },
series: [ series: [{
{
type: 'pie', type: 'pie',
radius: '55%', radius: '55%',
center: ['50%', '60%'], center: ['50%', '60%'],
data: [ data: [{
{
value: serverInfo.proxy_type_count.tcp, value: serverInfo.proxy_type_count.tcp,
name: 'TCP' name: 'TCP'
}, }, {
{
value: serverInfo.proxy_type_count.udp, value: serverInfo.proxy_type_count.udp,
name: 'UDP' name: 'UDP'
}, }, {
{
value: serverInfo.proxy_type_count.http, value: serverInfo.proxy_type_count.http,
name: 'HTTP' name: 'HTTP'
}, }, {
{
value: serverInfo.proxy_type_count.https, value: serverInfo.proxy_type_count.https,
name: 'HTTPS' name: 'HTTPS'
}, }, {
{
value: serverInfo.proxy_type_count.stcp, value: serverInfo.proxy_type_count.stcp,
name: 'STCP' name: 'STCP'
}, }, {
{
value: serverInfo.proxy_type_count.xtcp, value: serverInfo.proxy_type_count.xtcp,
name: 'XTCP' name: 'XTCP'
} }],
],
itemStyle: { itemStyle: {
emphasis: { emphasis: {
shadowBlur: 10, shadowBlur: 10,
@ -124,34 +111,33 @@ function DrawProxyChart(elementId, serverInfo) {
shadowColor: 'rgba(0, 0, 0, 0.5)' shadowColor: 'rgba(0, 0, 0, 0.5)'
} }
} }
} }]
] };
} myChart.setOption(option);
myChart.setOption(option)
myChart.hideLoading() myChart.hideLoading()
} }
// 7 days // 7 days
function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) { function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
const params = { let params = {
width: '600px', width: '600px',
height: '400px' height: '400px'
} }
const myChart = echarts.init(document.getElementById(elementId), 'macarons', params) let 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)
const dates = [] let dates = new Array()
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)
} }
const option = { let option = {
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
@ -162,9 +148,9 @@ function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
if (data.length > 0) { if (data.length > 0) {
html += data[0].name + '<br/>' html += data[0].name + '<br/>'
} }
for (const v of data) { for (let v of data) {
const colorEl = let colorEl = '<span style="display:inline-block;margin-right:5px;' +
'<span style="display:inline-block;margin-right:5px;' + 'border-radius:10px;width:9px;height:9px;background-color:' + v.color + '"></span>' '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
@ -179,37 +165,35 @@ function DrawProxyTrafficChart(elementId, trafficInArr, trafficOutArr) {
bottom: '3%', bottom: '3%',
containLabel: true containLabel: true
}, },
xAxis: [ xAxis: [{
{
type: 'category', type: 'category',
data: dates data: dates
} }],
], yAxis: [{
yAxis: [
{
type: 'value', type: 'value',
axisLabel: { axisLabel: {
formatter: function(value) { formatter: function(value) {
return Humanize.fileSize(value) return Humanize.fileSize(value)
} }
} }
} }],
], series: [{
series: [
{
name: 'Traffic In', name: 'Traffic In',
type: 'bar', type: 'bar',
data: trafficInArr data: trafficInArr
}, }, {
{
name: 'Traffic Out', name: 'Traffic Out',
type: 'bar', type: 'bar',
data: trafficOutArr data: trafficOutArr
} }]
] };
} myChart.setOption(option);
myChart.setOption(option)
myChart.hideLoading() myChart.hideLoading()
} }
export { DrawTrafficChart, DrawProxyChart, DrawProxyTrafficChart } export {
DrawTrafficChart,
DrawProxyChart,
DrawProxyTrafficChart
}

View File

@ -1,20 +0,0 @@
import { Message } from 'element-ui'
export default function(api, init = {}) {
return new Promise(resolve => {
fetch(`/api/${api}`, Object.assign({ credentials: 'include' }, init))
.then(res => {
if (res.status < 200 || res.status >= 300) {
Message.warning('Get server info from frps failed!')
resolve()
return
}
resolve(res ? res.json() : undefined)
})
.catch(err => {
this.$message.error(err.message)
resolve()
})
})
}

View File

@ -5,8 +5,8 @@ class BaseProxy {
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.conns = proxyStats.cur_conns
this.traffic_in = proxyStats.today_traffic_in this.traffic_in = proxyStats.today_traffic_in
@ -20,13 +20,13 @@ class BaseProxy {
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 = ""
} }
} }
} }
@ -34,13 +34,13 @@ class TcpProxy extends BaseProxy {
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 = ""
} }
} }
} }
@ -48,22 +48,22 @@ class UdpProxy extends BaseProxy {
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 = ""
} }
} }
} }
@ -71,18 +71,18 @@ class HttpProxy extends BaseProxy {
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 = ""
} }
} }
} }
@ -90,7 +90,7 @@ class HttpsProxy extends BaseProxy {
class StcpProxy extends BaseProxy { class StcpProxy extends BaseProxy {
constructor(proxyStats) { constructor(proxyStats) {
super(proxyStats) super(proxyStats)
this.type = 'stcp' this.type = "stcp"
} }
} }

View File

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

107
web/frps/webpack.config.js Normal file
View File

@ -0,0 +1,107 @@
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'
})

File diff suppressed because it is too large Load Diff