/** * 备份管理控制器 * @file backupController.js * @description 处理数据备份和恢复相关业务逻辑 */ const backupService = require('../services/backupService'); const logger = require('../utils/logger'); const { validationResult } = require('express-validator'); const cron = require('node-cron'); /** * 创建备份 * @route POST /api/backup/create */ const createBackup = async (req, res) => { try { const { type = 'full', description } = req.body; logger.info(`用户 ${req.user.username} 开始创建备份,类型: ${type}`); const backupResult = await backupService.createFullBackup({ type, description, createdBy: req.user.id }); res.json({ success: true, message: '备份创建成功', data: backupResult }); } catch (error) { logger.error('创建备份失败:', error); res.status(500).json({ success: false, message: error.message || '创建备份失败', error: error.message }); } }; /** * 获取备份列表 * @route GET /api/backup/list */ const getBackupList = async (req, res) => { try { const { page = 1, limit = 10, type } = req.query; let backups = await backupService.getBackupList(); // 按类型过滤 if (type) { backups = backups.filter(backup => backup.type === type); } // 分页 const startIndex = (page - 1) * limit; const endIndex = startIndex + parseInt(limit); const paginatedBackups = backups.slice(startIndex, endIndex); res.json({ success: true, data: paginatedBackups, total: backups.length, page: parseInt(page), limit: parseInt(limit) }); } catch (error) { logger.error('获取备份列表失败:', error); res.status(500).json({ success: false, message: '获取备份列表失败', error: error.message }); } }; /** * 获取备份统计 * @route GET /api/backup/stats */ const getBackupStats = async (req, res) => { try { const stats = await backupService.getBackupStats(); res.json({ success: true, data: stats }); } catch (error) { logger.error('获取备份统计失败:', error); res.status(500).json({ success: false, message: '获取备份统计失败', error: error.message }); } }; /** * 删除备份 * @route DELETE /api/backup/:id */ const deleteBackup = async (req, res) => { try { const { id } = req.params; const result = await backupService.deleteBackup(id); if (result) { logger.info(`用户 ${req.user.username} 删除备份: ${id}`); res.json({ success: true, message: '备份删除成功' }); } else { res.status(404).json({ success: false, message: '备份不存在或删除失败' }); } } catch (error) { logger.error('删除备份失败:', error); res.status(500).json({ success: false, message: '删除备份失败', error: error.message }); } }; /** * 恢复数据库 * @route POST /api/backup/:id/restore */ const restoreBackup = async (req, res) => { try { const { id } = req.params; const { confirm } = req.body; if (!confirm) { return res.status(400).json({ success: false, message: '请确认恢复操作' }); } logger.info(`用户 ${req.user.username} 开始恢复备份: ${id}`); const result = await backupService.restoreDatabase(id); if (result) { res.json({ success: true, message: '数据恢复成功' }); } else { res.status(500).json({ success: false, message: '数据恢复失败' }); } } catch (error) { logger.error('恢复备份失败:', error); res.status(500).json({ success: false, message: '恢复备份失败', error: error.message }); } }; /** * 下载备份文件 * @route GET /api/backup/:id/download */ const downloadBackup = async (req, res) => { try { const { id } = req.params; const path = require('path'); const fs = require('fs'); const archivePath = path.join(__dirname, '../../backups', `${id}.zip`); // 检查文件是否存在 if (!fs.existsSync(archivePath)) { return res.status(404).json({ success: false, message: '备份文件不存在' }); } const stats = fs.statSync(archivePath); const filename = `backup_${id}.zip`; res.setHeader('Content-Type', 'application/zip'); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.setHeader('Content-Length', stats.size); const readStream = fs.createReadStream(archivePath); readStream.pipe(res); logger.info(`用户 ${req.user.username} 下载备份: ${id}`); } catch (error) { logger.error('下载备份失败:', error); res.status(500).json({ success: false, message: '下载备份失败', error: error.message }); } }; /** * 清理过期备份 * @route POST /api/backup/cleanup */ const cleanupBackups = async (req, res) => { try { const deletedCount = await backupService.cleanupExpiredBackups(); logger.info(`用户 ${req.user.username} 执行备份清理,删除了 ${deletedCount} 个过期备份`); res.json({ success: true, message: `清理完成,删除了 ${deletedCount} 个过期备份`, data: { deletedCount } }); } catch (error) { logger.error('清理备份失败:', error); res.status(500).json({ success: false, message: '清理备份失败', error: error.message }); } }; /** * 获取备份健康状态 * @route GET /api/backup/health */ const getBackupHealth = async (req, res) => { try { const health = await backupService.checkBackupHealth(); res.json({ success: true, data: health }); } catch (error) { logger.error('获取备份健康状态失败:', error); res.status(500).json({ success: false, message: '获取备份健康状态失败', error: error.message }); } }; /** * 自动备份任务调度器 */ class BackupScheduler { constructor() { this.tasks = new Map(); } /** * 启动自动备份调度 */ start() { // 每日备份(凌晨2点) const dailyTask = cron.schedule('0 2 * * *', async () => { try { logger.info('开始执行自动日备份'); await backupService.createFullBackup({ type: 'daily', description: '自动日备份' }); logger.info('自动日备份完成'); } catch (error) { logger.error('自动日备份失败:', error); } }, { scheduled: false }); // 每周备份(周日凌晨3点) const weeklyTask = cron.schedule('0 3 * * 0', async () => { try { logger.info('开始执行自动周备份'); await backupService.createFullBackup({ type: 'weekly', description: '自动周备份' }); logger.info('自动周备份完成'); } catch (error) { logger.error('自动周备份失败:', error); } }, { scheduled: false }); // 每月备份(每月1号凌晨4点) const monthlyTask = cron.schedule('0 4 1 * *', async () => { try { logger.info('开始执行自动月备份'); await backupService.createFullBackup({ type: 'monthly', description: '自动月备份' }); logger.info('自动月备份完成'); } catch (error) { logger.error('自动月备份失败:', error); } }, { scheduled: false }); // 自动清理任务(每天凌晨5点) const cleanupTask = cron.schedule('0 5 * * *', async () => { try { logger.info('开始执行自动备份清理'); const deletedCount = await backupService.cleanupExpiredBackups(); logger.info(`自动备份清理完成,删除了 ${deletedCount} 个过期备份`); } catch (error) { logger.error('自动备份清理失败:', error); } }, { scheduled: false }); this.tasks.set('daily', dailyTask); this.tasks.set('weekly', weeklyTask); this.tasks.set('monthly', monthlyTask); this.tasks.set('cleanup', cleanupTask); // 启动所有任务 this.tasks.forEach((task, name) => { task.start(); logger.info(`备份调度任务已启动: ${name}`); }); } /** * 停止自动备份调度 */ stop() { this.tasks.forEach((task, name) => { task.stop(); logger.info(`备份调度任务已停止: ${name}`); }); } /** * 获取调度状态 */ getStatus() { const status = {}; this.tasks.forEach((task, name) => { status[name] = { running: task.running, lastExecution: task.lastExecution, nextExecution: task.nextExecution }; }); return status; } } // 创建调度器实例 const scheduler = new BackupScheduler(); /** * 启动自动备份调度 * @route POST /api/backup/schedule/start */ const startScheduler = async (req, res) => { try { scheduler.start(); logger.info(`用户 ${req.user.username} 启动自动备份调度`); res.json({ success: true, message: '自动备份调度已启动' }); } catch (error) { logger.error('启动备份调度失败:', error); res.status(500).json({ success: false, message: '启动备份调度失败', error: error.message }); } }; /** * 停止自动备份调度 * @route POST /api/backup/schedule/stop */ const stopScheduler = async (req, res) => { try { scheduler.stop(); logger.info(`用户 ${req.user.username} 停止自动备份调度`); res.json({ success: true, message: '自动备份调度已停止' }); } catch (error) { logger.error('停止备份调度失败:', error); res.status(500).json({ success: false, message: '停止备份调度失败', error: error.message }); } }; /** * 获取调度状态 * @route GET /api/backup/schedule/status */ const getSchedulerStatus = async (req, res) => { try { const status = scheduler.getStatus(); res.json({ success: true, data: status }); } catch (error) { logger.error('获取调度状态失败:', error); res.status(500).json({ success: false, message: '获取调度状态失败', error: error.message }); } }; module.exports = { createBackup, getBackupList, getBackupStats, deleteBackup, restoreBackup, downloadBackup, cleanupBackups, getBackupHealth, startScheduler, stopScheduler, getSchedulerStatus, scheduler };