Lumin Admin 备份还原系统:全站数据安全保障
📌 概述
备份还原系统是博客数据安全的核心保障,支持一键全站备份、从备份恢复、自动定时备份、云存储备份等功能。所有备份文件以 tar.gz 格式存储,包含站点内容和配置的完整快照。
本功能基于 Node.js
child_process.execSync调用系统tar命令实现打包,备份文件存储在admin/_backups/目录。
一、功能概览
| 功能 | 说明 |
|---|---|
| 一键全站备份 | 将 content、static、hugo.toml、config 打包为 tar.gz |
| 备份恢复 | 从备份文件还原全站数据 |
| 备份下载 | 下载备份文件到本地 |
| 备份删除 | 删除不需要的备份 |
| 自动备份 | 支持每天/每周/每月自动备份 |
| 保留策略 | 可设置保留份数(1-50) |
| 云存储备份 | 可选择已配置的图床作为备份存储 |
二、后端实现
2.1 备份目录与配置
1const BACKUP_DIR = join(ROOT, 'admin', '_backups')
2const BACKUP_CONFIG = join(ROOT, 'admin', 'backupConfig.json')
备份配置结构:
1{
2 "autoBackup": false,
3 "interval": "daily",
4 "retention": 10,
5 "cloudProvider": ""
6}
2.2 创建备份
1app.post('/api/backup/create', (req, res) => {
2 const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
3 const filename = `backup-${ts}.tar.gz`
4 const filepath = join(BACKUP_DIR, filename)
5
6 const dirsToBackup = ['content', 'static', 'hugo.toml', 'config']
7 .filter(d => existsSync(join(ROOT, d)))
8
9 execSync(`tar -czf "${filepath}" -C "${ROOT}" ${dirsToBackup.join(' ')}`, {
10 cwd: ROOT,
11 shell: true
12 })
13
14 const stat = statSync(filepath)
15 ok(res, { filename, size: stat.size, created: new Date().toISOString() })
16})
备份文件命名格式:backup-2026-05-22T12-30-00.tar.gz
2.3 备份范围
| 目录/文件 | 说明 | 是否必须存在 |
|---|---|---|
content/ | 所有文章内容 | 是 |
static/ | 静态资源 | 是 |
hugo.toml | 站点配置 | 是 |
config/ | 额外配置目录 | 否(如存在则包含) |
2.4 恢复备份
1app.post('/api/backup/restore/:filename', (req, res) => {
2 const fp = join(BACKUP_DIR, req.params.filename)
3 if (!fp.startsWith(BACKUP_DIR)) return fail(res, 403, 'Access denied')
4 if (!existsSync(fp)) return fail(res, 404, 'Backup not found')
5
6 execSync(`tar -xzf "${fp}" -C "${ROOT}"`, { cwd: ROOT, shell: true })
7 ok(res, { status: 'ok' })
8})
2.5 备份列表
1app.get('/api/backup/list', (req, res) => {
2 const files = readdirSync(BACKUP_DIR)
3 .filter(f => f.endsWith('.tar.gz'))
4 .map(name => {
5 const fp = join(BACKUP_DIR, name)
6 const s = statSync(fp)
7 return { filename: name, size: s.size, created: s.mtime.toISOString() }
8 })
9 .sort((a, b) => b.created.localeCompare(a.created))
10 ok(res, { files })
11})
2.6 安全措施
1// 路径安全检查:确保文件路径在备份目录内
2app.delete('/api/backup/:filename', (req, res) => {
3 const fp = join(BACKUP_DIR, req.params.filename)
4 if (!fp.startsWith(BACKUP_DIR)) return fail(res, 403, 'Access denied')
5 // ...
6})
7
8// 下载接口:使用 res.download() 确保安全
9app.get('/api/backup/download/:filename', (req, res) => {
10 const fp = join(BACKUP_DIR, req.params.filename)
11 if (!fp.startsWith(BACKUP_DIR)) return res.status(403).json({ error: 'Access denied' })
12 res.download(fp, req.params.filename)
13})
2.7 API 接口
| 接口 | 方法 | 说明 |
|---|---|---|
/api/backup/config | GET | 获取备份配置 |
/api/backup/config | PUT | 更新备份配置 |
/api/backup/create | POST | 创建全站备份 |
/api/backup/list | GET | 获取备份列表 |
/api/backup/:filename | DELETE | 删除备份 |
/api/backup/restore/:filename | POST | 从备份恢复 |
/api/backup/download/:filename | GET | 下载备份文件 |
三、前端实现
3.1 页面布局
1┌──────────────────────────────┐ ┌──────────────────┐
2│ 📦 备份文件 │ │ ⚙️ 备份设置 │
3│ │ │ │
4│ [立即备份] │ │ 自动备份 [开关] │
5│ │ │ 备份频率 [选择] │
6│ 文件名 大小 时间 操作 │ │ 保留份数 [数字] │
7│ backup-1 390M 12:00 ... │ │ 云存储 [选择] │
8│ backup-2 385M 11:00 ... │ ├──────────────────┤
9│ │ │ 📊 备份统计 │
10│ │ │ 备份总数: 2 │
11│ │ │ 占用空间: 775MB │
12│ │ │ 最近备份: 12:00 │
13└──────────────────────────────┘ └──────────────────┘
3.2 恢复确认
恢复操作需要二次确认,防止误操作:
1async function restoreBackup(row) {
2 await ElMessageBox.confirm(
3 `确定从备份 ${row.filename} 恢复?当前数据将被覆盖!`,
4 '确认恢复',
5 { type: 'warning', confirmButtonText: '确认恢复', cancelButtonText: '取消' }
6 )
7 await restoreBackupApi(row.filename)
8 ElMessage.success('恢复成功,建议重启服务')
9}
3.3 云存储备份
备份设置中的「云存储备份」选项,动态获取已配置的图床列表:
1async function loadCloudProviders() {
2 const res = await getImageHostingProviders()
3 cloudProviders.value = Object.values(res.data.providers || {})
4 .filter(p => p.configured && p.enabled)
5}
3.4 备份统计
1const totalSize = computed(() =>
2 backups.value.reduce((sum, b) => sum + (b.size || 0), 0)
3)
四、后续规划
- 增量备份:仅备份变更文件,减小备份体积
- 定时自动备份:基于调度器实现,按配置频率自动执行
- 云存储上传:备份完成后自动上传到配置的图床
- 备份加密:对备份文件进行加密保护
- 备份对比:对比两个备份之间的差异
五、文件变更清单
| 文件 | 变更 |
|---|---|
admin/backend/server.js | 新增 7 个备份 API |
admin/frontend/src/views/Backup/Index.vue | 新建备份还原页面 |
admin/frontend/src/api/index.js | 新增 7 个备份 API 函数 |
admin/frontend/src/router/index.js | /backup 路由从 Placeholder 改为真实页面 |
admin/frontend/src/layouts/MainLayout.vue | 菜单从「备份」改为「备份还原」 |
admin/_backups/ | 备份文件存储目录 |
admin/backupConfig.json | 备份配置文件 |
留言评论
期待你的想法评论加载中