merge web

This commit is contained in:
hubery.wang 2020-12-04 09:33:55 +08:00
parent 5c48241bcb
commit 0e56137f07
25 changed files with 315 additions and 41 deletions

2
web/frps/.env.dev-frpc Normal file
View File

@ -0,0 +1,2 @@
ENV = 'development'
VUE_APP_TYPE = 'frpc'

2
web/frps/.env.dev-frps Normal file
View File

@ -0,0 +1,2 @@
ENV = 'development'
VUE_APP_TYPE = 'frps'

View File

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

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

@ -0,0 +1,2 @@
ENV = 'production'
VUE_APP_TYPE = 'frpc'

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

@ -0,0 +1,2 @@
ENV = 'production'
VUE_APP_TYPE = 'frps'

View File

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

View File

@ -262,6 +262,18 @@ module.exports = {
objectsInObjects: false
}
],
'array-bracket-spacing': [2, 'never']
'array-bracket-spacing': [2, 'never'],
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always'
},
svg: 'always',
math: 'always'
}
]
}
}

View File

@ -4,8 +4,10 @@
"author": "fatedier",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"serve:c": "vue-cli-service serve --mode dev-frpc",
"serve:s": "vue-cli-service serve --mode dev-frps",
"build:c": "vue-cli-service build --mode frpc",
"build:s": "vue-cli-service build --mode frps",
"lint": "vue-cli-service lint"
},
"dependencies": {

View File

@ -2,7 +2,7 @@
<template>
<!-- eslint-disable vue/require-component-is -->
<component v-bind="linkProps(to)">
<slot />
<slot></slot>
</component>
</template>

View File

@ -2,11 +2,11 @@
<div class="sidebar-logo-container" :class="{ collapse: collapse }">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img src="/favicon.ico" alt="" class="sidebar-logo">
<img src="/favicon.ico" alt="" class="sidebar-logo" />
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img src="/favicon.ico" alt="" class="sidebar-logo">
<h1 class="sidebar-title">frps</h1>
<img src="/favicon.ico" alt="" class="sidebar-logo" />
<h1 class="sidebar-title">{{ title }}</h1>
</router-link>
</transition>
</div>
@ -21,6 +21,11 @@ export default {
required: true
}
},
data() {
return {
title: process.env.VUE_APP_TYPE
}
},
computed: {
baseUrl() {
return process.env.BASE_URL

View File

@ -1,6 +1,6 @@
<template>
<div :class="classObj" class="app-wrapper">
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"></div>
<sidebar class="sidebar-container" />
<div class="main-container">
<div>

View File

@ -1,5 +1,5 @@
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners"></div>
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>

View File

@ -1,5 +1,5 @@
<template>
<div :id="proxyName" style="width: 600px; height: 400px" />
<div :id="proxyName" style="width: 600px; height: 400px"></div>
</template>
<script>

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1607016779867" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4273" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><defs><style type="text/css"></style></defs><path d="M993.760151 704.386146l-41.03394 12.784554c1.179682 8.863611 2.455338 17.504282 2.455338 26.654816 0 9.05456-1.434613 17.854188-2.805244 26.52585l41.383846 12.880529c9.692388 2.806244 17.854188 9.373474 22.572915 18.237084 4.686737 8.863611 5.642479 19.257809 2.422348 28.822232-6.663204 19.703689-28.5663 30.351819-49.066775 24.03952l-41.862717-12.976502a207.334117 207.334117 0 0 1-32.169329 43.265338l25.538116 33.731909a36.378195 36.378195 0 0 1-8.60768 52.159941 39.88525 39.88525 0 0 1-54.42533-8.225783l-25.665083-33.890865a217.951255 217.951255 0 0 1-52.733786 17.089393v41.128915c0 20.595449-17.472291 37.39792-38.929508 37.39792-21.552191 0-38.99249-16.706497-38.99249-37.39892v-41.128914a217.759307 217.759307 0 0 1-52.765778-17.088394l-25.602099 33.890865a40.172172 40.172172 0 0 1-54.455323 8.225783 36.155255 36.155255 0 0 1-8.576688-52.159941l25.793048-34.050823a201.084801 201.084801 0 0 1-33.285029-42.691493l-40.905975 12.785554c-20.596449 6.440264-42.499545-4.335831-49.099766-24.039521a36.920049 36.920049 0 0 1 24.964272-47.059316l41.064931-12.720571c-1.147691-8.83162-2.519321-17.600256-2.519321-26.686807 0-9.181525 1.499596-17.854188 2.839235-26.652816l-41.416837-12.817546c-20.564457-6.440264-31.659467-27.482593-24.963271-47.123298 6.598222-19.607715 28.502318-30.287836 49.098766-24.00753l41.829726 13.040485a211.000129 211.000129 0 0 1 32.170329-43.360313l-25.506126-33.636933a36.442178 36.442178 0 0 1 8.576689-52.191933 39.949232 39.949232 0 0 1 54.455322 8.289766l25.633091 33.890865a214.412209 214.412209 0 0 1 52.734787-17.120386v-41.128914c0-20.62844 17.408308-37.39892 38.99249-37.39892 21.489208 0 38.960499 16.642514 38.960499 37.39892v41.064932a216.675599 216.675599 0 0 1 52.702795 17.152377l25.665082-33.922857a40.109189 40.109189 0 0 1 54.424331-8.289766c17.344325 12.179717 21.265268 35.485436 8.640671 52.223924l-25.825039 34.018831c12.720571 12.91252 24.358435 26.97273 33.349011 42.595519l40.873984-12.656588c20.532466-6.313298 42.403571 4.398814 49.066775 24.070512 6.759178 19.608715-4.52678 40.651043-24.995263 47.059316zM740.928297 613.13774c-75.306702 0-136.426229 58.600205-136.426229 130.78275 0 72.246527 61.119526 130.719767 136.426229 130.719767 75.211728 0 136.363246-58.47324 136.363246-130.719767 0-72.182545-61.056543-130.78275-136.363246-130.78275z" p-id="4274"></path><path d="M441.708946 895.428654H99.640144a49.736594 49.736594 0 0 1-49.768586-49.769586V99.570163c0-27.451601 22.285993-49.769586 49.768586-49.769586h447.698331v223.849666a99.50618 99.50618 0 0 0 99.50618 99.50618h198.948377v36.441178a322.016207 322.016207 0 0 1 49.768586 32.011372V348.286126l-8.799628-8.767637 1.529588-8.799628L581.324315 0.095974H99.671136A99.50618 99.50618 0 0 0 0.164956 99.602154V845.722051a99.50618 99.50618 0 0 0 99.50618 99.50618h390.34079a322.876975 322.876975 0 0 1-48.301981-49.800577z" p-id="4275"></path><path d="M597.107061 91.12144l217.758307 232.265397H646.843655a49.736594 49.736594 0 0 1-49.769585-49.736594V91.12144h0.031991zM174.276027 422.893017H422.99399v-49.736594H174.277027v49.736594zM422.99399 223.880657H174.277027v49.769586H422.99399V223.879657zM174.277027 743.857507H422.99399v-49.737594H174.277027v49.736594z m339.838403-198.980368h-339.838403v49.736594h339.838403v-49.736594z" p-id="4276"></path></svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -4,18 +4,31 @@ import AdminLayout from '@/components/AdminLayout'
Vue.use(Router)
export const routes = [
const allRoutes = [
{
path: '/',
component: AdminLayout,
meta: {
icon: 'dashboard'
hidden: true
},
children: [
{
path: '/',
component: () => import('@/views/Overview'),
name: 'Overview',
component: () => import('@/views/index')
}
]
},
{
path: '/frps',
component: AdminLayout,
meta: {
icon: 'dashboard',
type: 'frps'
},
children: [
{
path: '',
component: () => import('@/views/frps/Overview'),
meta: {
title: 'Overview'
}
@ -23,55 +36,85 @@ export const routes = [
]
},
{
path: '/proxies',
path: '/frps/proxies',
component: AdminLayout,
meta: {
title: 'Proxies',
icon: 'proxy'
icon: 'proxy',
type: 'frps'
},
children: [
{
path: 'tcp',
component: () => import('@/views/ProxiesTcp'),
name: 'ProxiesTcp',
component: () => import('@/views/frps/ProxiesTcp'),
meta: {
title: 'TCP'
}
},
{
path: 'udp',
component: () => import('@/views/ProxiesUdp'),
name: 'ProxiesUdp',
component: () => import('@/views/frps/ProxiesUdp'),
meta: {
title: 'UDP'
}
},
{
path: 'http',
component: () => import('@/views/ProxiesHttp'),
name: 'ProxiesHttp',
component: () => import('@/views/frps/ProxiesHttp'),
meta: {
title: 'HTTP'
}
},
{
path: 'https',
component: () => import('@/views/ProxiesHttps'),
name: 'ProxiesHttps',
component: () => import('@/views/frps/ProxiesHttps'),
meta: {
title: 'HTTPS'
}
},
{
path: 'stcp',
component: () => import('@/views/ProxiesStcp'),
name: 'ProxiesStcp',
component: () => import('@/views/frps/ProxiesStcp'),
meta: {
title: 'STCP'
}
}
]
},
{
path: '/frpc',
component: AdminLayout,
meta: {
icon: 'dashboard',
type: 'frpc'
},
children: [
{
path: '',
component: () => import('@/views/frpc/Overview'),
meta: {
title: 'Overview'
}
}
]
},
{
path: '/frpc/config',
component: AdminLayout,
meta: {
icon: 'config',
type: 'frpc'
},
children: [
{
path: '',
component: () => import('@/views/frpc/Configure'),
meta: {
title: 'Configure'
}
}
]
},
{
path: '/help',
component: AdminLayout,
@ -87,4 +130,19 @@ export const routes = [
}
]
export default new Router({ routes })
// filter routes recursively
const filterRoutes = function(routes) {
const newRoutes = routes.filter(route => !route.meta || !route.meta.type || route.meta.type === process.env.VUE_APP_TYPE)
for (const route in newRoutes) {
if (route.children) {
route.children = filterRoutes(route.children)
}
}
return newRoutes
}
export const routes = filterRoutes(allRoutes)
export default new Router({
routes
})

View File

@ -0,0 +1,102 @@
<template>
<div>
<el-row id="head">
<el-button type="primary" @click="fetchData">Refresh</el-button>
<el-button type="primary" @click="uploadConfig">Upload</el-button>
</el-row>
<el-input v-model="textarea" type="textarea" autosize placeholder="frpc configrue file, can not be empty..." />
</div>
</template>
<script>
export default {
data() {
return {
textarea: ''
}
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
fetchData() {
fetch('/api/config', { credentials: 'include' })
.then(res => {
return res.text()
})
.then(text => {
this.textarea = text
})
.catch(err => {
this.$message({
showClose: true,
message: 'Get configure content from frpc failed!',
type: 'warning'
})
return err
})
},
uploadConfig() {
this.$confirm('This operation will upload your frpc configure file content and hot reload it, do you want to continue?', 'Notice', {
confirmButtonText: 'Yes',
cancelButtonText: 'No',
type: 'warning'
})
.then(() => {
if (!this.textarea) {
this.$message({
type: 'warning',
message: 'Configure content can not be empty!'
})
return
}
fetch('/api/config', {
credentials: 'include',
method: 'PUT',
body: this.textarea
})
.then(() => {
fetch('/api/reload', { credentials: 'include' })
.then(() => {
this.$message({
type: 'success',
message: 'Success'
})
})
.catch(err => {
this.$message({
showClose: true,
message: 'Reload frpc configure file error, ' + err,
type: 'warning'
})
})
})
.catch(err => {
this.$message({
showClose: true,
message: 'Put config to frpc and hot reload failed!',
type: 'warning'
})
return err
})
})
.catch(() => {
this.$message({
type: 'info',
message: 'Canceled'
})
})
}
}
}
</script>
<style>
#head {
margin-bottom: 30px;
}
</style>

View File

@ -0,0 +1,75 @@
<template>
<div>
<el-row>
<el-col :md="24">
<div>
<el-table :data="status" stripe style="width: 100%" :default-sort="{ prop: 'type', order: 'ascending' }">
<el-table-column prop="name" label="name" />
<el-table-column prop="type" label="type" width="150" />
<el-table-column prop="local_addr" label="local address" width="200" />
<el-table-column prop="plugin" label="plugin" width="200" />
<el-table-column prop="remote_addr" label="remote address" />
<el-table-column prop="status" label="status" width="150" />
<el-table-column prop="err" label="info" />
</el-table>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
status: null
}
},
watch: {
$route: 'fetchData'
},
created() {
this.fetchData()
},
methods: {
fetchData() {
fetch('/api/status', { credentials: 'include' })
.then(res => {
return res.json()
})
.then(json => {
this.status = []
for (const s of json.tcp) {
this.status.push(s)
}
for (const s of json.udp) {
this.status.push(s)
}
for (const s of json.http) {
this.status.push(s)
}
for (const s of json.https) {
this.status.push(s)
}
for (const s of json.stcp) {
this.status.push(s)
}
for (const s of json.xtcp) {
this.status.push(s)
}
})
.catch(err => {
this.$message({
showClose: true,
message: 'Get status info from frpc failed!',
type: 'warning'
})
return err
})
}
}
}
</script>
<style>
</style>

View File

@ -44,15 +44,15 @@
</div>
</el-col>
<el-col :md="12">
<div id="traffic" style="width: 400px; height: 250px; margin-bottom: 30px" />
<div id="proxies" style="width: 400px; height: 250px" />
<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'
import { DrawTrafficChart, DrawProxyChart } from '@/utils/chart.js'
export default {
data() {
return {

View File

@ -61,7 +61,7 @@
<script>
import Humanize from 'humanize-plus'
import Traffic from '@/components/Traffic.vue'
import { HttpProxy } from '../utils/proxy.js'
import { HttpProxy } from '@/utils/proxy.js'
export default {
components: {
'my-traffic-chart': Traffic

View File

@ -55,7 +55,7 @@
<script>
import Humanize from 'humanize-plus'
import Traffic from '@/components/Traffic.vue'
import { HttpsProxy } from '../utils/proxy.js'
import { HttpsProxy } from '@/utils/proxy.js'
export default {
components: {
'my-traffic-chart': Traffic

View File

@ -50,7 +50,7 @@
<script>
import Humanize from 'humanize-plus'
import Traffic from '@/components/Traffic.vue'
import { StcpProxy } from '../utils/proxy.js'
import { StcpProxy } from '@/utils/proxy.js'
export default {
components: {
'my-traffic-chart': Traffic

View File

@ -54,7 +54,7 @@
<script>
import Humanize from 'humanize-plus'
import Traffic from '@/components/Traffic.vue'
import { TcpProxy } from '../utils/proxy.js'
import { TcpProxy } from '@/utils/proxy.js'
export default {
components: {
'my-traffic-chart': Traffic

View File

@ -52,7 +52,7 @@
<script>
import Humanize from 'humanize-plus'
import Traffic from '@/components/Traffic.vue'
import { UdpProxy } from '../utils/proxy.js'
import { UdpProxy } from '@/utils/proxy.js'
export default {
components: {
'my-traffic-chart': Traffic

View File

@ -1,7 +1,11 @@
<template>
<div></div>
</template>
<script>
export default {
created() {
this.$router.push(`/${process.env.VUE_APP_TYPE}`)
}
}
</script>

View File

@ -4,6 +4,17 @@ function resolve(dir) {
return path.join(__dirname, dir)
}
function getProxyTargetPort() {
switch (process.env.VUE_APP_TYPE) {
case 'frps':
return 8081
case 'frpc':
return 8082
default:
return 8080
}
}
module.exports = {
publicPath: './',
devServer: {
@ -11,7 +22,7 @@ module.exports = {
port: 8010,
proxy: {
'/api/': {
target: 'http://127.0.0.1:8080/api',
target: `http://127.0.0.1:${getProxyTargetPort()}/api`,
changeOrigin: true,
pathRewrite: {
'^/api': ''