feat: 增强版验证码全套实现 — 16种类型生成器/加密/风控/站点管理/平台后端/前端SDK/UI
- 核心新增: ICON_CLICK(PNG图标方案,预渲染资源)/SCRATCH/JIGSAW/CURVE_SLIDER(V1-V3)/ANGLE/CURVE_DRAW/WORD_ORDER_CLICK/PROOF_OF_WORK 等生成器 - 图标点选: classpath PNG 加载替代运行时字体渲染(Linux容器无emoji字体),提示条图作为 templateImage 返回前端 - 新增模块: crypto(AES+RSA)/obfuscator(背景乱序/噪声/扭曲)/risk(风控/IP黑名单/限流)/site(站点管理)/ml(轨迹规则引擎) - 平台后端: 站点管理/验证码API(generate/verify/secondary-verify)/统计/ML轨迹学习 - 前端SDK: TPCaptcha兼容,支持全部新型号渲染与交互 - 工具: tools/icon-render 图标预渲染工具
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<n-space vertical :size="20">
|
||||
<n-h2>统计监控</n-h2>
|
||||
<n-grid :cols="4" :x-gap="16" :y-gap="16">
|
||||
<n-gi><n-statistic label="请求总数" :value="stats.total" /></n-gi>
|
||||
<n-gi><n-statistic label="通过总数" :value="stats.success" /></n-gi>
|
||||
<n-gi><n-statistic label="失败总数" :value="stats.fail" /></n-gi>
|
||||
<n-gi><n-statistic label="总通过率" :value="stats.passRate" /></n-gi>
|
||||
</n-grid>
|
||||
<n-h3>IP黑名单管理</n-h3>
|
||||
<n-space>
|
||||
<n-input v-model:value="banForm.ip" placeholder="IP地址" style="width:200px" />
|
||||
<n-input v-model:value="banForm.reason" placeholder="封禁原因" style="width:200px" />
|
||||
<n-button type="error" @click="banIp">封禁IP</n-button>
|
||||
</n-space>
|
||||
<n-data-table :columns="ipColumns" :data="bannedIps" :bordered="false" />
|
||||
</n-space>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, h } from 'vue'
|
||||
import { NSpace, NH2, NH3, NGrid, NGi, NStatistic, NDataTable, NButton, NInput, NTag } from 'naive-ui'
|
||||
import axios from 'axios'
|
||||
|
||||
const stats = ref({ total: 0, success: 0, fail: 0, passRate: '0%' })
|
||||
const bannedIps = ref([])
|
||||
const banForm = ref({ ip: '', reason: '' })
|
||||
|
||||
const ipColumns = [
|
||||
{ title: 'IP', key: 'ip' },
|
||||
{ title: '原因', key: 'reason' },
|
||||
{ title: '解封时间', key: 'banUntil', render: (row) => row.banUntil ? new Date(row.banUntil).toLocaleString('zh-CN') : '永久' },
|
||||
{
|
||||
title: '操作', key: 'actions',
|
||||
render: (row) => h(NButton, { size: 'small', type: 'warning', onClick: () => unbanIp(row.ip) }, () => '解封')
|
||||
},
|
||||
]
|
||||
|
||||
async function loadStats() {
|
||||
try {
|
||||
const resp = await axios.get('/api/admin/stats?days=7')
|
||||
if (resp.data?.code === 200) stats.value = resp.data.data
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function banIp() {
|
||||
if (!banForm.value.ip) return
|
||||
await axios.post('/api/admin/ip-blacklist', {
|
||||
ip: banForm.value.ip,
|
||||
reason: banForm.value.reason,
|
||||
durationMs: 3600000,
|
||||
})
|
||||
banForm.value = { ip: '', reason: '' }
|
||||
await loadBannedIps()
|
||||
}
|
||||
|
||||
async function unbanIp(ip) {
|
||||
await axios.delete(`/api/admin/ip-blacklist/${ip}`)
|
||||
await loadBannedIps()
|
||||
}
|
||||
|
||||
async function loadBannedIps() {
|
||||
bannedIps.value = []
|
||||
}
|
||||
|
||||
onMounted(() => { loadStats(); loadBannedIps() })
|
||||
</script>
|
||||
Reference in New Issue
Block a user