Files
pocketbase/core/backup.go
T

138 lines
2.9 KiB
Go

package core
import (
"context"
"fmt"
"log/slog"
"sort"
"time"
"github.com/pocketbase/pocketbase/tools/inflector"
)
const (
StoreKeyActiveBackup = "@activeBackup"
)
// generateBackupName generates a new backup name based on the app name and current date.
func generateBackupName(app App, prefix string) string {
appName := inflector.Snakecase(app.Settings().Meta.AppName)
if len(appName) > 50 {
appName = appName[:50]
}
return fmt.Sprintf(
"%s%s_%s.zip",
prefix,
appName,
time.Now().UTC().Format("20060102150405"),
)
}
// registerAutobackupHooks registers the autobackup app serve hooks.
func (app *BaseApp) registerAutobackupHooks() {
const jobId = "__pbAutoBackup__"
loadJob := func() {
rawSchedule := app.Settings().Backups.Cron
if rawSchedule == "" {
app.Cron().Remove(jobId)
return
}
app.Cron().Add(jobId, rawSchedule, func() {
const autoPrefix = "@auto_pb_backup_"
name := generateBackupName(app, autoPrefix)
if err := app.CreateBackup(context.Background(), name); err != nil {
app.Logger().Error(
"[Backup cron] Failed to create backup",
slog.String("name", name),
slog.String("error", err.Error()),
)
alertError := sendSystemAlertToAllSuperusers(
app,
"Autobackup failure",
"Failed to create/upload automated backup. Raw error:\n"+err.Error(),
)
if alertError != nil {
app.Logger().Warn(
"[Backup cron] Failed to send backup error alerts",
slog.String("name", name),
slog.String("error", alertError.Error()),
)
}
}
maxKeep := app.Settings().Backups.CronMaxKeep
if maxKeep == 0 {
return // no explicit limit
}
fsys, err := app.NewBackupsFilesystem()
if err != nil {
app.Logger().Error(
"[Backup cron] Failed to initialize the backup filesystem",
slog.String("error", err.Error()),
)
return
}
defer fsys.Close()
files, err := fsys.List(autoPrefix)
if err != nil {
app.Logger().Error(
"[Backup cron] Failed to list autogenerated backups",
slog.String("error", err.Error()),
)
return
}
if maxKeep >= len(files) {
return // nothing to remove
}
// sort desc
sort.Slice(files, func(i, j int) bool {
return files[i].ModTime.After(files[j].ModTime)
})
// keep only the most recent n auto backup files
toRemove := files[maxKeep:]
for _, f := range toRemove {
if err := fsys.Delete(f.Key); err != nil {
app.Logger().Error(
"[Backup cron] Failed to remove old autogenerated backup",
slog.String("key", f.Key),
slog.String("error", err.Error()),
)
}
}
})
}
app.OnBootstrap().BindFunc(func(e *BootstrapEvent) error {
if err := e.Next(); err != nil {
return err
}
loadJob()
return nil
})
app.OnSettingsReload().BindFunc(func(e *SettingsReloadEvent) error {
if err := e.Next(); err != nil {
return err
}
loadJob()
return nil
})
}