add admin UI for frpc

This commit is contained in:
fatedier
2019-02-01 19:26:10 +08:00
parent d879b8208b
commit 96d7e2da6f
39 changed files with 16189 additions and 30 deletions

73
web/frpc/src/App.vue Normal file
View File

@@ -0,0 +1,73 @@
<template>
<div id="app">
<header class="grid-content header-color">
<el-row>
<a class="brand" href="#">frp client</a>
</el-row>
</header>
<section>
<el-row :gutter="20">
<el-col id="side-nav" :xs="24" :md="4">
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect">
<el-menu-item index="/">Overview</el-menu-item>
<el-menu-item index="/configure">Configure</el-menu-item>
<el-menu-item index="">Help</el-menu-item>
</el-menu>
</el-col>
<el-col :xs="24" :md="20">
<div id="content">
<router-view></router-view>
</div>
</el-col>
</el-row>
</section>
<footer></footer>
</div>
</template>
<script>
export default {
methods: {
handleSelect(key, path) {
if (key == '') {
window.open("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;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,106 @@
<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 type="textarea" autosize v-model="textarea" placeholder="frpc configrue file, can not be empty..."></el-input>
</div>
</template>
<script>
export default {
data() {
return {
textarea: ''
}
},
created() {
this.fetchData()
},
watch: {
'$route': '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'
})
})
},
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(res => {
return res.json()
}).then(json => {
console.log(json)
if (json.code != 0) {
this.$message({
showClose: true,
message: 'Put config to frpc and hot reload failed!',
type: 'warning'
})
} else {
fetch('/api/reload', {credentials: 'include'})
.then(res => {
return res.json()
}).then(json => {
this.$message({
type: 'success',
message: 'Success'
})
}).catch(err => {
this.$message({
showClose: true,
message: 'Reload frpc configure file error!',
type: 'warning'
})
})
}
}).catch(err => {
this.$message({
showClose: true,
message: 'Put config to frpc and hot reload failed!',
type: 'warning'
})
})
}).catch(() => {
this.$message({
type: 'info',
message: 'Canceled'
})
})
}
}
}
</script>
<style>
#head {
margin-bottom: 30px;
}
</style>

View File

@@ -0,0 +1,72 @@
<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>
<el-table-column prop="type" label="type" width="150"></el-table-column>
<el-table-column prop="local_addr" label="local address" width="200"></el-table-column>
<el-table-column prop="plugin" label="plugin" width="200"></el-table-column>
<el-table-column prop="remote_addr" label="remote address"></el-table-column>
<el-table-column prop="status" label="status" width="150"></el-table-column>
<el-table-column prop="err" label="info"></el-table-column>
</el-table>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
status: null
}
},
created() {
this.fetchData()
},
watch: {
'$route': 'fetchData'
},
methods: {
fetchData() {
fetch('/api/status', {credentials: 'include'})
.then(res => {
return res.json()
}).then(json => {
this.status = new Array()
for (let s of json.tcp) {
this.status.push(s)
}
for (let s of json.udp) {
this.status.push(s)
}
for (let s of json.http) {
this.status.push(s)
}
for (let s of json.https) {
this.status.push(s)
}
for (let s of json.stcp) {
this.status.push(s)
}
for (let s of json.xtcp) {
this.status.push(s)
}
}).catch( err => {
this.$message({
showClose: true,
message: 'Get status info from frpc failed!',
type: 'warning'
})
})
}
}
}
</script>
<style>
</style>

15
web/frpc/src/index.html Normal file
View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>frp client admin UI</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>

52
web/frpc/src/main.js Normal file
View File

@@ -0,0 +1,52 @@
import Vue from 'vue'
// import ElementUI from 'element-ui'
import {
Button,
Form,
FormItem,
Row,
Col,
Table,
TableColumn,
Menu,
MenuItem,
MessageBox,
Message,
Input
} 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(Menu)
Vue.use(MenuItem)
Vue.use(Input)
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$confirm = MessageBox.confirm
Vue.prototype.$message = Message
//Vue.use(ElementUI)
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})

View File

@@ -0,0 +1,18 @@
import Vue from 'vue'
import Router from 'vue-router'
import Overview from '../components/Overview.vue'
import Configure from '../components/Configure.vue'
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
name: 'Overview',
component: Overview
},{
path: '/configure',
name: 'Configure',
component: Configure,
}]
})

View File

@@ -0,0 +1,22 @@
@color: red;
.el-form-item {
span {
margin-left: 15px;
}
}
.demo-table-expand {
font-size: 0;
label {
width: 90px;
color: #99a9bf;
}
.el-form-item {
margin-right: 0;
margin-bottom: 0;
width: 50%;
}
}

View File

@@ -0,0 +1,13 @@
class ProxyStatus {
constructor(status) {
this.name = status.name
this.type = status.type
this.status = status.status
this.err = status.err
this.local_addr = status.local_addr
this.plugin = status.plugin
this.remote_addr = status.remote_addr
}
}
export {ProxyStatus}