mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
web/frps: upgrade vue and element-plus (#3310)
This commit is contained in:
parent
39941117b6
commit
24f0b3afa5
Binary file not shown.
Binary file not shown.
1
assets/frps/static/index-4ce77078.css
Normal file
1
assets/frps/static/index-4ce77078.css
Normal file
File diff suppressed because one or more lines are too long
74
assets/frps/static/index-cd02d3b4.js
Normal file
74
assets/frps/static/index-cd02d3b4.js
Normal file
File diff suppressed because one or more lines are too long
@ -1 +1,16 @@
|
||||
<!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?5d154ba4c6b342d8c0c3"></script><script type="text/javascript" src="vendor.js?ddbd1f69fb6e67be4b78"></script></body> </html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>frps dashboard</title>
|
||||
<script type="module" crossorigin src="./index-cd02d3b4.js"></script>
|
||||
<link rel="stylesheet" href="./index-4ce77078.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -1 +0,0 @@
|
||||
!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:"ddbd1f69fb6e67be4b78"}[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
@ -1,14 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
["es2015", { "modules": false }]
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"component",
|
||||
{
|
||||
"libraryName": "element-ui",
|
||||
"styleLibraryName": "theme-chalk"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
30
web/frps/.eslintrc.cjs
Normal file
30
web/frps/.eslintrc.cjs
Normal file
@ -0,0 +1,30 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier',
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'vue/multi-word-component-names': [
|
||||
'error',
|
||||
{
|
||||
ignores: ['Traffic'],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
30
web/frps/.gitignore
vendored
30
web/frps/.gitignore
vendored
@ -1,6 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.vscode/settings.json
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
5
web/frps/.prettierrc.json
Normal file
5
web/frps/.prettierrc.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
@ -1,7 +1,13 @@
|
||||
.PHONY: dist build
|
||||
.PHONY: dist build preview lint
|
||||
|
||||
build:
|
||||
@npm run build
|
||||
|
||||
dev: install
|
||||
dev:
|
||||
@npm run dev
|
||||
|
||||
preview:
|
||||
@npm run preview
|
||||
|
||||
lint:
|
||||
@npm run lint
|
||||
|
46
web/frps/README.md
Normal file
46
web/frps/README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# frps-dashboard
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
5
web/frps/auto-imports.d.ts
vendored
Normal file
5
web/frps/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
35
web/frps/components.d.ts
vendored
Normal file
35
web/frps/components.d.ts
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ProxiesHTTP: typeof import('./src/components/ProxiesHTTP.vue')['default']
|
||||
ProxiesHTTPS: typeof import('./src/components/ProxiesHTTPS.vue')['default']
|
||||
ProxiesSTCP: typeof import('./src/components/ProxiesSTCP.vue')['default']
|
||||
ProxiesSUDP: typeof import('./src/components/ProxiesSUDP.vue')['default']
|
||||
ProxiesTCP: typeof import('./src/components/ProxiesTCP.vue')['default']
|
||||
ProxiesUDP: typeof import('./src/components/ProxiesUDP.vue')['default']
|
||||
ProxyView: typeof import('./src/components/ProxyView.vue')['default']
|
||||
ProxyViewExpand: typeof import('./src/components/ProxyViewExpand.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
ServerOverview: typeof import('./src/components/ServerOverview.vue')['default']
|
||||
Traffic: typeof import('./src/components/Traffic.vue')['default']
|
||||
}
|
||||
}
|
1
web/frps/env.d.ts
vendored
Normal file
1
web/frps/env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
14
web/frps/index.html
Normal file
14
web/frps/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>frps dashboard</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,48 +1,38 @@
|
||||
{
|
||||
"name": "frps-dashboard",
|
||||
"description": "A dashboard for frp server.",
|
||||
"author": "fatedier",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "webpack-dev-server -d --inline --hot --env.dev",
|
||||
"build": "rimraf dist && webpack -p --progress --hide-modules"
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^3.3.7",
|
||||
"echarts": "^3.5.0",
|
||||
"element-ui": "^2.3.8",
|
||||
"@types/humanize-plus": "^1.8.0",
|
||||
"echarts": "^5.4.1",
|
||||
"element-plus": "^2.2.28",
|
||||
"humanize-plus": "^1.8.2",
|
||||
"vue": "^2.5.16",
|
||||
"vue-resource": "^1.2.1",
|
||||
"vue-router": "^2.3.0",
|
||||
"whatwg-fetch": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"vue": "^3.2.45",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.6.0",
|
||||
"babel-core": "^6.21.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^6.4.0",
|
||||
"babel-plugin-component": "^1.1.1",
|
||||
"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": "^3.0.4",
|
||||
"less-loader": "^4.1.0",
|
||||
"postcss-loader": "^1.3.3",
|
||||
"rimraf": "^2.5.4",
|
||||
"style-loader": "^0.13.2",
|
||||
"url-loader": "^1.0.1",
|
||||
"vue-loader": "^15.0.10",
|
||||
"vue-template-compiler": "^2.1.8",
|
||||
"webpack": "^2.2.0-rc.4",
|
||||
"webpack-dev-server": "^3.1.4"
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/node": "^18.11.12",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.7.1",
|
||||
"typescript": "~4.7.4",
|
||||
"unplugin-auto-import": "^0.13.0",
|
||||
"unplugin-vue-components": "^0.23.0",
|
||||
"vite": "^4.0.4",
|
||||
"vue-tsc": "^1.0.12"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: [
|
||||
require('autoprefixer')()
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
@ -1,81 +1,85 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<header class="grid-content header-color">
|
||||
<el-row>
|
||||
<a class="brand" href="#">frp</a>
|
||||
</el-row>
|
||||
</header>
|
||||
<section>
|
||||
<el-row>
|
||||
<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-menu-item index="/proxies/stcp">STCP</el-menu-item>
|
||||
<el-menu-item index="/proxies/sudp">SUDP</el-menu-item>
|
||||
</el-submenu>
|
||||
<el-menu-item index="">Help</el-menu-item>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
<div id="app">
|
||||
<header class="grid-content header-color">
|
||||
<el-row>
|
||||
<a class="brand" href="#">frp</a>
|
||||
</el-row>
|
||||
</header>
|
||||
<section>
|
||||
<el-row>
|
||||
<el-col id="side-nav" :xs="24" :md="4">
|
||||
<el-menu
|
||||
default-active="/"
|
||||
mode="vertical"
|
||||
theme="light"
|
||||
router="false"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<el-menu-item index="/">Overview</el-menu-item>
|
||||
<el-sub-menu index="/proxies">
|
||||
<template #title>
|
||||
<span>Proxies</span>
|
||||
</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-menu-item index="/proxies/stcp">STCP</el-menu-item>
|
||||
<el-menu-item index="/proxies/sudp">SUDP</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<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>
|
||||
<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("https://github.com/fatedier/frp")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<script setup lang="ts">
|
||||
const handleSelect = (key: string) => {
|
||||
if (key == '') {
|
||||
window.open('https://github.com/fatedier/frp')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
margin: 0px;
|
||||
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;
|
||||
}
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
margin: 0px;
|
||||
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;
|
||||
}
|
||||
</style>
|
||||
|
22
web/frps/src/assets/custom.css
Normal file
22
web/frps/src/assets/custom.css
Normal file
@ -0,0 +1,22 @@
|
||||
.el-form-item span {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.proxy-table-expand {
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.proxy-table-expand .el-form-item__label{
|
||||
width: 90px;
|
||||
color: #99a9bf;
|
||||
}
|
||||
|
||||
.proxy-table-expand .el-form-item {
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.el-table .el-table__expanded-cell {
|
||||
padding: 20px 50px;
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :md="12">
|
||||
<div class="source">
|
||||
<el-form label-position="left" class="server_info">
|
||||
<el-form-item label="Version">
|
||||
<span>{{ version }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="BindPort">
|
||||
<span>{{ bind_port }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="BindUdpPort">
|
||||
<span>{{ bind_udp_port }}</span>
|
||||
</el-form-item>
|
||||
<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="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="Max Ports Per Client">
|
||||
<span>{{ max_ports_per_client }}</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 Connections">
|
||||
<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 {
|
||||
version: '',
|
||||
bind_port: '',
|
||||
bind_udp_port: '',
|
||||
vhost_http_port: '',
|
||||
vhost_https_port: '',
|
||||
subdomain_host: '',
|
||||
max_pool_count: '',
|
||||
max_ports_per_client: '',
|
||||
heart_beat_timeout: '',
|
||||
client_counts: '',
|
||||
cur_conns: '',
|
||||
proxy_counts: ''
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
watch: {
|
||||
'$route': 'fetchData'
|
||||
},
|
||||
methods: {
|
||||
fetchData() {
|
||||
fetch('../api/serverinfo', {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
this.version = json.version
|
||||
this.bind_port = json.bind_port
|
||||
this.bind_udp_port = json.bind_udp_port
|
||||
if (this.bind_udp_port == 0) {
|
||||
this.bind_udp_port = "disable"
|
||||
}
|
||||
this.vhost_http_port = json.vhost_http_port
|
||||
if (this.vhost_http_port == 0) {
|
||||
this.vhost_http_port = "disable"
|
||||
}
|
||||
this.vhost_https_port = json.vhost_https_port
|
||||
if (this.vhost_https_port == 0) {
|
||||
this.vhost_https_port = "disable"
|
||||
}
|
||||
this.subdomain_host = json.subdomain_host
|
||||
this.max_pool_count = json.max_pool_count
|
||||
this.max_ports_per_client = json.max_ports_per_client
|
||||
if (this.max_ports_per_client == 0) {
|
||||
this.max_ports_per_client = "no limit"
|
||||
}
|
||||
this.heart_beat_timeout = json.heart_beat_timeout
|
||||
this.client_counts = json.client_counts
|
||||
this.cur_conns = json.cur_conns
|
||||
this.proxy_counts = 0
|
||||
if (json.proxy_type_count != null) {
|
||||
if (json.proxy_type_count.tcp != null) {
|
||||
this.proxy_counts += json.proxy_type_count.tcp
|
||||
}
|
||||
if (json.proxy_type_count.udp != null) {
|
||||
this.proxy_counts += json.proxy_type_count.udp
|
||||
}
|
||||
if (json.proxy_type_count.http != null) {
|
||||
this.proxy_counts += json.proxy_type_count.http
|
||||
}
|
||||
if (json.proxy_type_count.https != null) {
|
||||
this.proxy_counts += json.proxy_type_count.https
|
||||
}
|
||||
if (json.proxy_type_count.stcp != null) {
|
||||
this.proxy_counts += json.proxy_type_count.stcp
|
||||
}
|
||||
if (json.proxy_type_count.sudp != null) {
|
||||
this.proxy_counts += json.proxy_type_count.sudp
|
||||
}
|
||||
if (json.proxy_type_count.xtcp != null) {
|
||||
this.proxy_counts += json.proxy_type_count.xtcp
|
||||
}
|
||||
}
|
||||
DrawTrafficChart('traffic', json.total_traffic_in, json.total_traffic_out)
|
||||
DrawProxyChart('proxies', json)
|
||||
}).catch( err => {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: 'Get server info from frps failed!',
|
||||
type: 'warning'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
@ -1,148 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||
<el-table-column type="expand">
|
||||
<template slot-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-item label="Last Start">
|
||||
<span>{{ props.row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ props.row.last_close_time }}</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 slot-scope="scope">
|
||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<ProxyView :proxies="proxies" proxyType="http" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Humanize from 'humanize-plus';
|
||||
import Traffic from './Traffic.vue'
|
||||
import {
|
||||
HttpProxy
|
||||
} from '../utils/proxy.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: new Array(),
|
||||
vhost_http_port: "",
|
||||
subdomain_host: ""
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { HTTPProxy } from '../utils/proxy.js'
|
||||
import ProxyView from './ProxyView.vue'
|
||||
|
||||
let proxies = ref<HTTPProxy[]>([])
|
||||
|
||||
const fetchData = () => {
|
||||
let vhost_http_port: number
|
||||
let subdomain_host: string
|
||||
fetch('../api/serverinfo', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
vhost_http_port = json.vhost_http_port
|
||||
subdomain_host = json.subdomain_host
|
||||
if (vhost_http_port == null || vhost_http_port == 0) {
|
||||
return
|
||||
}
|
||||
},
|
||||
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', {credentials: 'include'})
|
||||
.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', {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
this.proxies = new Array()
|
||||
for (let proxyStats of json.proxies) {
|
||||
this.proxies.push(new HttpProxy(proxyStats, this.vhost_http_port, this.subdomain_host))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'my-traffic-chart': Traffic
|
||||
}
|
||||
}
|
||||
fetch('../api/proxy/http', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
for (let proxyStats of json.proxies) {
|
||||
proxies.value.push(
|
||||
new HTTPProxy(proxyStats, vhost_http_port, subdomain_host)
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
|
@ -1,143 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||
<el-table-column type="expand">
|
||||
<template slot-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-item label="Last Start">
|
||||
<span>{{ props.row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ props.row.last_close_time }}</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 slot-scope="scope">
|
||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
</div>
|
||||
<ProxyView :proxies="proxies" proxyType="https" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Humanize from 'humanize-plus';
|
||||
import Traffic from './Traffic.vue'
|
||||
import {
|
||||
HttpsProxy
|
||||
} from '../utils/proxy.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: new Array(),
|
||||
vhost_https_port: '',
|
||||
subdomain_host: ''
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { HTTPSProxy } from '../utils/proxy.js'
|
||||
import ProxyView from './ProxyView.vue'
|
||||
|
||||
let proxies = ref<HTTPSProxy[]>([])
|
||||
|
||||
const fetchData = () => {
|
||||
let vhost_https_port: number
|
||||
let subdomain_host: string
|
||||
fetch('../api/serverinfo', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
vhost_https_port = json.vhost_https_port
|
||||
subdomain_host = json.subdomain_host
|
||||
if (vhost_https_port == null || vhost_https_port == 0) {
|
||||
return
|
||||
}
|
||||
},
|
||||
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', {credentials: 'include'})
|
||||
.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', {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
this.proxies = new Array()
|
||||
for (let proxyStats of json.proxies) {
|
||||
this.proxies.push(new HttpsProxy(proxyStats, this.vhost_https_port, this.subdomain_host))
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'my-traffic-chart': Traffic
|
||||
}
|
||||
}
|
||||
fetch('../api/proxy/https', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
for (let proxyStats of json.proxies) {
|
||||
proxies.value.push(
|
||||
new HTTPSProxy(proxyStats, vhost_https_port, subdomain_host)
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
|
@ -1,116 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||
<el-table-column type="expand">
|
||||
<template slot-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="Encryption">
|
||||
<span>{{ props.row.encryption }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Compression">
|
||||
<span>{{ props.row.compression }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Start">
|
||||
<span>{{ props.row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ props.row.last_close_time }}</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="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">
|
||||
<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>
|
||||
<ProxyView :proxies="proxies" proxyType="stcp" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Humanize from 'humanize-plus'
|
||||
import Traffic from './Traffic.vue'
|
||||
import { StcpProxy } from '../utils/proxy.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: new Array(),
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { STCPProxy } from '../utils/proxy.js'
|
||||
import ProxyView from './ProxyView.vue'
|
||||
|
||||
let proxies = ref<STCPProxy[]>([])
|
||||
|
||||
const fetchData = () => {
|
||||
fetch('../api/proxy/stcp', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
for (let proxyStats of json.proxies) {
|
||||
proxies.value.push(new STCPProxy(proxyStats))
|
||||
}
|
||||
},
|
||||
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/stcp', {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
this.proxies = new Array()
|
||||
for (let proxyStats of json.proxies) {
|
||||
this.proxies.push(new StcpProxy(proxyStats))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'my-traffic-chart': Traffic
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
|
@ -1,116 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||
<el-table-column type="expand">
|
||||
<template slot-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="Encryption">
|
||||
<span>{{ props.row.encryption }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Compression">
|
||||
<span>{{ props.row.compression }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Start">
|
||||
<span>{{ props.row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ props.row.last_close_time }}</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="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">
|
||||
<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>
|
||||
<ProxyView :proxies="proxies" proxyType="sudp" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Humanize from 'humanize-plus'
|
||||
import Traffic from './Traffic.vue'
|
||||
import { SudpProxy } from '../utils/proxy.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: new Array(),
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { SUDPProxy } from '../utils/proxy.js'
|
||||
import ProxyView from './ProxyView.vue'
|
||||
|
||||
let proxies = ref<SUDPProxy[]>([])
|
||||
|
||||
const fetchData = () => {
|
||||
fetch('../api/proxy/sudp', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
for (let proxyStats of json.proxies) {
|
||||
proxies.value.push(new SUDPProxy(proxyStats))
|
||||
}
|
||||
},
|
||||
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/sudp', {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
this.proxies = new Array()
|
||||
for (let proxyStats of json.proxies) {
|
||||
this.proxies.push(new SudpProxy(proxyStats))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'my-traffic-chart': Traffic
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
|
@ -1,124 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||
<el-table-column type="expand">
|
||||
<template slot-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-item label="Last Start">
|
||||
<span>{{ props.row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ props.row.last_close_time }}</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 slot-scope="scope">
|
||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<ProxyView :proxies="proxies" proxyType="tcp" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Humanize from 'humanize-plus'
|
||||
import Traffic from './Traffic.vue'
|
||||
import { TcpProxy } from '../utils/proxy.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: new Array(),
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { TCPProxy } from '../utils/proxy.js'
|
||||
import ProxyView from './ProxyView.vue'
|
||||
|
||||
let proxies = ref<TCPProxy[]>([])
|
||||
|
||||
const fetchData = () => {
|
||||
fetch('../api/proxy/tcp', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
for (let proxyStats of json.proxies) {
|
||||
proxies.value.push(new TCPProxy(proxyStats))
|
||||
}
|
||||
},
|
||||
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', {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
this.proxies = new Array()
|
||||
for (let proxyStats of json.proxies) {
|
||||
this.proxies.push(new TcpProxy(proxyStats))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'my-traffic-chart': Traffic
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
|
@ -1,126 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table :data="proxies" :default-sort="{prop: 'name', order: 'ascending'}" style="width: 100%">
|
||||
<el-table-column type="expand">
|
||||
<template slot-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-item label="Last Start">
|
||||
<span>{{ props.row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ props.row.last_close_time }}</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 slot-scope="scope">
|
||||
<el-tag type="success" v-if="scope.row.status === 'online'">{{ scope.row.status }}</el-tag>
|
||||
<el-tag type="danger" v-else>{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<ProxyView :proxies="proxies" proxyType="udp" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Humanize from 'humanize-plus';
|
||||
import Traffic from './Traffic.vue'
|
||||
import {
|
||||
UdpProxy
|
||||
} from '../utils/proxy.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
proxies: new Array(),
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { UDPProxy } from '../utils/proxy.js'
|
||||
import ProxyView from './ProxyView.vue'
|
||||
|
||||
let proxies = ref<UDPProxy[]>([])
|
||||
|
||||
const fetchData = () => {
|
||||
fetch('../api/proxy/udp', { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
for (let proxyStats of json.proxies) {
|
||||
proxies.value.push(new UDPProxy(proxyStats))
|
||||
}
|
||||
},
|
||||
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', {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
this.proxies = new Array()
|
||||
for (let proxyStats of json.proxies) {
|
||||
this.proxies.push(new UdpProxy(proxyStats))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'my-traffic-chart': Traffic
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
|
91
web/frps/src/components/ProxyView.vue
Normal file
91
web/frps/src/components/ProxyView.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table
|
||||
:data="proxies"
|
||||
:default-sort="{ prop: 'name', order: 'ascending' }"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="expand">
|
||||
<template #default="props">
|
||||
<el-popover
|
||||
ref="popoverTraffic"
|
||||
:virtual-ref="buttonTraffic"
|
||||
placement="right"
|
||||
width="600"
|
||||
style="margin-left: 0px"
|
||||
trigger="click"
|
||||
virtual-triggering
|
||||
>
|
||||
<Traffic :proxy_name="props.row.name" />
|
||||
</el-popover>
|
||||
|
||||
<el-button
|
||||
ref="buttonTraffic"
|
||||
type="primary"
|
||||
size="large"
|
||||
:name="props.row.name"
|
||||
style="margin-bottom: 10px"
|
||||
v-click-outside="onClickTrafficStats"
|
||||
>Traffic Statistics
|
||||
</el-button>
|
||||
|
||||
<ProxyViewExpand :row="props.row" :proxyType="proxyType" />
|
||||
</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 #default="scope">
|
||||
<el-tag v-if="scope.row.status === 'online'" type="success">{{
|
||||
scope.row.status
|
||||
}}</el-tag>
|
||||
<el-tag v-else type="danger">{{ scope.row.status }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, unref } from 'vue'
|
||||
import * as Humanize from 'humanize-plus'
|
||||
import type { TableColumnCtx } from 'element-plus'
|
||||
import type { BaseProxy } from '../utils/proxy.js'
|
||||
import ProxyViewExpand from './ProxyViewExpand.vue'
|
||||
|
||||
defineProps<{
|
||||
proxies: BaseProxy[]
|
||||
proxyType: string
|
||||
}>()
|
||||
|
||||
const formatTrafficIn = (row: BaseProxy, _: TableColumnCtx<BaseProxy>) => {
|
||||
return Humanize.fileSize(row.traffic_in)
|
||||
}
|
||||
|
||||
const formatTrafficOut = (row: BaseProxy, _: TableColumnCtx<BaseProxy>) => {
|
||||
return Humanize.fileSize(row.traffic_out)
|
||||
}
|
||||
|
||||
const buttonTraffic = ref()
|
||||
const popoverTraffic = ref()
|
||||
|
||||
const onClickTrafficStats = () => {
|
||||
unref(popoverTraffic).popoverTraffic?.delayHide?.()
|
||||
}
|
||||
</script>
|
70
web/frps/src/components/ProxyViewExpand.vue
Normal file
70
web/frps/src/components/ProxyViewExpand.vue
Normal file
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<el-form
|
||||
label-position="left"
|
||||
inline
|
||||
class="proxy-table-expand"
|
||||
v-if="proxyType === 'http' || proxyType === 'https'"
|
||||
>
|
||||
<el-form-item label="Name">
|
||||
<span>{{ row.name }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Type">
|
||||
<span>{{ row.type }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Domains">
|
||||
<span>{{ row.custom_domains }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="SubDomain">
|
||||
<span>{{ row.subdomain }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="locations">
|
||||
<span>{{ row.locations }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="HostRewrite">
|
||||
<span>{{ row.host_header_rewrite }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Encryption">
|
||||
<span>{{ row.encryption }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Compression">
|
||||
<span>{{ row.compression }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Start">
|
||||
<span>{{ row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ row.last_close_time }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-form label-position="left" inline class="proxy-table-expand" v-else>
|
||||
<el-form-item label="Name">
|
||||
<span>{{ row.name }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Type">
|
||||
<span>{{ row.type }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Addr">
|
||||
<span>{{ row.addr }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Encryption">
|
||||
<span>{{ row.encryption }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Compression">
|
||||
<span>{{ row.compression }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Start">
|
||||
<span>{{ row.last_start_time }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Last Close">
|
||||
<span>{{ row.last_close_time }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
row: any
|
||||
proxyType: string
|
||||
}>()
|
||||
</script>
|
177
web/frps/src/components/ServerOverview.vue
Normal file
177
web/frps/src/components/ServerOverview.vue
Normal file
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :md="12">
|
||||
<div class="source">
|
||||
<el-form
|
||||
label-position="left"
|
||||
label-width="160px"
|
||||
class="server_info"
|
||||
>
|
||||
<el-form-item label="Version">
|
||||
<span>{{ data.version }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="BindPort">
|
||||
<span>{{ data.bind_port }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="BindUdpPort">
|
||||
<span>{{ data.bind_udp_port }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Http Port">
|
||||
<span>{{ data.vhost_http_port }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Https Port">
|
||||
<span>{{ data.vhost_https_port }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Subdomain Host">
|
||||
<span>{{ data.subdomain_host }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Max PoolCount">
|
||||
<span>{{ data.max_pool_count }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Max Ports Per Client">
|
||||
<span>{{ data.max_ports_per_client }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="HeartBeat Timeout">
|
||||
<span>{{ data.heart_beat_timeout }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Client Counts">
|
||||
<span>{{ data.client_counts }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Current Connections">
|
||||
<span>{{ data.cur_conns }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="Proxy Counts">
|
||||
<span>{{ data.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 setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DrawTrafficChart, DrawProxyChart } from '../utils/chart'
|
||||
|
||||
let data = ref({
|
||||
version: '',
|
||||
bind_port: '',
|
||||
bind_udp_port: '',
|
||||
vhost_http_port: '',
|
||||
vhost_https_port: '',
|
||||
subdomain_host: '',
|
||||
max_pool_count: '',
|
||||
max_ports_per_client: '',
|
||||
heart_beat_timeout: '',
|
||||
client_counts: '',
|
||||
cur_conns: '',
|
||||
proxy_counts: 0,
|
||||
})
|
||||
|
||||
const fetchData = () => {
|
||||
fetch('../api/serverinfo', { credentials: 'include' })
|
||||
.then((res) => res.json())
|
||||
.then((json) => {
|
||||
data.value.version = json.version
|
||||
data.value.bind_port = json.bind_port
|
||||
data.value.bind_udp_port = json.bind_udp_port
|
||||
if (data.value.bind_udp_port == '0') {
|
||||
data.value.bind_udp_port = 'disable'
|
||||
}
|
||||
data.value.vhost_http_port = json.vhost_http_port
|
||||
if (data.value.vhost_http_port == '0') {
|
||||
data.value.vhost_http_port = 'disable'
|
||||
}
|
||||
data.value.vhost_https_port = json.vhost_https_port
|
||||
if (data.value.vhost_https_port == '0') {
|
||||
data.value.vhost_https_port = 'disable'
|
||||
}
|
||||
data.value.subdomain_host = json.subdomain_host
|
||||
data.value.max_pool_count = json.max_pool_count
|
||||
data.value.max_ports_per_client = json.max_ports_per_client
|
||||
if (data.value.max_ports_per_client == '0') {
|
||||
data.value.max_ports_per_client = 'no limit'
|
||||
}
|
||||
data.value.heart_beat_timeout = json.heart_beat_timeout
|
||||
data.value.client_counts = json.client_counts
|
||||
data.value.cur_conns = json.cur_conns
|
||||
data.value.proxy_counts = 0
|
||||
if (json.proxy_type_count != null) {
|
||||
if (json.proxy_type_count.tcp != null) {
|
||||
data.value.proxy_counts += json.proxy_type_count.tcp
|
||||
}
|
||||
if (json.proxy_type_count.udp != null) {
|
||||
data.value.proxy_counts += json.proxy_type_count.udp
|
||||
}
|
||||
if (json.proxy_type_count.http != null) {
|
||||
data.value.proxy_counts += json.proxy_type_count.http
|
||||
}
|
||||
if (json.proxy_type_count.https != null) {
|
||||
data.value.proxy_counts += json.proxy_type_count.https
|
||||
}
|
||||
if (json.proxy_type_count.stcp != null) {
|
||||
data.value.proxy_counts += json.proxy_type_count.stcp
|
||||
}
|
||||
if (json.proxy_type_count.sudp != null) {
|
||||
data.value.proxy_counts += json.proxy_type_count.sudp
|
||||
}
|
||||
if (json.proxy_type_count.xtcp != null) {
|
||||
data.value.proxy_counts += json.proxy_type_count.xtcp
|
||||
}
|
||||
}
|
||||
|
||||
// draw chart
|
||||
DrawTrafficChart('traffic', json.total_traffic_in, json.total_traffic_out)
|
||||
DrawProxyChart('proxies', json)
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: 'Get server info from frps failed!',
|
||||
type: 'warning',
|
||||
})
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.source {
|
||||
border: 1px solid #eaeefb;
|
||||
border-radius: 4px;
|
||||
transition: 0.2s;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.server_info {
|
||||
margin-left: 40px;
|
||||
font-size: 0px;
|
||||
}
|
||||
|
||||
.server_info .el-form-item__label {
|
||||
color: #99a9bf;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.server_info .el-form-item__content {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.server_info .el-form-item {
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
@ -1,36 +1,32 @@
|
||||
<template>
|
||||
<div :id="proxy_name" style="width: 600px;height:400px;"></div>
|
||||
<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/traffic/' + this.proxy_name
|
||||
fetch(url, {credentials: 'include'})
|
||||
.then(res => {
|
||||
return res.json()
|
||||
}).then(json => {
|
||||
DrawProxyTrafficChart(this.proxy_name, json.traffic_in, json.traffic_out)
|
||||
}).catch( err => {
|
||||
this.$message({
|
||||
showClose: true,
|
||||
message: 'Get server info from frps failed!' + err,
|
||||
type: 'warning'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script setup lang="ts">
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { DrawProxyTrafficChart } from '../utils/chart.js'
|
||||
|
||||
<style>
|
||||
</style>
|
||||
const props = defineProps<{
|
||||
proxy_name: string
|
||||
}>()
|
||||
|
||||
const fetchData = () => {
|
||||
let url = '../api/traffic/' + props.proxy_name
|
||||
fetch(url, { credentials: 'include' })
|
||||
.then((res) => {
|
||||
return res.json()
|
||||
})
|
||||
.then((json) => {
|
||||
DrawProxyTrafficChart(props.proxy_name, json.traffic_in, json.traffic_out)
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: 'Get traffic info failed!' + err,
|
||||
type: 'warning',
|
||||
})
|
||||
})
|
||||
}
|
||||
fetchData()
|
||||
</script>
|
||||
<style></style>
|
||||
|
@ -1,15 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>frps dashboard</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!--<script src="https://code.jquery.com/jquery-3.2.0.min.js"></script>-->
|
||||
<!--<script src="//cdn.bootcss.com/echarts/3.4.0/echarts.min.js"></script>-->
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,48 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
//import ElementUI 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 locale from 'element-ui/lib/locale'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import './utils/less/custom.less'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import 'whatwg-fetch'
|
||||
|
||||
locale.use(lang)
|
||||
|
||||
Vue.use(Button)
|
||||
Vue.use(Form)
|
||||
Vue.use(FormItem)
|
||||
Vue.use(Row)
|
||||
Vue.use(Col)
|
||||
Vue.use(Table)
|
||||
Vue.use(TableColumn)
|
||||
Vue.use(Popover)
|
||||
Vue.use(Menu)
|
||||
Vue.use(Submenu)
|
||||
Vue.use(MenuItem)
|
||||
Vue.use(Tag)
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
template: '<App/>',
|
||||
components: { App }
|
||||
})
|
12
web/frps/src/main.ts
Normal file
12
web/frps/src/main.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { createApp } from 'vue'
|
||||
import 'element-plus/dist/index.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import './assets/custom.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
@ -1,43 +0,0 @@
|
||||
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'
|
||||
import ProxiesStcp from '../components/ProxiesStcp.vue'
|
||||
import ProxiesSudp from '../components/ProxiesSudp.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
|
||||
}, {
|
||||
path: '/proxies/stcp',
|
||||
name: 'ProxiesStcp',
|
||||
component: ProxiesStcp
|
||||
}, {
|
||||
path: '/proxies/sudp',
|
||||
name: 'ProxiesSudp',
|
||||
component: ProxiesSudp
|
||||
}]
|
||||
})
|
51
web/frps/src/router/index.ts
Normal file
51
web/frps/src/router/index.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import ServerOverview from '../components/ServerOverview.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'
|
||||
import ProxiesSTCP from '../components/ProxiesSTCP.vue'
|
||||
import ProxiesSUDP from '../components/ProxiesSUDP.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'ServerOverview',
|
||||
component: ServerOverview,
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
{
|
||||
path: '/proxies/stcp',
|
||||
name: 'ProxiesSTCP',
|
||||
component: ProxiesSTCP,
|
||||
},
|
||||
{
|
||||
path: '/proxies/sudp',
|
||||
name: 'ProxiesSUDP',
|
||||
component: ProxiesSUDP,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export default router
|
@ -1,186 +0,0 @@
|
||||
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) {
|
||||
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: [],
|
||||
itemStyle: {
|
||||
emphasis: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
if (serverInfo.proxy_type_count.tcp != null && serverInfo.proxy_type_count.tcp != 0) {
|
||||
option.series[0].data.push({value: serverInfo.proxy_type_count.tcp, name: 'TCP'})
|
||||
}
|
||||
if (serverInfo.proxy_type_count.udp != null && serverInfo.proxy_type_count.udp != 0) {
|
||||
option.series[0].data.push({value: serverInfo.proxy_type_count.udp, name: 'UDP'})
|
||||
}
|
||||
if (serverInfo.proxy_type_count.http != null && serverInfo.proxy_type_count.http != 0) {
|
||||
option.series[0].data.push({value: serverInfo.proxy_type_count.http, name: 'HTTP'})
|
||||
}
|
||||
if (serverInfo.proxy_type_count.https != null && serverInfo.proxy_type_count.https != 0) {
|
||||
option.series[0].data.push({value: serverInfo.proxy_type_count.https, name: 'HTTPS'})
|
||||
}
|
||||
if (serverInfo.proxy_type_count.stcp != null && serverInfo.proxy_type_count.stcp != 0) {
|
||||
option.series[0].data.push({value: serverInfo.proxy_type_count.stcp, name: 'STCP'})
|
||||
}
|
||||
if (serverInfo.proxy_type_count.sudp != null && serverInfo.proxy_type_count.sudp != 0) {
|
||||
option.series[0].data.push({value: serverInfo.proxy_type_count.sudp, name: 'SUDP'})
|
||||
}
|
||||
if (serverInfo.proxy_type_count.xtcp != null && serverInfo.proxy_type_count.xtcp != 0) {
|
||||
option.series[0].data.push({value: serverInfo.proxy_type_count.xtcp, name: 'XTCP'})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
293
web/frps/src/utils/chart.ts
Normal file
293
web/frps/src/utils/chart.ts
Normal file
@ -0,0 +1,293 @@
|
||||
import * as Humanize from 'humanize-plus'
|
||||
import * as echarts from 'echarts/core'
|
||||
import { PieChart, BarChart } from 'echarts/charts'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { LabelLayout } from 'echarts/features'
|
||||
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent,
|
||||
} from 'echarts/components'
|
||||
|
||||
echarts.use([
|
||||
PieChart,
|
||||
BarChart,
|
||||
CanvasRenderer,
|
||||
LabelLayout,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent,
|
||||
])
|
||||
|
||||
function DrawTrafficChart(
|
||||
elementId: string,
|
||||
trafficIn: number,
|
||||
trafficOut: number
|
||||
) {
|
||||
const myChart = echarts.init(
|
||||
document.getElementById(elementId) as HTMLElement,
|
||||
'macarons'
|
||||
)
|
||||
myChart.showLoading()
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: 'Network Traffic',
|
||||
subtext: 'today',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (v: any) {
|
||||
return Humanize.fileSize(v.data.value) + ' (' + v.percent + '%)'
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: ['Traffic In', 'Traffic Out'],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '60%'],
|
||||
data: [
|
||||
{
|
||||
value: trafficIn,
|
||||
name: 'Traffic In',
|
||||
},
|
||||
{
|
||||
value: trafficOut,
|
||||
name: 'Traffic Out',
|
||||
},
|
||||
],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
myChart.setOption(option)
|
||||
myChart.hideLoading()
|
||||
}
|
||||
|
||||
function DrawProxyChart(elementId: string, serverInfo: any) {
|
||||
const myChart = echarts.init(
|
||||
document.getElementById(elementId) as HTMLElement,
|
||||
'macarons'
|
||||
)
|
||||
myChart.showLoading()
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: 'Proxies',
|
||||
subtext: 'now',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: function (v: any) {
|
||||
return String(v.data.value)
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: <string[]>[],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['50%', '60%'],
|
||||
data: <any[]>[],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
if (
|
||||
serverInfo.proxy_type_count.tcp != null &&
|
||||
serverInfo.proxy_type_count.tcp != 0
|
||||
) {
|
||||
option.series[0].data.push({
|
||||
value: serverInfo.proxy_type_count.tcp,
|
||||
name: 'TCP',
|
||||
})
|
||||
option.legend.data.push('TCP')
|
||||
}
|
||||
if (
|
||||
serverInfo.proxy_type_count.udp != null &&
|
||||
serverInfo.proxy_type_count.udp != 0
|
||||
) {
|
||||
option.series[0].data.push({
|
||||
value: serverInfo.proxy_type_count.udp,
|
||||
name: 'UDP',
|
||||
})
|
||||
option.legend.data.push('UDP')
|
||||
}
|
||||
if (
|
||||
serverInfo.proxy_type_count.http != null &&
|
||||
serverInfo.proxy_type_count.http != 0
|
||||
) {
|
||||
option.series[0].data.push({
|
||||
value: serverInfo.proxy_type_count.http,
|
||||
name: 'HTTP',
|
||||
})
|
||||
option.legend.data.push('HTTP')
|
||||
}
|
||||
if (
|
||||
serverInfo.proxy_type_count.https != null &&
|
||||
serverInfo.proxy_type_count.https != 0
|
||||
) {
|
||||
option.series[0].data.push({
|
||||
value: serverInfo.proxy_type_count.https,
|
||||
name: 'HTTPS',
|
||||
})
|
||||
option.legend.data.push('HTTPS')
|
||||
}
|
||||
if (
|
||||
serverInfo.proxy_type_count.stcp != null &&
|
||||
serverInfo.proxy_type_count.stcp != 0
|
||||
) {
|
||||
option.series[0].data.push({
|
||||
value: serverInfo.proxy_type_count.stcp,
|
||||
name: 'STCP',
|
||||
})
|
||||
option.legend.data.push('STCP')
|
||||
}
|
||||
if (
|
||||
serverInfo.proxy_type_count.sudp != null &&
|
||||
serverInfo.proxy_type_count.sudp != 0
|
||||
) {
|
||||
option.series[0].data.push({
|
||||
value: serverInfo.proxy_type_count.sudp,
|
||||
name: 'SUDP',
|
||||
})
|
||||
option.legend.data.push('SUDP')
|
||||
}
|
||||
if (
|
||||
serverInfo.proxy_type_count.xtcp != null &&
|
||||
serverInfo.proxy_type_count.xtcp != 0
|
||||
) {
|
||||
option.series[0].data.push({
|
||||
value: serverInfo.proxy_type_count.xtcp,
|
||||
name: 'XTCP',
|
||||
})
|
||||
option.legend.data.push('XTCP')
|
||||
}
|
||||
|
||||
myChart.setOption(option)
|
||||
myChart.hideLoading()
|
||||
}
|
||||
|
||||
// 7 days
|
||||
function DrawProxyTrafficChart(
|
||||
elementId: string,
|
||||
trafficInArr: number[],
|
||||
trafficOutArr: number[]
|
||||
) {
|
||||
const params = {
|
||||
width: '600px',
|
||||
height: '400px',
|
||||
}
|
||||
|
||||
const myChart = echarts.init(
|
||||
document.getElementById(elementId) as HTMLElement,
|
||||
'macarons',
|
||||
params
|
||||
)
|
||||
myChart.showLoading()
|
||||
|
||||
trafficInArr = trafficInArr.reverse()
|
||||
trafficOutArr = trafficOutArr.reverse()
|
||||
let now = new Date()
|
||||
now = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 6)
|
||||
const dates: Array<string> = []
|
||||
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)
|
||||
}
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
formatter: function (data: any) {
|
||||
let html = ''
|
||||
if (data.length > 0) {
|
||||
html += data[0].name + '<br/>'
|
||||
}
|
||||
for (const v of data) {
|
||||
const 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: number) {
|
||||
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 }
|
@ -1,22 +0,0 @@
|
||||
@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%;
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
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.last_start_time = proxyStats.last_start_time
|
||||
this.last_close_time = proxyStats.last_close_time
|
||||
this.status = proxyStats.status
|
||||
}
|
||||
}
|
||||
|
||||
class TcpProxy extends BaseProxy {
|
||||
constructor(proxyStats) {
|
||||
super(proxyStats)
|
||||
this.type = "tcp"
|
||||
if (proxyStats.conf != null) {
|
||||
this.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.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.subdomain != "") {
|
||||
this.subdomain = proxyStats.conf.subdomain + "." + 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.subdomain != "") {
|
||||
this.subdomain = proxyStats.conf.subdomain + "." + subdomain_host
|
||||
} else {
|
||||
this.subdomain = ""
|
||||
}
|
||||
} else {
|
||||
this.custom_domains = ""
|
||||
this.subdomain = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StcpProxy extends BaseProxy {
|
||||
constructor(proxyStats) {
|
||||
super(proxyStats)
|
||||
this.type = "stcp"
|
||||
}
|
||||
}
|
||||
|
||||
class SudpProxy extends BaseProxy {
|
||||
constructor(proxyStats) {
|
||||
super(proxyStats)
|
||||
this.type = "sudp"
|
||||
}
|
||||
}
|
||||
|
||||
export {BaseProxy, TcpProxy, UdpProxy, HttpProxy, HttpsProxy, StcpProxy, SudpProxy}
|
138
web/frps/src/utils/proxy.ts
Normal file
138
web/frps/src/utils/proxy.ts
Normal file
@ -0,0 +1,138 @@
|
||||
class BaseProxy {
|
||||
name: string
|
||||
type: string
|
||||
encryption: boolean
|
||||
compression: boolean
|
||||
conns: number
|
||||
traffic_in: number
|
||||
traffic_out: number
|
||||
last_start_time: string
|
||||
last_close_time: string
|
||||
status: string
|
||||
addr: string
|
||||
port: number
|
||||
|
||||
custom_domains: string
|
||||
host_header_rewrite: string
|
||||
locations: string
|
||||
subdomain: string
|
||||
|
||||
constructor(proxyStats: any) {
|
||||
this.name = proxyStats.name
|
||||
this.type = ''
|
||||
if (proxyStats.conf != null) {
|
||||
this.encryption = proxyStats.conf.use_encryption
|
||||
this.compression = proxyStats.conf.use_compression
|
||||
} else {
|
||||
this.encryption = false
|
||||
this.compression = false
|
||||
}
|
||||
this.conns = proxyStats.cur_conns
|
||||
this.traffic_in = proxyStats.today_traffic_in
|
||||
this.traffic_out = proxyStats.today_traffic_out
|
||||
this.last_start_time = proxyStats.last_start_time
|
||||
this.last_close_time = proxyStats.last_close_time
|
||||
this.status = proxyStats.status
|
||||
|
||||
this.addr = ''
|
||||
this.port = 0
|
||||
this.custom_domains = ''
|
||||
this.host_header_rewrite = ''
|
||||
this.locations = ''
|
||||
this.subdomain = ''
|
||||
}
|
||||
}
|
||||
|
||||
class TCPProxy extends BaseProxy {
|
||||
constructor(proxyStats: any) {
|
||||
super(proxyStats)
|
||||
this.type = 'tcp'
|
||||
if (proxyStats.conf != null) {
|
||||
this.addr = ':' + proxyStats.conf.remote_port
|
||||
this.port = proxyStats.conf.remote_port
|
||||
} else {
|
||||
this.addr = ''
|
||||
this.port = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UDPProxy extends BaseProxy {
|
||||
constructor(proxyStats: any) {
|
||||
super(proxyStats)
|
||||
this.type = 'udp'
|
||||
if (proxyStats.conf != null) {
|
||||
this.addr = ':' + proxyStats.conf.remote_port
|
||||
this.port = proxyStats.conf.remote_port
|
||||
} else {
|
||||
this.addr = ''
|
||||
this.port = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HTTPProxy extends BaseProxy {
|
||||
constructor(proxyStats: any, port: number, subdomain_host: string) {
|
||||
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.subdomain != '') {
|
||||
this.subdomain = proxyStats.conf.subdomain + '.' + subdomain_host
|
||||
} else {
|
||||
this.subdomain = ''
|
||||
}
|
||||
} else {
|
||||
this.custom_domains = ''
|
||||
this.host_header_rewrite = ''
|
||||
this.subdomain = ''
|
||||
this.locations = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HTTPSProxy extends BaseProxy {
|
||||
constructor(proxyStats: any, port: number, subdomain_host: string) {
|
||||
super(proxyStats)
|
||||
this.type = 'https'
|
||||
this.port = port
|
||||
if (proxyStats.conf != null) {
|
||||
this.custom_domains = proxyStats.conf.custom_domains
|
||||
if (proxyStats.conf.subdomain != '') {
|
||||
this.subdomain = proxyStats.conf.subdomain + '.' + subdomain_host
|
||||
} else {
|
||||
this.subdomain = ''
|
||||
}
|
||||
} else {
|
||||
this.custom_domains = ''
|
||||
this.subdomain = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class STCPProxy extends BaseProxy {
|
||||
constructor(proxyStats: any) {
|
||||
super(proxyStats)
|
||||
this.type = 'stcp'
|
||||
}
|
||||
}
|
||||
|
||||
class SUDPProxy extends BaseProxy {
|
||||
constructor(proxyStats: any) {
|
||||
super(proxyStats)
|
||||
this.type = 'sudp'
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
BaseProxy,
|
||||
TCPProxy,
|
||||
UDPProxy,
|
||||
HTTPProxy,
|
||||
HTTPSProxy,
|
||||
STCPProxy,
|
||||
SUDPProxy,
|
||||
}
|
8
web/frps/tsconfig.config.json
Normal file
8
web/frps/tsconfig.config.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.node.json",
|
||||
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
16
web/frps/tsconfig.json
Normal file
16
web/frps/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.config.json"
|
||||
}
|
||||
]
|
||||
}
|
29
web/frps/vite.config.ts
Normal file
29
web/frps/vite.config.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: '',
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
assetsDir: '',
|
||||
},
|
||||
})
|
@ -1,107 +0,0 @@
|
||||
const path = require('path')
|
||||
var webpack = require('webpack')
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
var VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||
var url = require('url')
|
||||
var publicPath = ''
|
||||
|
||||
module.exports = (options = {}) => ({
|
||||
entry: {
|
||||
vendor: './src/main'
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: options.dev ? '[name].js' : '[name].js?[chunkhash]',
|
||||
chunkFilename: '[id].js?[chunkhash]',
|
||||
publicPath: options.dev ? '/assets/' : publicPath
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue', '.json'],
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader'
|
||||
}, {
|
||||
test: /\.js$/,
|
||||
use: ['babel-loader'],
|
||||
exclude: /node_modules/
|
||||
}, {
|
||||
test: /\.html$/,
|
||||
use: [{
|
||||
loader: 'html-loader',
|
||||
options: {
|
||||
root: path.resolve(__dirname, 'src'),
|
||||
attrs: ['img:src', 'link:href']
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
test: /\.less$/,
|
||||
loader: 'style-loader!css-loader!postcss-loader!less-loader'
|
||||
}, {
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader', 'postcss-loader']
|
||||
}, {
|
||||
test: /favicon\.png$/,
|
||||
use: [{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: '[name].[ext]?[hash]'
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
|
||||
exclude: /favicon\.png$/,
|
||||
use: [{
|
||||
loader: 'url-loader',
|
||||
options: {
|
||||
limit: 10000
|
||||
}
|
||||
}]
|
||||
}]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
names: ['vendor', 'manifest']
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
favicon: 'src/assets/favicon.ico',
|
||||
template: 'src/index.html'
|
||||
}),
|
||||
new webpack.NormalModuleReplacementPlugin(/element-ui[\/\\]lib[\/\\]locale[\/\\]lang[\/\\]zh-CN/, 'element-ui/lib/locale/lang/en'),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: '"production"'
|
||||
}
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
sourceMap: false,
|
||||
comments: false,
|
||||
compress: {
|
||||
warnings: false
|
||||
}
|
||||
}),
|
||||
new VueLoaderPlugin()
|
||||
],
|
||||
devServer: {
|
||||
host: '127.0.0.1',
|
||||
port: 8010,
|
||||
proxy: {
|
||||
'/api/': {
|
||||
target: 'http://127.0.0.1:8080',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api': ''
|
||||
}
|
||||
}
|
||||
},
|
||||
historyApiFallback: {
|
||||
index: url.parse(options.dev ? '/assets/' : publicPath).pathname
|
||||
}
|
||||
}//,
|
||||
//devtool: options.dev ? '#eval-source-map' : '#source-map'
|
||||
})
|
8182
web/frps/yarn.lock
8182
web/frps/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user