Files
BYW-VoiceHub/VoiceHub-main/server/api/admin/backup/upload.post.ts
T
Henry_Du 4ce4649aaf
Build VoiceHub FPK / build-fpk (push) Failing after 56m5s
Build and Push Docker Image / build-and-push (push) Canceled after 1h9m35s
初次提交
2026-09-06 08:19:02 +08:00

134 lines
4.1 KiB
TypeScript

import { createError, defineEventHandler } from 'h3'
import { promises as fs } from 'fs'
import path from 'path'
import formidable from 'formidable'
export default defineEventHandler(async (event) => {
try {
// 验证管理员权限
const user = event.context.user
if (!user || !['ADMIN', 'SUPER_ADMIN'].includes(user.role)) {
throw createError({
statusCode: 403,
statusMessage: '权限不足'
})
}
// 创建备份目录
const backupDir = path.join(process.cwd(), 'backups')
try {
await fs.access(backupDir)
} catch {
await fs.mkdir(backupDir, { recursive: true })
}
// 解析上传的文件
const form = formidable({
uploadDir: backupDir,
keepExtensions: true,
maxFileSize: 100 * 1024 * 1024, // 100MB
filter: ({ mimetype, originalFilename }) => {
// 只允许JSON文件
return (
mimetype === 'application/json' ||
(originalFilename && originalFilename.endsWith('.json'))
)
}
})
const [fields, files] = await form.parse(event.node.req)
if (!files.file || !files.file[0]) {
throw createError({
statusCode: 400,
statusMessage: '请选择要上传的备份文件'
})
}
const uploadedFile = files.file[0]
// 验证文件内容
let backupData
try {
const fileContent = await fs.readFile(uploadedFile.filepath, 'utf8')
backupData = JSON.parse(fileContent)
} catch (error) {
// 删除无效文件
await fs.unlink(uploadedFile.filepath)
throw createError({
statusCode: 400,
statusMessage: '备份文件格式无效'
})
}
// 验证备份文件结构
if (!backupData.metadata && !backupData.users) {
await fs.unlink(uploadedFile.filepath)
throw createError({
statusCode: 400,
statusMessage: '不是有效的备份文件'
})
}
// 生成新的文件名
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
const originalName = uploadedFile.originalFilename || 'backup.json'
const fileExtension = path.extname(originalName)
const baseName = path.basename(originalName, fileExtension)
const newFilename = `uploaded-${baseName}-${timestamp}${fileExtension}`
const newFilepath = path.join(backupDir, newFilename)
// 移动文件到新位置
await fs.rename(uploadedFile.filepath, newFilepath)
// 更新备份文件的元数据
if (backupData.metadata) {
// 新格式备份文件
backupData.metadata.uploadedBy = user.username
backupData.metadata.uploadedAt = new Date().toISOString()
backupData.metadata.originalFilename = originalName
backupData.metadata.creator = user.username // 确保创建者信息正确
} else if (backupData.users) {
// 旧格式备份文件,创建元数据
backupData.metadata = {
version: '0.1',
timestamp: backupData.timestamp || new Date().toISOString(),
creator: user.username,
uploadedBy: user.username,
uploadedAt: new Date().toISOString(),
originalFilename: originalName,
description: '用户数据备份(上传)',
tables: [{ name: 'users', recordCount: backupData.totalUsers || backupData.users.length }],
totalRecords: backupData.totalUsers || backupData.users.length
}
}
// 重新保存文件
await fs.writeFile(newFilepath, JSON.stringify(backupData, null, 2), 'utf8')
console.log(`管理员 ${user.username} 上传了备份文件: ${newFilename}`)
// 获取文件信息
const stats = await fs.stat(newFilepath)
return {
success: true,
message: '备份文件上传成功',
backup: {
filename: newFilename,
originalFilename: originalName,
size: stats.size,
uploadedBy: user.username,
uploadedAt: new Date().toISOString(),
metadata: backupData.metadata || null
}
}
} catch (error) {
console.error('上传备份文件失败:', error)
throw createError({
statusCode: error.statusCode || 500,
statusMessage: error.statusMessage || '上传备份文件失败'
})
}
})