Files
BYW-VoiceHub/VoiceHub-main/server/api/admin/backup/list.get.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

156 lines
4.8 KiB
TypeScript

import { createError, defineEventHandler } from 'h3'
import { promises as fs } from 'fs'
import path from 'path'
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 isVercel = process.env.VERCEL || process.env.VERCEL_ENV
const isNetlify = process.env.NETLIFY
const isServerless = isVercel || isNetlify
if (isServerless) {
// 在无服务器环境中,无法持久化存储文件,返回空列表
console.log('🌐 检测到无服务器环境,备份文件无法持久化存储')
return {
success: true,
backups: [],
total: 0,
message: '无服务器环境中备份文件无法持久化存储,请使用直接下载功能'
}
}
const backupDir = path.join(process.cwd(), 'backups')
// 检查备份目录是否存在
try {
await fs.access(backupDir)
} catch {
// 如果目录不存在,尝试创建它
try {
await fs.mkdir(backupDir, { recursive: true })
} catch (mkdirError) {
console.warn('无法创建备份目录,可能在只读文件系统中:', mkdirError.message)
return {
success: true,
backups: [],
total: 0,
message: '文件系统只读,备份文件无法持久化存储'
}
}
return {
success: true,
backups: [],
total: 0
}
}
// 读取备份目录中的文件
const files = await fs.readdir(backupDir)
const backupFiles = files.filter(
(file) =>
file.endsWith('.json') &&
(file.startsWith('database-backup-') ||
file.startsWith('users-backup-') ||
file.startsWith('users-system-backup-') ||
file.startsWith('songs-backup-') ||
file.startsWith('songs-system-backup-') ||
file.startsWith('system-settings-backup-') ||
file.startsWith('uploaded-'))
)
const backups = []
for (const filename of backupFiles) {
try {
const filepath = path.join(backupDir, filename)
const stats = await fs.stat(filepath)
// 尝试读取备份文件的元数据
let metadata = null
try {
const fileContent = await fs.readFile(filepath, 'utf8')
const backupData = JSON.parse(fileContent)
if (backupData.metadata) {
metadata = backupData.metadata
} else if (backupData.users) {
// 兼容旧格式的用户备份文件
metadata = {
version: '0.1',
timestamp: backupData.timestamp || stats.mtime.toISOString(),
creator: 'system',
description: '用户数据备份(旧格式)',
tables: [
{ name: 'users', recordCount: backupData.totalUsers || backupData.users.length }
],
totalRecords: backupData.totalUsers || backupData.users.length
}
}
} catch (parseError) {
console.warn(`无法解析备份文件 ${filename}:`, parseError.message)
}
// 判断备份类型
let type = 'users' // 默认为用户备份
if (filename.startsWith('database-backup-')) {
type = 'full'
} else if (
filename.startsWith('users-backup-') ||
filename.startsWith('users-system-backup-')
) {
type = 'users'
} else if (filename.startsWith('songs-backup-') || filename.startsWith('songs-system-backup-')) {
type = 'songs'
} else if (filename.startsWith('system-settings-backup-')) {
type = 'system'
} else if (filename.startsWith('uploaded-')) {
// 对于上传的文件,根据元数据判断类型
if (metadata) {
if (metadata.backupType) {
type = metadata.backupType
} else if (metadata.tables && metadata.tables.length > 1) {
type = 'full'
}
}
}
backups.push({
filename,
size: stats.size,
createdAt: stats.mtime,
metadata,
type,
isValid: metadata !== null
})
} catch (error) {
console.error(`处理备份文件 ${filename} 失败:`, error)
}
}
// 按创建时间倒序排列
backups.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
return {
success: true,
backups,
total: backups.length
}
} catch (error) {
console.error('获取备份文件列表失败:', error)
throw createError({
statusCode: 500,
statusMessage: '获取备份文件列表失败'
})
}
})