134 lines
4.1 KiB
TypeScript
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 || '上传备份文件失败'
|
|
})
|
|
}
|
|
})
|