feat: implement VPN platform management and real protocol services
This commit is contained in:
commit
58c036c34c
45 files changed
+9244
No files matched your search
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="10">
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>修改密码</b></template>
|
||||
<el-form label-width="90px">
|
||||
<el-form-item label="原密码"><el-input v-model="pwd.old_password" type="password" show-password /></el-form-item>
|
||||
<el-form-item label="新密码"><el-input v-model="pwd.new_password" type="password" show-password /></el-form-item>
|
||||
<el-form-item label="确认新密码"><el-input v-model="pwd.confirm" type="password" show-password /></el-form-item>
|
||||
<el-button type="primary" style="width:100%" @click="changePwd">修改</el-button>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<el-card class="page-card" shadow="never" style="margin-top:16px">
|
||||
<template #header><b>双因素认证(MFA)</b></template>
|
||||
<el-alert type="info" :closable="false" title="使用 Microsoft Authenticator、Google Authenticator 等应用扫描二维码。" style="margin-bottom:10px" />
|
||||
<el-button v-if="!mfa.enabled" type="primary" @click="setupMFA">设置 MFA</el-button>
|
||||
<el-button v-else type="danger" @click="disableMFA">关闭 MFA</el-button>
|
||||
<div v-if="mfa.uri" style="margin-top:12px"><canvas ref="mfaCanvas"></canvas><el-input v-model="mfa.code" placeholder="输入 App 显示的 6 位验证码" style="margin-top:8px" /><el-button type="success" style="margin-top:8px" @click="enableMFA">验证并启用</el-button></div>
|
||||
</el-card>
|
||||
<el-card class="page-card" shadow="never" style="margin-top:16px">
|
||||
<template #header><b>部署说明</b></template>
|
||||
<el-descriptions :column="1" size="small" border>
|
||||
<el-descriptions-item label="交付形态">单二进制(前端已内嵌)或 Docker</el-descriptions-item>
|
||||
<el-descriptions-item label="数据目录">./data(可用 VPN_DATA_DIR 覆盖)</el-descriptions-item>
|
||||
<el-descriptions-item label="监听地址">:8080(VPN_LISTEN_ADDR 覆盖)</el-descriptions-item>
|
||||
<el-descriptions-item label="密钥存储">PSK / 私钥 AES-GCM 加密落库,配置文件 0600 权限</el-descriptions-item>
|
||||
<el-descriptions-item label="系统依赖">wireguard-tools / strongswan / xl2tpd / openvpn / frp(缺失时标记为未部署,无法启动)</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<el-card class="page-card" shadow="never">
|
||||
<template #header><b>操作审计(最近 100 条)</b></template>
|
||||
<el-table :data="logs" size="small" max-height="560">
|
||||
<el-table-column prop="created_at" label="时间" width="170">
|
||||
<template #default="{ row }">{{ new Date(row.created_at).toLocaleString('zh-CN') }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="username" label="用户" width="90" />
|
||||
<el-table-column prop="action" label="动作" width="140" />
|
||||
<el-table-column prop="target" label="对象" show-overflow-tooltip />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import QRCode from 'qrcode'
|
||||
import api from '../api'
|
||||
|
||||
const pwd = ref({ old_password: '', new_password: '', confirm: '' })
|
||||
const logs = ref([])
|
||||
const mfa = ref({ enabled: false, uri: '', code: '' })
|
||||
const mfaCanvas = ref(null)
|
||||
|
||||
async function changePwd() {
|
||||
if (pwd.value.new_password !== pwd.value.confirm) return ElMessage.warning('两次新密码不一致')
|
||||
try {
|
||||
await api.post('/password', pwd.value)
|
||||
ElMessage.success('密码已修改')
|
||||
pwd.value = { old_password: '', new_password: '', confirm: '' }
|
||||
} catch (e) {
|
||||
ElMessage.error(e.response?.data?.error || '修改失败')
|
||||
}
|
||||
}
|
||||
onMounted(async () => {
|
||||
const [audit, status] = await Promise.all([api.get('/audit'), api.get('/mfa/status')])
|
||||
logs.value = audit.data || []
|
||||
mfa.value.enabled = status.data.enabled
|
||||
})
|
||||
async function setupMFA() { const { data } = await api.post('/mfa/setup'); mfa.value.uri = data.otpauth_url; await nextTick(); QRCode.toCanvas(mfaCanvas.value, data.otpauth_url, { width: 180 }) }
|
||||
async function enableMFA() { try { const { data } = await api.post('/mfa/enable', { code: mfa.value.code }); mfa.value = { enabled: true, uri: '', code: '' }; ElMessage.success(`MFA 已启用,请保存备用码:${data.backup_codes.join(' ')}`) } catch (e) { ElMessage.error(e.response?.data?.error || '验证失败') } }
|
||||
async function disableMFA() { await api.post('/mfa/disable'); mfa.value = { enabled: false, uri: '', code: '' }; ElMessage.success('MFA 已关闭') }
|
||||
</script>
|
||||
Reference in new issue
Block a user