feat: 后端限速实现 + 前端i18n全面国际化 + 安全与代码质量修复
后端: - trafficmgr: 实现 tc htb 限速(ApplyRateLimit/RemoveRateLimit),自动推导接口名 - 各协议服务: 代码质量改进与错误处理增强 前端: - protocols.js: 170个硬编码中文字符串全部转为 i18n key,中英文双语翻译 - 提取 fmtBytes() 到 utils/format.js 公共模块(消除 4 处重复) - router.js: 添加路由级权限守卫,非管理员无法直接 URL 访问管理页 - Tunnels.vue: SSE 日志流改用 fetch+ReadableStream,token 放 Authorization header - VPNUsers.vue: 修复 catch 块中文硬编码 fallback - 新增 TaskProgress 组件、taskmgr 后端任务管理
This commit is contained in:
1 parent
021d012478
commit
91974fc30f
26 files changed
+1977
-377
No files matched your search
@@ -34,7 +34,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.traffic')" width="170">
|
||||
<template #default="{ row }">{{ bytes(row.rx_bytes) }} / {{ bytes(row.tx_bytes) }}</template>
|
||||
<template #default="{ row }">{{ fmtBytes(row.rx_bytes) }} / {{ fmtBytes(row.tx_bytes) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('vpnUsers.lastOnline')" width="180">
|
||||
<template #default="{ row }">{{ row.last_seen_at ? new Date(row.last_seen_at).toLocaleString() : '-' }}</template>
|
||||
@@ -60,7 +60,7 @@
|
||||
<el-form-item :label="$t('vpnUsers.dialTunnel')" required>
|
||||
<el-select v-model="form.tunnel_id" style="width:100%" filterable>
|
||||
<el-option v-for="t in tunnels" :key="t.id" :value="t.id"
|
||||
:label="`${t.name} (${protocolDisplay(t.protocol)})`" />
|
||||
:label="`${t.name} (${$t(protocolDisplay(t.protocol))})`" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('common.username')" required>
|
||||
@@ -175,6 +175,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import QRCode from 'qrcode'
|
||||
import api from '../api'
|
||||
import { fmtBytes } from '../utils/format'
|
||||
import { protocolDisplay } from '../protocols'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -199,17 +200,10 @@ const mfaSaving = ref(false)
|
||||
const pinVisible = ref(false)
|
||||
const pinValue = ref('')
|
||||
|
||||
const bytes = n => {
|
||||
if (!n) return '0 B'
|
||||
const u = ['B', 'KB', 'MB', 'GB', 'TB']
|
||||
let i = 0
|
||||
while (n >= 1024 && i < 4) { n /= 1024; i++ }
|
||||
return `${n.toFixed(1)} ${u[i]}`
|
||||
}
|
||||
const tunnelName = id => tunnels.value.find(t => t.id === id)?.name || `#${id}`
|
||||
const tunnelProto = id => {
|
||||
const tun = tunnels.value.find(x => x.id === id)
|
||||
return tun ? protocolDisplay(tun.protocol) : ''
|
||||
return tun ? t(protocolDisplay(tun.protocol)) : ''
|
||||
}
|
||||
const isOpenVPN = id => tunnels.value.find(x => x.id === id)?.protocol === 'openvpn'
|
||||
const isLocked = row => row.mfa_locked_until && new Date(row.mfa_locked_until) > new Date()
|
||||
@@ -259,14 +253,22 @@ async function create() {
|
||||
}
|
||||
|
||||
async function toggle(row) {
|
||||
await api.put(`/vpn-users/${row.id}`, { enabled: !row.enabled, display_name: row.display_name, email: row.email })
|
||||
load()
|
||||
try {
|
||||
await api.put(`/vpn-users/${row.id}`, { enabled: !row.enabled, display_name: row.display_name, email: row.email })
|
||||
load()
|
||||
} catch (e) {
|
||||
ElMessage.error(e.response?.data?.error || t('common.operationFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(row) {
|
||||
await ElMessageBox.confirm(t('vpnUsers.deleteVpnUserConfirm', { username: row.username }), t('common.confirm'))
|
||||
await api.delete(`/vpn-users/${row.id}`)
|
||||
load()
|
||||
try {
|
||||
await ElMessageBox.confirm(t('vpnUsers.deleteVpnUserConfirm', { username: row.username }), t('common.confirm'))
|
||||
await api.delete(`/vpn-users/${row.id}`)
|
||||
load()
|
||||
} catch (e) {
|
||||
if (e !== 'cancel') ElMessage.error(e.response?.data?.error || t('common.deleteFailed'))
|
||||
}
|
||||
}
|
||||
|
||||
async function download(row) {
|
||||
@@ -311,7 +313,7 @@ async function refreshOTPStatus() {
|
||||
mfa_locked: isLocked(otpUser.value),
|
||||
mfa_locked_until: otpUser.value.mfa_locked_until,
|
||||
mfa_last_used_at: otpUser.value.mfa_last_used_at,
|
||||
compatible: 'TOTP 标准(兼容 Google/Microsoft Authenticator)'
|
||||
compatible: t('vpnUsers.totpCompatible')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user