chore: update backup

This commit is contained in:
henry.chen
2025-07-17 10:52:43 +08:00
parent a0b41d08bd
commit be0280ac56
7 changed files with 189 additions and 153 deletions

View File

@@ -35,11 +35,11 @@ func init() {
logrus.Infof("Run mode:%s", mode)
// 加载配置文件
dir, err := config.WalkWorkDir()
etc, err := config.WorkEtcPath()
if err != nil {
panic(err)
}
path := filepath.Join(dir, "etc", "app.yml")
path := filepath.Join(etc, "app.yml")
data, err := os.ReadFile(path)
if err != nil {

View File

@@ -0,0 +1,7 @@
package db
// Storage 备份恢复器
type Storage interface {
Backup(name string) (string, error)
Restore(path string) error
}

View File

@@ -0,0 +1,72 @@
package db
import (
"context"
"fmt"
"net/url"
"os/exec"
"time"
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/pkg/connector/db"
)
// MongoStorage 备份恢复器
type MongoStorage struct{}
// Backup 备份
func (r MongoStorage) Backup(name string) (string, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
// dump
u, err := url.Parse(config.Conf.Database.Source)
if err != nil {
return "", err
}
arg := fmt.Sprintf("mongodump -h %s -d eiblog -o /tmp", u.Host)
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return "", err
}
// tar
arg = fmt.Sprintf("tar czf /tmp/%s -C /tmp eiblog", name)
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return "", err
}
return "/tmp/" + name, nil
}
// Restore 恢复
func (r MongoStorage) Restore(path string) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
defer cancel()
// drop database
mdb, err := db.NewMDB(config.Conf.Database)
if err != nil {
return err
}
err = mdb.Drop(ctx)
if err != nil {
return err
}
// unarchive
arg := fmt.Sprintf("tar xzf %s -C /tmp", path)
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return err
}
// restore
u, err := url.Parse(config.Conf.Database.Source)
if err != nil {
return err
}
arg = fmt.Sprintf("mongorestore -h %s -d eiblog /tmp/eiblog", u.Host)
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
return cmd.Run()
}

View File

@@ -1,138 +0,0 @@
package qiniu
import (
"context"
"errors"
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/cmd/backup/handler/internal"
"github.com/eiblog/eiblog/pkg/connector/db"
"github.com/eiblog/eiblog/pkg/third/qiniu"
)
// BackupRestorer qiniu backup restorer
type BackupRestorer struct{}
// Backup implements timer.BackupRestorer
func (s BackupRestorer) Backup(now time.Time) error {
switch config.Conf.Database.Driver {
case "mongodb":
return backupFromMongoDB(now)
default:
return errors.New("unsupported source backup to qiniu: " +
config.Conf.Database.Driver)
}
}
// Restore implements timer.BackupRestorer
func (s BackupRestorer) Restore() error {
switch config.Conf.Database.Driver {
case "mongodb":
return restoreToMongoDB()
default:
return errors.New("unsupported source restore from qiniu: " +
config.Conf.Database.Driver)
}
}
func backupFromMongoDB(now time.Time) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
defer cancel()
// dump
u, err := url.Parse(config.Conf.Database.Source)
if err != nil {
return err
}
arg := fmt.Sprintf("mongodump -h %s -d eiblog -o /tmp", u.Host)
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return err
}
// tar
name := fmt.Sprintf("eiblog-%s.tar.gz", now.Format("2006-01-02"))
arg = fmt.Sprintf("tar czf /tmp/%s -C /tmp eiblog", name)
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return err
}
// upload file
f, err := os.Open("/tmp/" + name)
if err != nil {
return err
}
s, err := f.Stat()
if err != nil {
return err
}
uploadParams := qiniu.UploadParams{
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
Size: s.Size(),
Data: f,
NoCompletePath: true,
}
_, err = internal.QiniuClient.Upload(uploadParams)
if err != nil {
return err
}
// after days delete
deleteParams := qiniu.DeleteParams{
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
Days: config.Conf.Validity,
NoCompletePath: true,
}
return internal.QiniuClient.Delete(deleteParams)
}
func restoreToMongoDB() error {
// backup file
params := qiniu.ContentParams{
Prefix: "blog/",
}
raw, err := internal.QiniuClient.Content(params)
if err != nil {
return err
}
f, err := os.OpenFile("/tmp/eiblog.tar.gz", os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return err
}
_, _ = f.Write(raw)
defer f.Close()
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*20)
defer cancel()
// drop database
mdb, err := db.NewMDB(config.Conf.Database)
if err != nil {
return err
}
err = mdb.Drop(ctx)
if err != nil {
return err
}
// unarchive
arg := "tar xzf /tmp/eiblog.tar.gz -C /tmp"
cmd := exec.CommandContext(ctx, "sh", "-c", arg)
err = cmd.Run()
if err != nil {
return err
}
// restore
u, err := url.Parse(config.Conf.Database.Source)
if err != nil {
return err
}
arg = fmt.Sprintf("mongorestore -h %s -d eiblog /tmp/eiblog", u.Host)
cmd = exec.CommandContext(ctx, "sh", "-c", arg)
return cmd.Run()
}

View File

@@ -2,49 +2,71 @@ package timer
import (
"errors"
"fmt"
"strconv"
"time"
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/cmd/backup/handler/timer/qiniu"
"github.com/eiblog/eiblog/cmd/backup/handler/timer/db"
"github.com/eiblog/eiblog/cmd/backup/handler/timer/to"
"github.com/sirupsen/logrus"
)
// BackupRestorer 备份恢复器
type BackupRestorer interface {
Backup(now time.Time) error
Restore() error
}
// Start to backup with ticker
func Start(restore bool) (err error) {
var storage BackupRestorer
var (
storage db.Storage
backupTo to.BackupRestorer
)
// backup instance
// backup from
switch config.Conf.Database.Driver {
case "mongodb":
storage = db.MongoStorage{}
default:
return errors.New("timer: unknown backup from driver: " +
config.Conf.Database.Driver)
}
// backup to
switch config.Conf.BackupTo {
case "qiniu":
storage = qiniu.BackupRestorer{}
backupTo = to.QiniuBackupRestorer{}
default:
return errors.New("timer: unknown backup to driver: " +
config.Conf.BackupTo)
}
// restore
if restore {
err = storage.Restore()
path, err := backupTo.Download()
if err != nil {
return err
}
err = storage.Restore(path)
if err != nil {
return err
}
logrus.Info("timer: Restore success")
}
// parse duration
// backup
interval, err := ParseDuration(config.Conf.Interval)
if err != nil {
return err
}
t := time.NewTicker(interval)
for now := range t.C {
err = storage.Backup(now)
name := fmt.Sprintf("eiblog-%s.tar.gz", now.Format("2006-01-02"))
path, err := storage.Backup(name)
if err != nil {
logrus.Error("timer: Start.Backup: ", now.Format(time.RFC3339), err)
continue
}
err = backupTo.Upload(path)
if err != nil {
logrus.Error("timer: Start.Backup: ", now.Format(time.RFC3339), err)
}

View File

@@ -0,0 +1,66 @@
package to
import (
"os"
"path/filepath"
"github.com/eiblog/eiblog/cmd/backup/config"
"github.com/eiblog/eiblog/cmd/backup/handler/internal"
"github.com/eiblog/eiblog/pkg/third/qiniu"
)
// QiniuBackupRestorer qiniu backup restorer
type QiniuBackupRestorer struct{}
// Upload implements timer.BackupRestorer
func (s QiniuBackupRestorer) Upload(path string) error {
name := filepath.Base(path)
// upload file
f, err := os.Open(path)
if err != nil {
return err
}
fi, err := f.Stat()
if err != nil {
return err
}
uploadParams := qiniu.UploadParams{
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
Size: fi.Size(),
Data: f,
NoCompletePath: true,
}
_, err = internal.QiniuClient.Upload(uploadParams)
if err != nil {
return err
}
// after days delete
deleteParams := qiniu.DeleteParams{
Name: filepath.Join("blog", name), // blog/eiblog-xx.tar.gz
Days: config.Conf.Validity,
NoCompletePath: true,
}
return internal.QiniuClient.Delete(deleteParams)
}
// Download implements timer.BackupRestorer
func (s QiniuBackupRestorer) Download() (string, error) {
// backup file
params := qiniu.ContentParams{
Prefix: "blog/",
}
raw, err := internal.QiniuClient.Content(params)
if err != nil {
return "", err
}
path := filepath.Join("/tmp", "eiblog.tar.gz")
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return "", err
}
_, _ = f.Write(raw)
defer f.Close()
return path, nil
}

View File

@@ -0,0 +1,7 @@
package to
// BackupRestorer 备份存储接口
type BackupRestorer interface {
Upload(path string) error
Download() (path string, err error)
}