Skip to content

Commit d86513a

Browse files
committed
feat: 添加归档验证机制,优化备份流程和日志记录
1 parent 2f4d5a0 commit d86513a

File tree

6 files changed

+107
-27
lines changed

6 files changed

+107
-27
lines changed

internal/app/backup.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ func New(cfg *config.Config) *App {
2525
// Run 执行完整的备份流程:检查 -> 备份 -> 打包 -> 清理
2626
func (a *App) Run() error {
2727
startTime := time.Now()
28-
// 记录备份总耗时
29-
defer func(t time.Time) {
30-
duration := time.Since(t)
31-
slog.Info("✅ 备份完成", "duration", duration)
32-
}(startTime)
3328

3429
timestamp := startTime.Format("20060102_150405")
3530
slog.Info("🚀 开始备份", "timestamp", timestamp)
@@ -62,11 +57,17 @@ func (a *App) Run() error {
6257
s.Register(&tasks.CleanupTask{})
6358

6459
// 确保临时目录在函数结束时被清理
65-
defer os.RemoveAll(a.cfg.TmpDir)
60+
defer func() {
61+
slog.Debug("🧽 清理临时文件", "tmpDir", a.cfg.TmpDir)
62+
os.RemoveAll(a.cfg.TmpDir)
63+
}()
6664

6765
if err := s.Start(); err != nil {
6866
slog.Error("🚨 备份失败", "error", err)
6967
return err
7068
}
69+
70+
duration := time.Since(startTime)
71+
slog.Info("✅ 备份完成", "duration", duration)
7172
return nil
7273
}

internal/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func Load() (*Config, error) {
4747

4848
backupDir := getEnv("BACKUP_DIR", "/backups")
4949
dataDir := getEnv("DATA_DIR", "/data")
50-
tmpDir := filepath.Join(backupDir, "tmp")
50+
tmpDir := filepath.Join(backupDir, "/.tmp")
5151

5252
cfg := &Config{
5353
BackupDir: backupDir,

internal/scheduler/scheduler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func handleTask(t Task, cfg *config.Config) error {
5555
slog.Error("❌ 任务失败", "task", t.Name(), "error", err)
5656
return err
5757
}
58-
slog.Info("✅ 任务完成", "task", t.Name(), "duration", duration)
58+
slog.Debug("✅ 任务完成", "task", t.Name(), "duration", duration)
5959
return nil
6060
}
6161

internal/tasks/archive.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,20 @@ func (c *ArchiveTask) Run(cfg *config.Config) error {
3636
return fmt.Errorf("创建加密归档失败: %w", err)
3737
}
3838

39-
// 验证归档完整性 - 解密到单独的验证目录
40-
verifyDir := filepath.Join(cfg.BackupDir, "verify_tmp")
39+
// 验证归档完整性
40+
slog.Debug("🔎 开始验证归档完整性")
41+
42+
// 计算原始备份目录的哈希值
43+
slog.Debug("🧮 计算原始目录哈希", "dir", cfg.TmpDir)
44+
sourceHash, err := utils.HashDir(cfg.TmpDir)
45+
if err != nil {
46+
utils.RemoveIfExists(archiveFile)
47+
return fmt.Errorf("计算原始目录哈希失败: %w", err)
48+
}
49+
slog.Debug("✨ 原始目录哈希", "hash", sourceHash)
50+
51+
// 解密到单独的验证目录
52+
verifyDir := filepath.Join(cfg.BackupDir, "/.verify_tmp")
4153
defer utils.RemoveIfExists(verifyDir) // 确保验证目录被清理
4254

4355
if err := utils.EnsureDir(verifyDir); err != nil {
@@ -47,7 +59,22 @@ func (c *ArchiveTask) Run(cfg *config.Config) error {
4759

4860
if err := archive.DecryptBackup(archiveFile, cfg.Password, verifyDir); err != nil {
4961
utils.RemoveIfExists(archiveFile)
50-
return fmt.Errorf("归档验证失败: %w", err)
62+
return fmt.Errorf("解密归档失败: %w", err)
63+
}
64+
65+
// 计算验证目录的哈希值
66+
slog.Debug("🧮 计算验证目录哈希", "dir", verifyDir)
67+
verifyHash, err := utils.HashDir(verifyDir)
68+
if err != nil {
69+
utils.RemoveIfExists(archiveFile)
70+
return fmt.Errorf("计算验证目录哈希失败: %w", err)
71+
}
72+
slog.Debug("✨ 验证目录哈希", "hash", verifyHash)
73+
74+
// 比较哈希值
75+
if sourceHash != verifyHash {
76+
utils.RemoveIfExists(archiveFile)
77+
return fmt.Errorf("归档完整性验证失败: 哈希值不匹配")
5178
}
5279

5380
slog.Debug("✅ 归档验证成功", "file", filepath.Base(archiveFile))

internal/tasks/rsa.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tasks
22

33
import (
44
"fmt"
5+
"log/slog"
56
"path/filepath"
67

78
"github.com/xg4/vaultwarden-backup/internal/config"
@@ -27,6 +28,7 @@ func (RSATask) Run(cfg *config.Config) error {
2728

2829
// 逐个复制密钥文件
2930
for _, file := range matches {
31+
slog.Debug("✨ 找到 rsa_key* 文件", "file", filepath.Base(file))
3032
destFile := filepath.Join(cfg.TmpDir, filepath.Base(file))
3133
if err := utils.CopyFile(file, destFile); err != nil {
3234
return fmt.Errorf("🔒 备份RSA密钥 %s 失败: %w", file, err)

internal/utils/fs.go

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,77 @@
11
package utils
22

33
import (
4+
"crypto/sha256"
5+
"fmt"
46
"io"
57
"os"
68
"path/filepath"
9+
"sort"
710
)
811

12+
// EnsureDir 确保目录存在,如果不存在则创建
13+
func EnsureDir(path string) error {
14+
_, err := os.Stat(path)
15+
if os.IsNotExist(err) {
16+
return os.MkdirAll(path, 0755)
17+
}
18+
return err
19+
}
20+
21+
// RemoveIfExists 如果文件或目录存在,则删除
22+
func RemoveIfExists(path string) error {
23+
_, err := os.Stat(path)
24+
if os.IsNotExist(err) {
25+
return nil
26+
}
27+
return os.RemoveAll(path)
28+
}
29+
30+
// HashDir 计算目录中所有文件内容的 SHA256 哈希值
31+
func HashDir(dir string) (string, error) {
32+
h := sha256.New()
33+
var paths []string
34+
35+
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
36+
if err != nil {
37+
return err
38+
}
39+
if !info.IsDir() {
40+
relPath, err := filepath.Rel(dir, path)
41+
if err != nil {
42+
return err
43+
}
44+
paths = append(paths, relPath)
45+
}
46+
return nil
47+
})
48+
if err != nil {
49+
return "", err
50+
}
51+
52+
sort.Strings(paths)
53+
54+
for _, relPath := range paths {
55+
// 将文件名写入哈希
56+
if _, err := h.Write([]byte(relPath)); err != nil {
57+
return "", err
58+
}
59+
60+
// 将文件内容写入哈希
61+
f, err := os.Open(filepath.Join(dir, relPath))
62+
if err != nil {
63+
return "", err
64+
}
65+
defer f.Close()
66+
67+
if _, err := io.Copy(h, f); err != nil {
68+
return "", err
69+
}
70+
}
71+
72+
return fmt.Sprintf("%x", h.Sum(nil)), nil
73+
}
74+
975
func CopyFile(src, dst string) error {
1076
source, err := os.Open(src)
1177
if err != nil {
@@ -40,19 +106,3 @@ func CopyDir(src, dst string) error {
40106
return CopyFile(path, dstPath)
41107
})
42108
}
43-
44-
func FileExists(path string) bool {
45-
_, err := os.Stat(path)
46-
return !os.IsNotExist(err)
47-
}
48-
49-
func EnsureDir(dir string) error {
50-
return os.MkdirAll(dir, 0755)
51-
}
52-
53-
func RemoveIfExists(path string) error {
54-
if FileExists(path) {
55-
return os.RemoveAll(path)
56-
}
57-
return nil
58-
}

0 commit comments

Comments
 (0)