add persistent proxy/visitor store with CRUD API and web UI (#5188)

This commit is contained in:
fatedier
2026-03-02 01:09:59 +08:00
committed by GitHub
parent d0347325fc
commit 01997deb98
89 changed files with 13960 additions and 3864 deletions

View File

@@ -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', 'Proxies', 'Clients'],
},
],
},
}

36
web/frps/eslint.config.js Normal file
View File

@@ -0,0 +1,36 @@
import pluginVue from 'eslint-plugin-vue'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
export default [
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
{
name: 'app/files-to-ignore',
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
},
...pluginVue.configs['flat/essential'],
...vueTsEslintConfig(),
{
rules: {
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],
'vue/multi-word-component-names': [
'error',
{
ignores: ['Traffic', 'Proxies', 'Clients'],
},
],
},
},
skipFormatting,
]

File diff suppressed because it is too large Load Diff

View File

@@ -2,13 +2,14 @@
"name": "frps-dashboard",
"version": "0.0.1",
"private": true,
"type": "module",
"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"
"lint": "eslint --fix"
},
"dependencies": {
"element-plus": "^2.13.0",
@@ -16,14 +17,13 @@
"vue-router": "^4.6.4"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.15.0",
"@types/node": "24",
"@vitejs/plugin-vue": "^6.0.3",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.7.0",
"@vue/tsconfig": "^0.8.1",
"@vueuse/core": "^14.1.0",
"eslint": "^8.56.0",
"eslint": "^9.39.0",
"eslint-plugin-vue": "^9.33.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.7.4",

View File

@@ -13,6 +13,9 @@
<span v-if="client.hostname" class="hostname-badge">{{
client.hostname
}}</span>
<el-tag v-if="client.version" size="small" type="success"
>v{{ client.version }}</el-tag
>
</div>
<div class="card-meta">

View File

@@ -86,7 +86,7 @@ const processData = (trafficIn: number[], trafficOut: number[]) => {
// Calculate dates (last 7 days ending today)
const dates: string[] = []
let d = new Date()
const d = new Date()
d.setDate(d.getDate() - 6)
for (let i = 0; i < 7; i++) {

View File

@@ -3,6 +3,7 @@ export interface ClientInfoData {
user: string
clientID: string
runID: string
version?: string
hostname: string
clientIP?: string
metas?: Record<string, string>

View File

@@ -3,7 +3,6 @@ export interface ProxyStatsInfo {
conf: any
user: string
clientID: string
clientVersion: string
todayTrafficIn: number
todayTrafficOut: number
curConns: number

View File

@@ -6,6 +6,7 @@ export class Client {
user: string
clientID: string
runID: string
version: string
hostname: string
ip: string
metas: Map<string, string>
@@ -19,6 +20,7 @@ export class Client {
this.user = data.user
this.clientID = data.clientID
this.runID = data.runID
this.version = data.version || ''
this.hostname = data.hostname
this.ip = data.clientIP || ''
this.metas = new Map<string, string>()

View File

@@ -12,7 +12,6 @@ class BaseProxy {
status: string
user: string
clientID: string
clientVersion: string
addr: string
port: number
@@ -49,7 +48,6 @@ class BaseProxy {
this.status = proxyStats.status
this.user = proxyStats.user || ''
this.clientID = proxyStats.clientID || ''
this.clientVersion = proxyStats.clientVersion
this.addr = ''
this.port = 0

View File

@@ -22,7 +22,12 @@
{{ client.displayName.charAt(0).toUpperCase() }}
</div>
<div class="client-info">
<h1 class="client-name">{{ client.displayName }}</h1>
<div class="client-name-row">
<h1 class="client-name">{{ client.displayName }}</h1>
<el-tag v-if="client.version" size="small" type="success"
>v{{ client.version }}</el-tag
>
</div>
<div class="client-meta">
<span v-if="client.ip" class="meta-item">{{
client.ip
@@ -354,11 +359,18 @@ onMounted(() => {
min-width: 0;
}
.client-name-row {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 4px;
}
.client-name {
font-size: 20px;
font-weight: 500;
color: var(--text-primary);
margin: 0 0 4px 0;
margin: 0;
line-height: 1.3;
}

View File

@@ -230,7 +230,7 @@ const fetchData = async () => {
data.value.proxyCounts += count || 0
})
}
} catch (err) {
} catch {
ElMessage({
showClose: true,
message: 'Get server info from frps failed!',