mirror of
https://github.com/fatedier/frp.git
synced 2024-11-27 20:49:10 +08:00
web/frpc: upgrade vue and element-plus (#3322)
This commit is contained in:
parent
24f0b3afa5
commit
fe8374e99b
Binary file not shown.
Binary file not shown.
1
assets/frpc/static/index-26827c97.css
Normal file
1
assets/frpc/static/index-26827c97.css
Normal file
File diff suppressed because one or more lines are too long
32
assets/frpc/static/index-fec891f3.js
Normal file
32
assets/frpc/static/index-fec891f3.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>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?5d5774096cf5c1b4d5af"></script><script type="text/javascript" src="vendor.js?dc42700731a508d39009"></script></body> </html>
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>frp client admin UI</title>
|
||||||
|
<script type="module" crossorigin src="./index-fec891f3.js"></script>
|
||||||
|
<link rel="stylesheet" href="./index-26827c97.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,c,u){for(var i,a,f,l=0,s=[];l<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(r&&r(t,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=n(n.s=u[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var u=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"dc42700731a508d39009"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,u.appendChild(i),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);
|
|
File diff suppressed because one or more lines are too long
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
["es2015", { "modules": false }]
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"component",
|
|
||||||
{
|
|
||||||
"libraryName": "element-ui",
|
|
||||||
"styleLibraryName": "theme-chalk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
30
web/frpc/.eslintrc.cjs
Normal file
30
web/frpc/.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: ["Overview"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
30
web/frpc/.gitignore
vendored
30
web/frpc/.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
|
.DS_Store
|
||||||
node_modules/
|
dist
|
||||||
dist/
|
dist-ssr
|
||||||
npm-debug.log
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
.idea
|
.idea
|
||||||
.vscode/settings.json
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
1
web/frpc/.prettierrc.json
Normal file
1
web/frpc/.prettierrc.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -1,6 +1,13 @@
|
|||||||
.PHONY: dist build
|
.PHONY: dist build preview lint
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@npm run build
|
@npm run build
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
@npm run dev
|
@npm run dev
|
||||||
|
|
||||||
|
preview:
|
||||||
|
@npm run preview
|
||||||
|
|
||||||
|
lint:
|
||||||
|
@npm run lint
|
||||||
|
25
web/frpc/README.md
Normal file
25
web/frpc/README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# frpc-dashboard
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type-Check, Compile and Minify for Production
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lint with [ESLint](https://eslint.org/)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make lint
|
||||||
|
```
|
8
web/frpc/auto-imports.d.ts
vendored
Normal file
8
web/frpc/auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
|
||||||
|
}
|
25
web/frpc/components.d.ts
vendored
Normal file
25
web/frpc/components.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
import '@vue/runtime-core'
|
||||||
|
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
ClientConfigure: typeof import('./src/components/ClientConfigure.vue')['default']
|
||||||
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
|
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||||
|
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||||
|
ElRow: typeof import('element-plus/es')['ElRow']
|
||||||
|
ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
|
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||||
|
Overview: typeof import('./src/components/Overview.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
}
|
||||||
|
}
|
1
web/frpc/env.d.ts
vendored
Normal file
1
web/frpc/env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
@ -8,8 +8,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<!--<script src="https://code.jquery.com/jquery-3.2.0.min.js"></script>-->
|
<script type="module" src="/src/main.ts"></script>
|
||||||
<!--<script src="//cdn.bootcss.com/echarts/3.4.0/echarts.min.js"></script>-->
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -1,46 +1,35 @@
|
|||||||
{
|
{
|
||||||
"name": "frpc-web",
|
"name": "-frpc-dashboard",
|
||||||
"description": "An admin web ui for frp client.",
|
"version": "0.0.0",
|
||||||
"author": "fatedier",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "webpack-dev-server -d --inline --hot --env.dev",
|
"dev": "vite",
|
||||||
"build": "rimraf dist && webpack -p --progress --hide-modules"
|
"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": {
|
"dependencies": {
|
||||||
"element-ui": "^2.5.3",
|
"element-plus": "^2.2.28",
|
||||||
"vue": "^2.5.22",
|
"vue": "^3.2.45",
|
||||||
"vue-resource": "^1.5.1",
|
"vue-router": "^4.1.6"
|
||||||
"vue-router": "^3.0.2",
|
|
||||||
"whatwg-fetch": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^9.4.7",
|
"@rushstack/eslint-patch": "^1.1.4",
|
||||||
"babel-core": "^6.26.3",
|
"@types/node": "^18.11.12",
|
||||||
"babel-eslint": "^10.0.1",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"babel-loader": "^7.1.5",
|
"@vue/eslint-config-prettier": "^7.0.0",
|
||||||
"babel-plugin-component": "^1.1.1",
|
"@vue/eslint-config-typescript": "^11.0.0",
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"@vue/tsconfig": "^0.1.3",
|
||||||
"css-loader": "^2.1.0",
|
"eslint": "^8.22.0",
|
||||||
"eslint": "^5.12.1",
|
"eslint-plugin-vue": "^9.3.0",
|
||||||
"eslint-config-enough": "^0.3.4",
|
"npm-run-all": "^4.1.5",
|
||||||
"eslint-loader": "^2.1.1",
|
"prettier": "^2.7.1",
|
||||||
"file-loader": "^3.0.1",
|
"typescript": "~4.7.4",
|
||||||
"html-loader": "^0.5.5",
|
"unplugin-auto-import": "^0.14.3",
|
||||||
"html-webpack-plugin": "^2.24.1",
|
"unplugin-vue-components": "^0.24.0",
|
||||||
"less": "^3.9.0",
|
"vite": "^4.0.0",
|
||||||
"less-loader": "^4.1.0",
|
"vue-tsc": "^1.0.12"
|
||||||
"postcss-loader": "^3.0.0",
|
|
||||||
"rimraf": "^2.6.3",
|
|
||||||
"style-loader": "^0.23.1",
|
|
||||||
"url-loader": "^1.1.2",
|
|
||||||
"vue-loader": "^15.6.2",
|
|
||||||
"vue-template-compiler": "^2.5.22",
|
|
||||||
"webpack": "^2.7.0",
|
|
||||||
"webpack-cli": "^3.2.1",
|
|
||||||
"webpack-dev-server": "^3.1.14"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: [
|
|
||||||
require('autoprefixer')()
|
|
||||||
]
|
|
||||||
}
|
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
@ -1,73 +1,75 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<header class="grid-content header-color">
|
<header class="grid-content header-color">
|
||||||
<el-row>
|
<el-row>
|
||||||
<a class="brand" href="#">frp client</a>
|
<a class="brand" href="#">frp client</a>
|
||||||
</el-row>
|
</el-row>
|
||||||
</header>
|
</header>
|
||||||
<section>
|
<section>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col id="side-nav" :xs="24" :md="4">
|
<el-col id="side-nav" :xs="24" :md="4">
|
||||||
<el-menu default-active="1" mode="vertical" theme="light" router="false" @select="handleSelect">
|
<el-menu
|
||||||
<el-menu-item index="/">Overview</el-menu-item>
|
default-active="1"
|
||||||
<el-menu-item index="/configure">Configure</el-menu-item>
|
mode="vertical"
|
||||||
<el-menu-item index="">Help</el-menu-item>
|
theme="light"
|
||||||
</el-menu>
|
router="false"
|
||||||
</el-col>
|
@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">
|
<el-col :xs="24" :md="20">
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</section>
|
</section>
|
||||||
<footer></footer>
|
<footer></footer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
export default {
|
const handleSelect = (key: string) => {
|
||||||
methods: {
|
if (key == "") {
|
||||||
handleSelect(key, path) {
|
window.open("https://github.com/fatedier/frp");
|
||||||
if (key == '') {
|
}
|
||||||
window.open("https://github.com/fatedier/frp")
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background-color: #fafafa;
|
background-color: #fafafa;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
font-family: -apple-system,BlinkMacSystemFont,Helvetica Neue,sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-color {
|
.header-color {
|
||||||
background: #58B7FF;
|
background: #58b7ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content {
|
#content {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
padding-right: 40px;
|
padding-right: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brand {
|
.brand {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
float: left;
|
float: left;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
padding: 15px 15px;
|
padding: 15px 15px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
102
web/frpc/src/components/ClientConfigure.vue
Normal file
102
web/frpc/src/components/ClientConfigure.vue
Normal 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
|
||||||
|
type="textarea"
|
||||||
|
autosize
|
||||||
|
v-model="textarea"
|
||||||
|
placeholder="frpc configrue file, can not be empty..."
|
||||||
|
></el-input>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
|
|
||||||
|
let textarea = ref("");
|
||||||
|
|
||||||
|
const fetchData = () => {
|
||||||
|
fetch("/api/config", { credentials: "include" })
|
||||||
|
.then((res) => {
|
||||||
|
return res.text();
|
||||||
|
})
|
||||||
|
.then((text) => {
|
||||||
|
textarea.value = text;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
showClose: true,
|
||||||
|
message: "Get configure content from frpc failed!",
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadConfig = () => {
|
||||||
|
ElMessageBox.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 (textarea.value == "") {
|
||||||
|
ElMessage({
|
||||||
|
message: "Configure content can not be empty!",
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch("/api/config", {
|
||||||
|
credentials: "include",
|
||||||
|
method: "PUT",
|
||||||
|
body: textarea.value,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
fetch("/api/reload", { credentials: "include" })
|
||||||
|
.then(() => {
|
||||||
|
ElMessage({
|
||||||
|
type: "success",
|
||||||
|
message: "Success",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
ElMessage({
|
||||||
|
showClose: true,
|
||||||
|
message: "Reload frpc configure file error, " + err,
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
showClose: true,
|
||||||
|
message: "Put config to frpc and hot reload failed!",
|
||||||
|
type: "warning",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ElMessage({
|
||||||
|
message: "Canceled",
|
||||||
|
type: "info",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchData();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#head {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,93 +0,0 @@
|
|||||||
<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(() => {
|
|
||||||
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'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}).catch(() => {
|
|
||||||
this.$message({
|
|
||||||
type: 'info',
|
|
||||||
message: 'Canceled'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
#head {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,75 +1,91 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :md="24">
|
<el-col :md="24">
|
||||||
<div>
|
<div>
|
||||||
<el-table :data="status" stripe style="width: 100%" :default-sort="{prop: 'type', order: 'ascending'}">
|
<el-table
|
||||||
<el-table-column prop="name" label="name"></el-table-column>
|
:data="status"
|
||||||
<el-table-column prop="type" label="type" width="150"></el-table-column>
|
stripe
|
||||||
<el-table-column prop="local_addr" label="local address" width="200"></el-table-column>
|
style="width: 100%"
|
||||||
<el-table-column prop="plugin" label="plugin" width="200"></el-table-column>
|
:default-sort="{ prop: 'type', order: 'ascending' }"
|
||||||
<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="name" label="name"></el-table-column>
|
||||||
<el-table-column prop="err" label="info"></el-table-column>
|
<el-table-column
|
||||||
</el-table>
|
prop="type"
|
||||||
</div>
|
label="type"
|
||||||
</el-col>
|
width="150"
|
||||||
</el-row>
|
></el-table-column>
|
||||||
</div>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
export default {
|
import { ref } from "vue";
|
||||||
data() {
|
import { ElMessage } from "element-plus";
|
||||||
return {
|
|
||||||
status: new Array(),
|
let status = ref<any[]>([]);
|
||||||
}
|
|
||||||
},
|
const fetchData = () => {
|
||||||
created() {
|
fetch("/api/status", { credentials: "include" })
|
||||||
this.fetchData()
|
.then((res) => {
|
||||||
},
|
return res.json();
|
||||||
watch: {
|
})
|
||||||
'$route': 'fetchData'
|
.then((json) => {
|
||||||
},
|
status.value = new Array();
|
||||||
methods: {
|
for (let s of json.tcp) {
|
||||||
fetchData() {
|
status.value.push(s);
|
||||||
fetch('/api/status', {credentials: 'include'})
|
}
|
||||||
.then(res => {
|
for (let s of json.udp) {
|
||||||
return res.json()
|
status.value.push(s);
|
||||||
}).then(json => {
|
}
|
||||||
this.status = new Array()
|
for (let s of json.http) {
|
||||||
for (let s of json.tcp) {
|
status.value.push(s);
|
||||||
this.status.push(s)
|
}
|
||||||
}
|
for (let s of json.https) {
|
||||||
for (let s of json.udp) {
|
status.value.push(s);
|
||||||
this.status.push(s)
|
}
|
||||||
}
|
for (let s of json.stcp) {
|
||||||
for (let s of json.http) {
|
status.value.push(s);
|
||||||
this.status.push(s)
|
}
|
||||||
}
|
for (let s of json.sudp) {
|
||||||
for (let s of json.https) {
|
status.value.push(s);
|
||||||
this.status.push(s)
|
}
|
||||||
}
|
for (let s of json.xtcp) {
|
||||||
for (let s of json.stcp) {
|
status.value.push(s);
|
||||||
this.status.push(s)
|
}
|
||||||
}
|
})
|
||||||
for (let s of json.sudp) {
|
.catch(() => {
|
||||||
this.status.push(s)
|
ElMessage({
|
||||||
}
|
showClose: true,
|
||||||
for (let s of json.xtcp) {
|
message: "Get status info from frpc failed!",
|
||||||
this.status.push(s)
|
type: "warning",
|
||||||
}
|
});
|
||||||
}).catch( err => {
|
});
|
||||||
this.$message({
|
};
|
||||||
showClose: true,
|
fetchData();
|
||||||
message: 'Get status info from frpc failed!',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style></style>
|
||||||
</style>
|
|
||||||
|
@ -1,52 +0,0 @@
|
|||||||
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 }
|
|
||||||
})
|
|
12
web/frpc/src/main.ts
Normal file
12
web/frpc/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,18 +0,0 @@
|
|||||||
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,
|
|
||||||
}]
|
|
||||||
})
|
|
21
web/frpc/src/router/index.ts
Normal file
21
web/frpc/src/router/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { createRouter, createWebHashHistory } from "vue-router";
|
||||||
|
import Overview from "../components/Overview.vue";
|
||||||
|
import ClientConfigure from "../components/ClientConfigure.vue";
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: "/",
|
||||||
|
name: "Overview",
|
||||||
|
component: Overview,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/configure",
|
||||||
|
name: "ClientConfigure",
|
||||||
|
component: ClientConfigure,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -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,13 +0,0 @@
|
|||||||
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}
|
|
8
web/frpc/tsconfig.config.json
Normal file
8
web/frpc/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/frpc/tsconfig.json
Normal file
16
web/frpc/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/frpc/vite.config.ts
Normal file
29
web/frpc/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'
|
|
||||||
})
|
|
7633
web/frpc/yarn.lock
7633
web/frpc/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -1,46 +1,25 @@
|
|||||||
# frps-dashboard
|
# 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
|
## Project Setup
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Compile and Hot-Reload for Development
|
### Compile and Hot-Reload for Development
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run dev
|
make dev
|
||||||
```
|
```
|
||||||
|
|
||||||
### Type-Check, Compile and Minify for Production
|
### Type-Check, Compile and Minify for Production
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run build
|
make build
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lint with [ESLint](https://eslint.org/)
|
### Lint with [ESLint](https://eslint.org/)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run lint
|
make lint
|
||||||
```
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user