Delete web directory
This commit is contained in:
parent
2855ac71e3
commit
109bcdf763
@ -1,30 +0,0 @@
|
|||||||
/* 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'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
28
web/frpc/.gitignore
vendored
28
web/frpc/.gitignore
vendored
@ -1,28 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
.DS_Store
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
coverage
|
|
||||||
*.local
|
|
||||||
|
|
||||||
/cypress/videos/
|
|
||||||
/cypress/screenshots/
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"tabWidth": 2,
|
|
||||||
"semi": false,
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
.PHONY: dist build preview lint
|
|
||||||
|
|
||||||
build:
|
|
||||||
@npm run build
|
|
||||||
|
|
||||||
dev:
|
|
||||||
@npm run dev
|
|
||||||
|
|
||||||
preview:
|
|
||||||
@npm run preview
|
|
||||||
|
|
||||||
lint:
|
|
||||||
@npm run lint
|
|
@ -1,25 +0,0 @@
|
|||||||
# 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
|
|
||||||
```
|
|
9
web/frpc/auto-imports.d.ts
vendored
9
web/frpc/auto-imports.d.ts
vendored
@ -1,9 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
// Generated by unplugin-auto-import
|
|
||||||
export {}
|
|
||||||
declare global {
|
|
||||||
|
|
||||||
}
|
|
24
web/frpc/components.d.ts
vendored
24
web/frpc/components.d.ts
vendored
@ -1,24 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// Generated by unplugin-vue-components
|
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
|
||||||
export {}
|
|
||||||
|
|
||||||
declare module 'vue' {
|
|
||||||
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']
|
|
||||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
|
||||||
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
1
web/frpc/env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
@ -1,14 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>frp client admin UI</title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "frpc-dashboard",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"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": {
|
|
||||||
"element-plus": "^2.5.3",
|
|
||||||
"vue": "^3.4.15",
|
|
||||||
"vue-router": "^4.2.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@rushstack/eslint-patch": "^1.7.2",
|
|
||||||
"@types/node": "^18.11.12",
|
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
|
||||||
"@vue/eslint-config-prettier": "^9.0.0",
|
|
||||||
"@vue/eslint-config-typescript": "^12.0.0",
|
|
||||||
"@vue/tsconfig": "^0.5.1",
|
|
||||||
"eslint": "^8.56.0",
|
|
||||||
"eslint-plugin-vue": "^9.21.0",
|
|
||||||
"npm-run-all": "^4.1.5",
|
|
||||||
"prettier": "^3.2.4",
|
|
||||||
"typescript": "~5.3.3",
|
|
||||||
"unplugin-auto-import": "^0.17.5",
|
|
||||||
"unplugin-vue-components": "^0.26.0",
|
|
||||||
"vite": "^5.0.12",
|
|
||||||
"vue-tsc": "^1.8.27"
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
@ -1,116 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="app">
|
|
||||||
<header class="grid-content header-color">
|
|
||||||
<div class="header-content">
|
|
||||||
<div class="brand">
|
|
||||||
<a href="#">frp client</a>
|
|
||||||
</div>
|
|
||||||
<div class="dark-switch">
|
|
||||||
<el-switch
|
|
||||||
v-model="darkmodeSwitch"
|
|
||||||
inline-prompt
|
|
||||||
active-text="Dark"
|
|
||||||
inactive-text="Light"
|
|
||||||
@change="toggleDark"
|
|
||||||
style="
|
|
||||||
--el-switch-on-color: #444452;
|
|
||||||
--el-switch-off-color: #589ef8;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</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-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 setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useDark, useToggle } from '@vueuse/core'
|
|
||||||
|
|
||||||
const isDark = useDark()
|
|
||||||
const darkmodeSwitch = ref(isDark)
|
|
||||||
const toggleDark = useToggle(isDark)
|
|
||||||
|
|
||||||
const handleSelect = (key: string) => {
|
|
||||||
if (key == '') {
|
|
||||||
window.open('https://github.com/fatedier/frp')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
width: 100%;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-color {
|
|
||||||
background: #58b7ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark .header-color {
|
|
||||||
background: #395c74;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
margin-top: 20px;
|
|
||||||
padding-right: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand a {
|
|
||||||
color: #fff;
|
|
||||||
background-color: transparent;
|
|
||||||
margin-left: 20px;
|
|
||||||
line-height: 25px;
|
|
||||||
font-size: 25px;
|
|
||||||
padding: 15px 15px;
|
|
||||||
height: 30px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-switch {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
flex-grow: 1;
|
|
||||||
padding-right: 40px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,5 +0,0 @@
|
|||||||
html.dark {
|
|
||||||
--el-bg-color: #343432;
|
|
||||||
--el-fill-color-blank: #343432;
|
|
||||||
background-color: #343432;
|
|
||||||
}
|
|
@ -1,102 +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 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,85 +0,0 @@
|
|||||||
<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"
|
|
||||||
sortable
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="type"
|
|
||||||
label="type"
|
|
||||||
width="150"
|
|
||||||
sortable
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="local_addr"
|
|
||||||
label="local address"
|
|
||||||
width="200"
|
|
||||||
sortable
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="plugin"
|
|
||||||
label="plugin"
|
|
||||||
width="200"
|
|
||||||
sortable
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="remote_addr"
|
|
||||||
label="remote address"
|
|
||||||
sortable
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
prop="status"
|
|
||||||
label="status"
|
|
||||||
width="150"
|
|
||||||
sortable
|
|
||||||
></el-table-column>
|
|
||||||
<el-table-column prop="err" label="info"></el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
|
|
||||||
let status = ref<any[]>([])
|
|
||||||
|
|
||||||
const fetchData = () => {
|
|
||||||
fetch('/api/status', { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
status.value = new Array()
|
|
||||||
for (let key in json) {
|
|
||||||
for (let ps of json[key]) {
|
|
||||||
console.log(ps)
|
|
||||||
status.value.push(ps)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
ElMessage({
|
|
||||||
showClose: true,
|
|
||||||
message: 'Get status info from frpc failed!' + err,
|
|
||||||
type: 'warning',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,13 +0,0 @@
|
|||||||
import { createApp } from 'vue'
|
|
||||||
import 'element-plus/dist/index.css'
|
|
||||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
|
||||||
import App from './App.vue'
|
|
||||||
import router from './router'
|
|
||||||
|
|
||||||
import './assets/dark.css'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
|
||||||
|
|
||||||
app.use(router)
|
|
||||||
|
|
||||||
app.mount('#app')
|
|
@ -1,21 +0,0 @@
|
|||||||
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,25 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
|
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
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: '',
|
|
||||||
},
|
|
||||||
})
|
|
2603
web/frpc/yarn.lock
2603
web/frpc/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -1,30 +0,0 @@
|
|||||||
/* 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'],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
28
web/frps/.gitignore
vendored
28
web/frps/.gitignore
vendored
@ -1,28 +0,0 @@
|
|||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
.DS_Store
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
coverage
|
|
||||||
*.local
|
|
||||||
|
|
||||||
/cypress/videos/
|
|
||||||
/cypress/screenshots/
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"tabWidth": 2,
|
|
||||||
"semi": false,
|
|
||||||
"singleQuote": true
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
.PHONY: dist build preview lint
|
|
||||||
|
|
||||||
build:
|
|
||||||
@npm run build
|
|
||||||
|
|
||||||
dev:
|
|
||||||
@npm run dev
|
|
||||||
|
|
||||||
preview:
|
|
||||||
@npm run preview
|
|
||||||
|
|
||||||
lint:
|
|
||||||
@npm run lint
|
|
@ -1,25 +0,0 @@
|
|||||||
# frps-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
|
|
||||||
```
|
|
9
web/frps/auto-imports.d.ts
vendored
9
web/frps/auto-imports.d.ts
vendored
@ -1,9 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
// Generated by unplugin-auto-import
|
|
||||||
export {}
|
|
||||||
declare global {
|
|
||||||
|
|
||||||
}
|
|
43
web/frps/components.d.ts
vendored
43
web/frps/components.d.ts
vendored
@ -1,43 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/* prettier-ignore */
|
|
||||||
// @ts-nocheck
|
|
||||||
// Generated by unplugin-vue-components
|
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
|
||||||
export {}
|
|
||||||
|
|
||||||
declare module 'vue' {
|
|
||||||
export interface GlobalComponents {
|
|
||||||
ElButton: typeof import('element-plus/es')['ElButton']
|
|
||||||
ElCol: typeof import('element-plus/es')['ElCol']
|
|
||||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
|
||||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
|
||||||
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']
|
|
||||||
ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
|
|
||||||
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
|
|
||||||
ElRow: typeof import('element-plus/es')['ElRow']
|
|
||||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
|
||||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
|
||||||
ElTable: typeof import('element-plus/es')['ElTable']
|
|
||||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
|
||||||
ElTag: typeof import('element-plus/es')['ElTag']
|
|
||||||
ElText: typeof import('element-plus/es')['ElText']
|
|
||||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
|
||||||
LongSpan: typeof import('./src/components/LongSpan.vue')['default']
|
|
||||||
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']
|
|
||||||
ProxiesTCPMux: typeof import('./src/components/ProxiesTCPMux.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
1
web/frps/env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
@ -1,14 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="dark">
|
|
||||||
|
|
||||||
<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,38 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "frps-dashboard",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"private": true,
|
|
||||||
"scripts": {
|
|
||||||
"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": {
|
|
||||||
"@types/humanize-plus": "^1.8.0",
|
|
||||||
"echarts": "^5.4.3",
|
|
||||||
"element-plus": "^2.5.3",
|
|
||||||
"humanize-plus": "^1.8.2",
|
|
||||||
"vue": "^3.4.15",
|
|
||||||
"vue-router": "^4.2.5"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@rushstack/eslint-patch": "^1.7.2",
|
|
||||||
"@types/node": "^18.11.12",
|
|
||||||
"@vitejs/plugin-vue": "^5.0.3",
|
|
||||||
"@vue/eslint-config-prettier": "^9.0.0",
|
|
||||||
"@vue/eslint-config-typescript": "^12.0.0",
|
|
||||||
"@vue/tsconfig": "^0.5.1",
|
|
||||||
"eslint": "^8.56.0",
|
|
||||||
"eslint-plugin-vue": "^9.21.0",
|
|
||||||
"npm-run-all": "^4.1.5",
|
|
||||||
"prettier": "^3.2.4",
|
|
||||||
"typescript": "~5.3.3",
|
|
||||||
"unplugin-auto-import": "^0.17.5",
|
|
||||||
"unplugin-vue-components": "^0.26.0",
|
|
||||||
"vite": "^5.0.12",
|
|
||||||
"vue-tsc": "^1.8.27"
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
@ -1,127 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div id="app">
|
|
||||||
<header class="grid-content header-color">
|
|
||||||
<div class="header-content">
|
|
||||||
<div class="brand">
|
|
||||||
<a href="#">frp</a>
|
|
||||||
</div>
|
|
||||||
<div class="dark-switch">
|
|
||||||
<el-switch
|
|
||||||
v-model="darkmodeSwitch"
|
|
||||||
inline-prompt
|
|
||||||
active-text="Dark"
|
|
||||||
inactive-text="Light"
|
|
||||||
@change="toggleDark"
|
|
||||||
style="
|
|
||||||
--el-switch-on-color: #444452;
|
|
||||||
--el-switch-off-color: #589ef8;
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</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/tcpmux">TCPMUX</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>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useDark, useToggle } from '@vueuse/core'
|
|
||||||
|
|
||||||
const isDark = useDark()
|
|
||||||
const darkmodeSwitch = ref(isDark)
|
|
||||||
const toggleDark = useToggle(isDark)
|
|
||||||
|
|
||||||
const handleSelect = (key: string) => {
|
|
||||||
if (key == '') {
|
|
||||||
window.open('https://github.com/fatedier/frp')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
width: 100%;
|
|
||||||
height: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-color {
|
|
||||||
background: #58b7ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark .header-color {
|
|
||||||
background: #395c74;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
margin-top: 20px;
|
|
||||||
padding-right: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand a {
|
|
||||||
color: #fff;
|
|
||||||
background-color: transparent;
|
|
||||||
margin-left: 20px;
|
|
||||||
line-height: 25px;
|
|
||||||
font-size: 25px;
|
|
||||||
padding: 15px 15px;
|
|
||||||
height: 30px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-switch {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
flex-grow: 1;
|
|
||||||
padding-right: 40px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,22 +0,0 @@
|
|||||||
.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,5 +0,0 @@
|
|||||||
html.dark {
|
|
||||||
--el-bg-color: #343432;
|
|
||||||
--el-fill-color-blank: #343432;
|
|
||||||
background-color: #343432;
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-tooltip :content="content" placement="top">
|
|
||||||
<span v-show="content.length > length"
|
|
||||||
>{{ content.slice(0, length) }}...</span
|
|
||||||
>
|
|
||||||
</el-tooltip>
|
|
||||||
<span v-show="content.length < 30">{{ content }}</span>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
defineProps<{
|
|
||||||
content: string
|
|
||||||
length: number
|
|
||||||
}>()
|
|
||||||
</script>
|
|
@ -1,42 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ProxyView :proxies="proxies" proxyType="http" @refresh="fetchData"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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 vhostHTTPPort: number
|
|
||||||
let subdomainHost: string
|
|
||||||
fetch('../api/serverinfo', { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
vhostHTTPPort = json.vhostHTTPPort
|
|
||||||
subdomainHost = json.subdomainHost
|
|
||||||
if (vhostHTTPPort == null || vhostHTTPPort == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fetch('../api/proxy/http', { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
proxies.value = []
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
proxies.value.push(
|
|
||||||
new HTTPProxy(proxyStats, vhostHTTPPort, subdomainHost)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,42 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ProxyView :proxies="proxies" proxyType="https" @refresh="fetchData"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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 vhostHTTPSPort: number
|
|
||||||
let subdomainHost: string
|
|
||||||
fetch('../api/serverinfo', { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
vhostHTTPSPort = json.vhostHTTPSPort
|
|
||||||
subdomainHost = json.subdomainHost
|
|
||||||
if (vhostHTTPSPort == null || vhostHTTPSPort == 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fetch('../api/proxy/https', { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
proxies.value = []
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
proxies.value.push(
|
|
||||||
new HTTPSProxy(proxyStats, vhostHTTPSPort, subdomainHost)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ProxyView :proxies="proxies" proxyType="stcp" @refresh="fetchData"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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) => {
|
|
||||||
proxies.value = []
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
proxies.value.push(new STCPProxy(proxyStats))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ProxyView :proxies="proxies" proxyType="sudp" @refresh="fetchData"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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) => {
|
|
||||||
proxies.value = []
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
proxies.value.push(new SUDPProxy(proxyStats))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ProxyView :proxies="proxies" proxyType="tcp" @refresh="fetchData" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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) => {
|
|
||||||
proxies.value = []
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
proxies.value.push(new TCPProxy(proxyStats))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,38 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ProxyView :proxies="proxies" proxyType="tcpmux" @refresh="fetchData" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { TCPMuxProxy } from '../utils/proxy.js'
|
|
||||||
import ProxyView from './ProxyView.vue'
|
|
||||||
|
|
||||||
let proxies = ref<TCPMuxProxy[]>([])
|
|
||||||
|
|
||||||
const fetchData = () => {
|
|
||||||
let tcpmuxHTTPConnectPort: number
|
|
||||||
let subdomainHost: string
|
|
||||||
fetch('../api/serverinfo', { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
tcpmuxHTTPConnectPort = json.tcpmuxHTTPConnectPort
|
|
||||||
subdomainHost = json.subdomainHost
|
|
||||||
|
|
||||||
fetch('../api/proxy/tcpmux', { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
proxies.value = []
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
proxies.value.push(new TCPMuxProxy(proxyStats, tcpmuxHTTPConnectPort, subdomainHost))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ProxyView :proxies="proxies" proxyType="udp" @refresh="fetchData"/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<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) => {
|
|
||||||
proxies.value = []
|
|
||||||
for (let proxyStats of json.proxies) {
|
|
||||||
proxies.value.push(new UDPProxy(proxyStats))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style></style>
|
|
@ -1,145 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-page-header
|
|
||||||
:icon="null"
|
|
||||||
style="width: 100%; margin-left: 30px; margin-bottom: 20px"
|
|
||||||
>
|
|
||||||
<template #title>
|
|
||||||
<span>{{ proxyType }}</span>
|
|
||||||
</template>
|
|
||||||
<template #content> </template>
|
|
||||||
<template #extra>
|
|
||||||
<div class="flex items-center" style="margin-right: 30px">
|
|
||||||
<el-popconfirm
|
|
||||||
title="Are you sure to clear all data of offline proxies?"
|
|
||||||
@confirm="clearOfflineProxies"
|
|
||||||
>
|
|
||||||
<template #reference>
|
|
||||||
<el-button>ClearOfflineProxies</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
<el-button @click="$emit('refresh')">Refresh</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-page-header>
|
|
||||||
|
|
||||||
<el-table
|
|
||||||
:data="proxies"
|
|
||||||
:default-sort="{ prop: 'name', order: 'ascending' }"
|
|
||||||
style="width: 100%"
|
|
||||||
>
|
|
||||||
<el-table-column type="expand">
|
|
||||||
<template #default="props">
|
|
||||||
<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="trafficIn"
|
|
||||||
:formatter="formatTrafficIn"
|
|
||||||
sortable
|
|
||||||
>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column
|
|
||||||
label="Traffic Out"
|
|
||||||
prop="trafficOut"
|
|
||||||
:formatter="formatTrafficOut"
|
|
||||||
sortable
|
|
||||||
>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="ClientVersion" prop="clientVersion" 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-column label="Operations">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
:name="scope.row.name"
|
|
||||||
style="margin-bottom: 10px"
|
|
||||||
@click="dialogVisibleName = scope.row.name; dialogVisible = true"
|
|
||||||
>Traffic
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
v-model="dialogVisible"
|
|
||||||
destroy-on-close="true"
|
|
||||||
:title="dialogVisibleName"
|
|
||||||
width="700px">
|
|
||||||
<Traffic :proxyName="dialogVisibleName" />
|
|
||||||
</el-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import * as Humanize from 'humanize-plus'
|
|
||||||
import type { TableColumnCtx } from 'element-plus'
|
|
||||||
import type { BaseProxy } from '../utils/proxy.js'
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import ProxyViewExpand from './ProxyViewExpand.vue'
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
proxies: BaseProxy[]
|
|
||||||
proxyType: string
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const emit = defineEmits(['refresh'])
|
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
|
||||||
const dialogVisibleName = ref("")
|
|
||||||
|
|
||||||
const formatTrafficIn = (row: BaseProxy, _: TableColumnCtx<BaseProxy>) => {
|
|
||||||
return Humanize.fileSize(row.trafficIn)
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatTrafficOut = (row: BaseProxy, _: TableColumnCtx<BaseProxy>) => {
|
|
||||||
return Humanize.fileSize(row.trafficOut)
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearOfflineProxies = () => {
|
|
||||||
fetch('../api/proxies?status=offline', {
|
|
||||||
method: 'DELETE',
|
|
||||||
credentials: 'include',
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.ok) {
|
|
||||||
ElMessage({
|
|
||||||
message: 'Successfully cleared offline proxies',
|
|
||||||
type: 'success',
|
|
||||||
})
|
|
||||||
emit('refresh')
|
|
||||||
} else {
|
|
||||||
ElMessage({
|
|
||||||
message: 'Failed to clear offline proxies: ' + res.status + ' ' + res.statusText,
|
|
||||||
type: 'warning',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
ElMessage({
|
|
||||||
message: 'Failed to clear offline proxies: ' + err.message,
|
|
||||||
type: 'warning',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.el-page-header__title {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,113 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-form
|
|
||||||
label-position="left"
|
|
||||||
label-width="auto"
|
|
||||||
inline
|
|
||||||
class="proxy-table-expand"
|
|
||||||
>
|
|
||||||
<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="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.lastStartTime }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Last Close">
|
|
||||||
<span>{{ row.lastCloseTime }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<div v-if="proxyType === 'http' || proxyType === 'https'">
|
|
||||||
<el-form-item label="Domains">
|
|
||||||
<span>{{ row.customDomains }}</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.hostHeaderRewrite }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<div v-else-if="proxyType === 'tcpmux'">
|
|
||||||
<el-form-item label="Multiplexer">
|
|
||||||
<span>{{ row.multiplexer }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="RouteByHTTPUser">
|
|
||||||
<span>{{ row.routeByHTTPUser }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Domains">
|
|
||||||
<span>{{ row.customDomains }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="SubDomain">
|
|
||||||
<span>{{ row.subdomain }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
<el-form-item label="Addr">
|
|
||||||
<span>{{ row.addr }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<div v-if="row.annotations && row.annotations.size > 0">
|
|
||||||
<el-divider />
|
|
||||||
<el-text class="title-text" size="large">Annotations</el-text>
|
|
||||||
<ul>
|
|
||||||
<li v-for="item in annotationsArray()">
|
|
||||||
<span class="annotation-key">{{ item.key }}</span>
|
|
||||||
<span>{{ item.value }}</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
row: any
|
|
||||||
proxyType: string
|
|
||||||
}>()
|
|
||||||
|
|
||||||
// annotationsArray returns an array of key-value pairs from the annotations map.
|
|
||||||
const annotationsArray = (): Array<{ key: string; value: string }> => {
|
|
||||||
const array: Array<{ key: string; value: any }> = [];
|
|
||||||
if (props.row.annotations) {
|
|
||||||
props.row.annotations.forEach((value: any, key: string) => {
|
|
||||||
array.push({ key, value });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul li {
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul .annotation-key {
|
|
||||||
width: 300px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-text {
|
|
||||||
color: #99a9bf;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,195 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<el-row>
|
|
||||||
<el-col :md="12">
|
|
||||||
<div class="source">
|
|
||||||
<el-form
|
|
||||||
label-position="left"
|
|
||||||
label-width="220px"
|
|
||||||
class="server_info"
|
|
||||||
>
|
|
||||||
<el-form-item label="Version">
|
|
||||||
<span>{{ data.version }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="BindPort">
|
|
||||||
<span>{{ data.bindPort }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="KCP Bind Port" v-if="data.kcpBindPort != 0">
|
|
||||||
<span>{{ data.kcpBindPort }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="QUIC Bind Port" v-if="data.quicBindPort != 0">
|
|
||||||
<span>{{ data.quicBindPort }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="HTTP Port" v-if="data.vhostHTTPPort != 0">
|
|
||||||
<span>{{ data.vhostHTTPPort }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="HTTPS Port" v-if="data.vhostHTTPSPort != 0">
|
|
||||||
<span>{{ data.vhostHTTPSPort }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
label="TCPMux HTTPConnect Port"
|
|
||||||
v-if="data.tcpmuxHTTPConnectPort != 0"
|
|
||||||
>
|
|
||||||
<span>{{ data.tcpmuxHTTPConnectPort }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
label="Subdomain Host"
|
|
||||||
v-if="data.subdomainHost != ''"
|
|
||||||
>
|
|
||||||
<LongSpan :content="data.subdomainHost" :length="30"></LongSpan>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Max PoolCount">
|
|
||||||
<span>{{ data.maxPoolCount }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Max Ports Per Client">
|
|
||||||
<span>{{ data.maxPortsPerClient }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Allow Ports" v-if="data.allowPortsStr != ''">
|
|
||||||
<LongSpan :content="data.allowPortsStr" :length="30"></LongSpan>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="TLS Force" v-if="data.tlsForce === true">
|
|
||||||
<span>{{ data.tlsForce }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="HeartBeat Timeout">
|
|
||||||
<span>{{ data.heartbeatTimeout }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Client Counts">
|
|
||||||
<span>{{ data.clientCounts }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Current Connections">
|
|
||||||
<span>{{ data.curConns }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Proxy Counts">
|
|
||||||
<span>{{ data.proxyCounts }}</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'
|
|
||||||
import LongSpan from './LongSpan.vue'
|
|
||||||
|
|
||||||
let data = ref({
|
|
||||||
version: '',
|
|
||||||
bindPort: 0,
|
|
||||||
kcpBindPort: 0,
|
|
||||||
quicBindPort: 0,
|
|
||||||
vhostHTTPPort: 0,
|
|
||||||
vhostHTTPSPort: 0,
|
|
||||||
tcpmuxHTTPConnectPort: 0,
|
|
||||||
subdomainHost: '',
|
|
||||||
maxPoolCount: 0,
|
|
||||||
maxPortsPerClient: '',
|
|
||||||
allowPortsStr: '',
|
|
||||||
tlsForce: false,
|
|
||||||
heartbeatTimeout: 0,
|
|
||||||
clientCounts: 0,
|
|
||||||
curConns: 0,
|
|
||||||
proxyCounts: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
const fetchData = () => {
|
|
||||||
fetch('../api/serverinfo', { credentials: 'include' })
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((json) => {
|
|
||||||
data.value.version = json.version
|
|
||||||
data.value.bindPort = json.bindPort
|
|
||||||
data.value.kcpBindPort = json.kcpBindPort
|
|
||||||
data.value.quicBindPort = json.quicBindPort
|
|
||||||
data.value.vhostHTTPPort = json.vhostHTTPPort
|
|
||||||
data.value.vhostHTTPSPort = json.vhostHTTPSPort
|
|
||||||
data.value.tcpmuxHTTPConnectPort = json.tcpmuxHTTPConnectPort
|
|
||||||
data.value.subdomainHost = json.subdomainHost
|
|
||||||
data.value.maxPoolCount = json.maxPoolCount
|
|
||||||
data.value.maxPortsPerClient = json.maxPortsPerClient
|
|
||||||
if (data.value.maxPortsPerClient == '0') {
|
|
||||||
data.value.maxPortsPerClient = 'no limit'
|
|
||||||
}
|
|
||||||
data.value.allowPortsStr = json.allowPortsStr
|
|
||||||
data.value.tlsForce = json.tlsForce
|
|
||||||
data.value.heartbeatTimeout = json.heartbeatTimeout
|
|
||||||
data.value.clientCounts = json.clientCounts
|
|
||||||
data.value.curConns = json.curConns
|
|
||||||
data.value.proxyCounts = 0
|
|
||||||
if (json.proxyTypeCount != null) {
|
|
||||||
if (json.proxyTypeCount.tcp != null) {
|
|
||||||
data.value.proxyCounts += json.proxyTypeCount.tcp
|
|
||||||
}
|
|
||||||
if (json.proxyTypeCount.udp != null) {
|
|
||||||
data.value.proxyCounts += json.proxyTypeCount.udp
|
|
||||||
}
|
|
||||||
if (json.proxyTypeCount.http != null) {
|
|
||||||
data.value.proxyCounts += json.proxyTypeCount.http
|
|
||||||
}
|
|
||||||
if (json.proxyTypeCount.https != null) {
|
|
||||||
data.value.proxyCounts += json.proxyTypeCount.https
|
|
||||||
}
|
|
||||||
if (json.proxyTypeCount.stcp != null) {
|
|
||||||
data.value.proxyCounts += json.proxyTypeCount.stcp
|
|
||||||
}
|
|
||||||
if (json.proxyTypeCount.sudp != null) {
|
|
||||||
data.value.proxyCounts += json.proxyTypeCount.sudp
|
|
||||||
}
|
|
||||||
if (json.proxyTypeCount.xtcp != null) {
|
|
||||||
data.value.proxyCounts += json.proxyTypeCount.xtcp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw chart
|
|
||||||
DrawTrafficChart('traffic', json.totalTrafficIn, json.totalTrafficOut)
|
|
||||||
DrawProxyChart('proxies', json)
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
ElMessage({
|
|
||||||
showClose: true,
|
|
||||||
message: 'Get server info from frps failed!',
|
|
||||||
type: 'warning',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.source {
|
|
||||||
border-radius: 4px;
|
|
||||||
transition: 0.2s;
|
|
||||||
padding-left: 24px;
|
|
||||||
padding-right: 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,32 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :id="proxyName" style="width: 600px; height: 400px"></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ElMessage } from 'element-plus'
|
|
||||||
import { DrawProxyTrafficChart } from '../utils/chart.js'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
proxyName: string
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const fetchData = () => {
|
|
||||||
let url = '../api/traffic/' + props.proxyName
|
|
||||||
fetch(url, { credentials: 'include' })
|
|
||||||
.then((res) => {
|
|
||||||
return res.json()
|
|
||||||
})
|
|
||||||
.then((json) => {
|
|
||||||
DrawProxyTrafficChart(props.proxyName, json.trafficIn, json.trafficOut)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
ElMessage({
|
|
||||||
showClose: true,
|
|
||||||
message: 'Get traffic info failed!' + err,
|
|
||||||
type: 'warning',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fetchData()
|
|
||||||
</script>
|
|
||||||
<style></style>
|
|
@ -1,14 +0,0 @@
|
|||||||
import { createApp } from 'vue'
|
|
||||||
import 'element-plus/dist/index.css'
|
|
||||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
|
||||||
import App from './App.vue'
|
|
||||||
import router from './router'
|
|
||||||
|
|
||||||
import './assets/custom.css'
|
|
||||||
import './assets/dark.css'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
|
||||||
|
|
||||||
app.use(router)
|
|
||||||
|
|
||||||
app.mount('#app')
|
|
@ -1,57 +0,0 @@
|
|||||||
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 ProxiesTCPMux from '../components/ProxiesTCPMux.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/tcpmux',
|
|
||||||
name: 'ProxiesTCPMux',
|
|
||||||
component: ProxiesTCPMux,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/proxies/stcp',
|
|
||||||
name: 'ProxiesSTCP',
|
|
||||||
component: ProxiesSTCP,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/proxies/sudp',
|
|
||||||
name: 'ProxiesSUDP',
|
|
||||||
component: ProxiesSUDP,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
export default router
|
|
@ -1,293 +0,0 @@
|
|||||||
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.proxyTypeCount.tcp != null &&
|
|
||||||
serverInfo.proxyTypeCount.tcp != 0
|
|
||||||
) {
|
|
||||||
option.series[0].data.push({
|
|
||||||
value: serverInfo.proxyTypeCount.tcp,
|
|
||||||
name: 'TCP',
|
|
||||||
})
|
|
||||||
option.legend.data.push('TCP')
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
serverInfo.proxyTypeCount.udp != null &&
|
|
||||||
serverInfo.proxyTypeCount.udp != 0
|
|
||||||
) {
|
|
||||||
option.series[0].data.push({
|
|
||||||
value: serverInfo.proxyTypeCount.udp,
|
|
||||||
name: 'UDP',
|
|
||||||
})
|
|
||||||
option.legend.data.push('UDP')
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
serverInfo.proxyTypeCount.http != null &&
|
|
||||||
serverInfo.proxyTypeCount.http != 0
|
|
||||||
) {
|
|
||||||
option.series[0].data.push({
|
|
||||||
value: serverInfo.proxyTypeCount.http,
|
|
||||||
name: 'HTTP',
|
|
||||||
})
|
|
||||||
option.legend.data.push('HTTP')
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
serverInfo.proxyTypeCount.https != null &&
|
|
||||||
serverInfo.proxyTypeCount.https != 0
|
|
||||||
) {
|
|
||||||
option.series[0].data.push({
|
|
||||||
value: serverInfo.proxyTypeCount.https,
|
|
||||||
name: 'HTTPS',
|
|
||||||
})
|
|
||||||
option.legend.data.push('HTTPS')
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
serverInfo.proxyTypeCount.stcp != null &&
|
|
||||||
serverInfo.proxyTypeCount.stcp != 0
|
|
||||||
) {
|
|
||||||
option.series[0].data.push({
|
|
||||||
value: serverInfo.proxyTypeCount.stcp,
|
|
||||||
name: 'STCP',
|
|
||||||
})
|
|
||||||
option.legend.data.push('STCP')
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
serverInfo.proxyTypeCount.sudp != null &&
|
|
||||||
serverInfo.proxyTypeCount.sudp != 0
|
|
||||||
) {
|
|
||||||
option.series[0].data.push({
|
|
||||||
value: serverInfo.proxyTypeCount.sudp,
|
|
||||||
name: 'SUDP',
|
|
||||||
})
|
|
||||||
option.legend.data.push('SUDP')
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
serverInfo.proxyTypeCount.xtcp != null &&
|
|
||||||
serverInfo.proxyTypeCount.xtcp != 0
|
|
||||||
) {
|
|
||||||
option.series[0].data.push({
|
|
||||||
value: serverInfo.proxyTypeCount.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,158 +0,0 @@
|
|||||||
class BaseProxy {
|
|
||||||
name: string
|
|
||||||
type: string
|
|
||||||
annotations: Map<string, string>
|
|
||||||
encryption: boolean
|
|
||||||
compression: boolean
|
|
||||||
conns: number
|
|
||||||
trafficIn: number
|
|
||||||
trafficOut: number
|
|
||||||
lastStartTime: string
|
|
||||||
lastCloseTime: string
|
|
||||||
status: string
|
|
||||||
clientVersion: string
|
|
||||||
addr: string
|
|
||||||
port: number
|
|
||||||
|
|
||||||
customDomains: string
|
|
||||||
hostHeaderRewrite: string
|
|
||||||
locations: string
|
|
||||||
subdomain: string
|
|
||||||
|
|
||||||
constructor(proxyStats: any) {
|
|
||||||
this.name = proxyStats.name
|
|
||||||
this.type = ''
|
|
||||||
this.annotations = new Map<string, string>()
|
|
||||||
if (proxyStats.conf?.annotations) {
|
|
||||||
for (const key in proxyStats.conf.annotations) {
|
|
||||||
this.annotations.set(key, proxyStats.conf.annotations[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.encryption = false
|
|
||||||
this.compression = false
|
|
||||||
this.encryption =
|
|
||||||
proxyStats.conf?.transport?.useEncryption || this.encryption
|
|
||||||
this.compression =
|
|
||||||
proxyStats.conf?.transport?.useCompression || this.compression
|
|
||||||
this.conns = proxyStats.curConns
|
|
||||||
this.trafficIn = proxyStats.todayTrafficIn
|
|
||||||
this.trafficOut = proxyStats.todayTrafficOut
|
|
||||||
this.lastStartTime = proxyStats.lastStartTime
|
|
||||||
this.lastCloseTime = proxyStats.lastCloseTime
|
|
||||||
this.status = proxyStats.status
|
|
||||||
this.clientVersion = proxyStats.clientVersion
|
|
||||||
|
|
||||||
this.addr = ''
|
|
||||||
this.port = 0
|
|
||||||
this.customDomains = ''
|
|
||||||
this.hostHeaderRewrite = ''
|
|
||||||
this.locations = ''
|
|
||||||
this.subdomain = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TCPProxy extends BaseProxy {
|
|
||||||
constructor(proxyStats: any) {
|
|
||||||
super(proxyStats)
|
|
||||||
this.type = 'tcp'
|
|
||||||
if (proxyStats.conf != null) {
|
|
||||||
this.addr = ':' + proxyStats.conf.remotePort
|
|
||||||
this.port = proxyStats.conf.remotePort
|
|
||||||
} 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.remotePort
|
|
||||||
this.port = proxyStats.conf.remotePort
|
|
||||||
} else {
|
|
||||||
this.addr = ''
|
|
||||||
this.port = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HTTPProxy extends BaseProxy {
|
|
||||||
constructor(proxyStats: any, port: number, subdomainHost: string) {
|
|
||||||
super(proxyStats)
|
|
||||||
this.type = 'http'
|
|
||||||
this.port = port
|
|
||||||
if (proxyStats.conf) {
|
|
||||||
this.customDomains = proxyStats.conf.customDomains || this.customDomains
|
|
||||||
this.hostHeaderRewrite = proxyStats.conf.hostHeaderRewrite
|
|
||||||
this.locations = proxyStats.conf.locations
|
|
||||||
if (proxyStats.conf.subdomain) {
|
|
||||||
this.subdomain = `${proxyStats.conf.subdomain}.${subdomainHost}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HTTPSProxy extends BaseProxy {
|
|
||||||
constructor(proxyStats: any, port: number, subdomainHost: string) {
|
|
||||||
super(proxyStats)
|
|
||||||
this.type = 'https'
|
|
||||||
this.port = port
|
|
||||||
if (proxyStats.conf != null) {
|
|
||||||
this.customDomains = proxyStats.conf.customDomains || this.customDomains
|
|
||||||
if (proxyStats.conf.subdomain) {
|
|
||||||
this.subdomain = `${proxyStats.conf.subdomain}.${subdomainHost}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TCPMuxProxy extends BaseProxy {
|
|
||||||
multiplexer: string
|
|
||||||
routeByHTTPUser: string
|
|
||||||
|
|
||||||
constructor(proxyStats: any, port: number, subdomainHost: string) {
|
|
||||||
super(proxyStats)
|
|
||||||
this.type = 'tcpmux'
|
|
||||||
this.port = port
|
|
||||||
this.multiplexer = ''
|
|
||||||
this.routeByHTTPUser = ''
|
|
||||||
|
|
||||||
if (proxyStats.conf) {
|
|
||||||
this.customDomains = proxyStats.conf.customDomains || this.customDomains
|
|
||||||
this.multiplexer = proxyStats.conf.multiplexer
|
|
||||||
this.routeByHTTPUser = proxyStats.conf.routeByHTTPUser
|
|
||||||
if (proxyStats.conf.subdomain) {
|
|
||||||
this.subdomain = `${proxyStats.conf.subdomain}.${subdomainHost}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
TCPMuxProxy,
|
|
||||||
HTTPProxy,
|
|
||||||
HTTPSProxy,
|
|
||||||
STCPProxy,
|
|
||||||
SUDPProxy,
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020",
|
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
||||||
"skipLibCheck": true,
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
|
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
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: '',
|
|
||||||
},
|
|
||||||
})
|
|
2633
web/frps/yarn.lock
2633
web/frps/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user