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: '获取备份文件列表失败' }) } })