mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-10-15 12:14:37 +00:00
feat: 审计 CSV 导出 — 用户/API Key/集群操作日志一键下载
新增端点 (read 级, 审计 viewer 角色即可导出): - GET /audit/users.csv: 账号清单 + 最后登录时间 - GET /audit/apikeys.csv: 密钥清单 (前缀+scope+最后使用+过期) - GET /audit/cluster-log.csv: 令牌环操作日志时间线 (seq/time_utc/node/kind/detail/data_json 六列, detail 为人读摘要, data_json 保留无损原始载荷) 安全设计: - RFC4180 转义 (引号/逗号/换行) - 公式注入防御: =/+/@/tab/- 开头单元格加 ' 前缀 - ISO8601 UTC 时间戳, Excel 直接排序 - 明文密钥不可逆, 仅导出展示前缀 前端: - UsersView: 账号表/API 密钥表各加「⤓ 导出 CSV」按钮 - ClusterView: 日志导出下拉新增 CSV 选项 (走服务端生成) - api.ts: downloadAuditCsv() 统一下载管道 测试: csvEscape 全用例 / users+apikeys CSV 内容断言 / 未认证 401
This commit is contained in:
@ -150,6 +150,7 @@
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="json">JSON(完整结构)</el-dropdown-item>
|
||||
<el-dropdown-item command="txt">文本(可读 TSV)</el-dropdown-item>
|
||||
<el-dropdown-item command="csv">CSV(审计用,Excel 可开)</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
@ -194,7 +195,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { api } from '../api'
|
||||
import { api, downloadAuditCsv } from '../api'
|
||||
import type { RingSnapshot, RingTaskInfo, RingLogEntry } from '../types'
|
||||
|
||||
const ring = ref<RingSnapshot | null>(null)
|
||||
@ -321,8 +322,18 @@ const detailOf = (e: RingLogEntry): string => {
|
||||
}
|
||||
}
|
||||
// exportLog downloads the FULL cluster log (snapshot carries every entry) as
|
||||
// either pretty JSON (lossless) or a readable TSV text file, sorted by seq.
|
||||
const exportLog = (fmt: string) => {
|
||||
// pretty JSON (lossless), a readable TSV text file, or server-generated RFC4180
|
||||
// CSV (audit deliverable, Excel-friendly) — sorted by seq.
|
||||
const exportLog = async (fmt: string) => {
|
||||
if (fmt === 'csv') {
|
||||
// Server-side CSV: RFC4180 escaping + formula-injection defence + ISO8601 UTC.
|
||||
try {
|
||||
await downloadAuditCsv('cluster-log')
|
||||
} catch (e: any) {
|
||||
ElMessage.error('导出失败: ' + (e?.message || e))
|
||||
}
|
||||
return
|
||||
}
|
||||
const entries = [...(ring.value?.log ?? [])].sort((a, b) => a.seq - b.seq)
|
||||
if (!entries.length) return
|
||||
let content = ''
|
||||
|
||||
Reference in New Issue
Block a user