mirror of
https://github.com/pocketbase/pocketbase.git
synced 2026-09-08 23:50:47 +02:00
138 lines
2.9 KiB
Go
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
|
|
})
|
|
}
|