Backups: Rename "backup-index" config option to "backup-database" #4243

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2024-05-14 11:11:50 +02:00
parent 9527082a03
commit d5580c116a
13 changed files with 150 additions and 143 deletions

View File

@@ -36,7 +36,7 @@ export class ConfigOptions extends Model {
OriginalsLimit: 0,
Workers: 0,
WakeupInterval: 0,
BackupIndex: false,
BackupDatabase: false,
DisableWebDAV: config.values.disable.webdav,
DisableSettings: config.values.disable.settings,
DisablePlaces: config.values.disable.places,

View File

@@ -37,9 +37,9 @@
<v-flex xs12 sm6 lg3 class="px-2 pb-2 pt-2">
<v-checkbox
v-model="settings.BackupIndex"
v-model="settings.BackupDatabase"
:disabled="busy"
class="ma-0 pa-0 input-backup-index"
class="ma-0 pa-0 input-backup-database"
color="secondary-dark"
:label="$gettext('Database Backups')"
:hint="$gettext('Create index backups based on the configured schedule.')"

View File

@@ -14,16 +14,16 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
)
const backupDescription = "A user-defined filename or - for stdout can be passed as the first argument. " +
"The -i parameter can be omitted in this case.\n" +
" Make sure to run the command with exec -T when using Docker to prevent log messages from being sent to stdout.\n" +
" The index backup and album file paths are automatically detected if not specified explicitly."
const backupDescription = `A custom filename for the database backup (or - to send the backup to stdout) can optionally be passed as argument.
The --database flag can be omitted in this case. When using Docker, please run the docker command with the -T flag
to prevent log messages from being sent to stdout. If nothing else is specified, the database and album backup paths
will be automatically determined based on the current configuration.`
// BackupCommand configures the command name, flags, and action.
var BackupCommand = cli.Command{
Name: "backup",
Description: backupDescription,
Usage: "Creates an index database dump and/or album YAML file backups",
Usage: "Creates an index database backup and/or album YAML backup files",
ArgsUsage: "[filename]",
Flags: backupFlags,
Action: backupAction,
@@ -32,27 +32,27 @@ var BackupCommand = cli.Command{
var backupFlags = []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "replace existing index backup files",
Usage: "replace the index database backup file, if it exists",
},
cli.BoolFlag{
Name: "albums, a",
Usage: "export album metadata to YAML files located in the backup path",
Usage: "create or update album YAML backup files in the album backup path",
},
cli.StringFlag{
Name: "albums-path",
Usage: "custom album backup `PATH`",
},
cli.BoolFlag{
Name: "index, i",
Usage: "create index database backup (sent to stdout if - is passed as first argument)",
Name: "database, index, i",
Usage: "create a database backup in the database backup path with the date as filename, or write it to the specified file (stdout if - is passed as filename)",
},
cli.StringFlag{
Name: "index-path",
Usage: "custom index backup `PATH`",
Name: "database-path, index-path",
Usage: "custom database backup `PATH`",
},
cli.IntFlag{
Name: "retain, r",
Usage: "`NUMBER` of index backups to keep (-1 to keep all)",
Usage: "`NUMBER` of database backups to keep (-1 to keep all)",
Value: config.DefaultBackupRetain,
},
}
@@ -61,14 +61,14 @@ var backupFlags = []cli.Flag{
func backupAction(ctx *cli.Context) error {
// Use command argument as backup file name.
fileName := ctx.Args().First()
backupPath := ctx.String("index-path")
backupIndex := ctx.Bool("index") || fileName != "" || backupPath != ""
databasePath := ctx.String("database-path")
backupDatabase := ctx.Bool("database") || fileName != "" || databasePath != ""
albumsPath := ctx.String("albums-path")
backupAlbums := ctx.Bool("albums") || albumsPath != ""
force := ctx.Bool("force")
retain := ctx.Int("retain")
if !backupIndex && !backupAlbums {
if !backupDatabase && !backupAlbums {
return cli.ShowSubcommandHelp(ctx)
}
@@ -86,30 +86,30 @@ func backupAction(ctx *cli.Context) error {
conf.RegisterDb()
defer conf.Shutdown()
if backupIndex {
// If empty, use default backup file name.
if backupDatabase {
// Use default if no explicit filename was provided.
if fileName == "" {
if !fs.PathWritable(backupPath) {
if backupPath != "" {
log.Warnf("custom index backup path not writable, using default")
if !fs.PathWritable(databasePath) {
if databasePath != "" {
log.Warnf("backup: specified database backup path not writable, using default backup path")
}
backupPath = conf.BackupIndexPath()
databasePath = conf.BackupDatabasePath()
}
backupFile := time.Now().UTC().Format("2006-01-02") + ".sql"
fileName = filepath.Join(backupPath, backupFile)
fileName = filepath.Join(databasePath, backupFile)
}
if err = photoprism.BackupIndex(backupPath, fileName, fileName == "-", force, retain); err != nil {
return fmt.Errorf("failed to create index backup: %w", err)
if err = photoprism.BackupDatabase(databasePath, fileName, fileName == "-", force, retain); err != nil {
return fmt.Errorf("failed to create database backup: %w", err)
}
}
if backupAlbums {
if !fs.PathWritable(albumsPath) {
if albumsPath != "" {
log.Warnf("album files path not writable, using default")
log.Warnf("backup: specified album backup path not writable, using default backup path")
}
albumsPath = conf.BackupAlbumsPath()
@@ -118,7 +118,7 @@ func backupAction(ctx *cli.Context) error {
if count, backupErr := photoprism.BackupAlbums(albumsPath, true); backupErr != nil {
return backupErr
} else {
log.Infof("exported %s", english.Plural(count, "album", "albums"))
log.Infof("backup: saved %s", english.Plural(count, "album backup", "album backups"))
}
}

View File

@@ -13,15 +13,15 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
)
const restoreDescription = "A user-defined filename or - for stdin can be passed as the first argument. " +
"The -i parameter can be omitted in this case.\n" +
" The index backup and album file paths are automatically detected if not specified explicitly."
const restoreDescription = `A custom filename for the database backup (or - to read the backup from stdin) can optionally be passed as argument.
The --database flag can be omitted in this case. If nothing else is specified, the database and album backup paths
will be automatically determined based on the current configuration.`
// RestoreCommand configures the command name, flags, and action.
var RestoreCommand = cli.Command{
Name: "restore",
Description: restoreDescription,
Usage: "Restores the index from a database dump and/or album YAML file backups",
Usage: "Restores the index database and/or album metadata from a backup",
ArgsUsage: "[filename]",
Flags: restoreFlags,
Action: restoreAction,
@@ -30,37 +30,37 @@ var RestoreCommand = cli.Command{
var restoreFlags = []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "replace existing index schema and data",
Usage: "replace the index database with the backup, if it already exists",
},
cli.BoolFlag{
Name: "albums, a",
Usage: "restore albums from YAML files located in the backup path",
Usage: "restore albums from the YAML backup files found in the album backup path",
},
cli.StringFlag{
Name: "albums-path",
Usage: "custom album backup `PATH`",
},
cli.BoolFlag{
Name: "index, i",
Usage: "restore index from the specified file or the most recent file in the backup path (from stdin if - is passed as first argument)",
Name: "database, index, i",
Usage: "restore the index database from the specified file (stdin if - is passed as filename), or the most recent backup found in the database backup path",
},
cli.StringFlag{
Name: "index-path",
Usage: "custom index backup `PATH`",
Name: "database-path, index-path",
Usage: "custom database backup `PATH`",
},
}
// restoreAction restores a database backup.
func restoreAction(ctx *cli.Context) error {
// Use command argument as backup file name.
indexFileName := ctx.Args().First()
indexPath := ctx.String("index-path")
restoreIndex := ctx.Bool("index") || indexFileName != "" || indexPath != ""
databaseFile := ctx.Args().First()
databasePath := ctx.String("database-path")
restoreDatabase := ctx.Bool("database") || databaseFile != "" || databasePath != ""
force := ctx.Bool("force")
albumsPath := ctx.String("albums-path")
restoreAlbums := ctx.Bool("albums") || albumsPath != ""
if !restoreIndex && !restoreAlbums {
if !restoreDatabase && !restoreAlbums {
return cli.ShowSubcommandHelp(ctx)
}
@@ -78,17 +78,18 @@ func restoreAction(ctx *cli.Context) error {
conf.RegisterDb()
defer conf.Shutdown()
// Restore index from specified file?
if !restoreIndex {
// Restore database from backup dump?
if !restoreDatabase {
// Do nothing.
} else if err = photoprism.RestoreIndex(indexPath, indexFileName, indexFileName == "-", force); err != nil {
} else if err = photoprism.RestoreDatabase(databasePath, databaseFile, databaseFile == "-", force); err != nil {
return err
}
log.Infoln("migrating index database schema")
log.Infoln("restore: migrating index database schema")
conf.InitDb()
// Restore albums from YAML backup files?
if restoreAlbums {
get.SetConfig(conf)
@@ -97,21 +98,21 @@ func restoreAction(ctx *cli.Context) error {
}
if !fs.PathExists(albumsPath) {
log.Warnf("album files path %s not found", clean.Log(albumsPath))
log.Warnf("restore: album files path %s not found", clean.Log(albumsPath))
} else {
log.Infof("restoring albums from %s", clean.Log(albumsPath))
log.Infof("restore: restoring albums from %s", clean.Log(albumsPath))
if count, err := photoprism.RestoreAlbums(albumsPath, true); err != nil {
return err
} else {
log.Infof("restored %s from YAML files", english.Plural(count, "album", "albums"))
log.Infof("restore: restored %s from YAML files", english.Plural(count, "album", "albums"))
}
}
}
elapsed := time.Since(start)
log.Infof("restored in %s", elapsed)
log.Infof("completed in %s", elapsed)
return nil
}

View File

@@ -32,24 +32,6 @@ func (c *Config) BackupBasePath() string {
return filepath.Join(c.StoragePath(), "backup")
}
// BackupAlbumsPath returns the backup path for album YAML files.
func (c *Config) BackupAlbumsPath() string {
if dir := filepath.Join(c.StoragePath(), "albums"); fs.PathExists(dir) {
return dir
}
return c.BackupPath("albums")
}
// BackupIndexPath returns the backup path for index database dumps.
func (c *Config) BackupIndexPath() string {
if driver := c.DatabaseDriver(); driver != "" {
return c.BackupPath(driver)
}
return c.BackupPath("index")
}
// BackupSchedule returns the backup schedule in cron format, e.g. "0 12 * * *" for daily at noon.
func (c *Config) BackupSchedule() string {
if c.options.BackupSchedule == "" {
@@ -73,9 +55,18 @@ func (c *Config) BackupRetain() int {
return c.options.BackupRetain
}
// BackupIndex checks if SQL database dumps should be created based on the configured schedule.
func (c *Config) BackupIndex() bool {
return c.options.BackupIndex
// BackupDatabase checks if index database backups should be created based on the configured schedule.
func (c *Config) BackupDatabase() bool {
return c.options.BackupDatabase
}
// BackupDatabasePath returns the backup path for index database dumps.
func (c *Config) BackupDatabasePath() string {
if driver := c.DatabaseDriver(); driver != "" {
return c.BackupPath(driver)
}
return c.BackupPath("index")
}
// BackupAlbums checks if album YAML file backups should be created based on the configured schedule.
@@ -83,6 +74,15 @@ func (c *Config) BackupAlbums() bool {
return c.options.BackupAlbums
}
// BackupAlbumsPath returns the backup path for album YAML files.
func (c *Config) BackupAlbumsPath() string {
if dir := filepath.Join(c.StoragePath(), "albums"); fs.PathExists(dir) {
return dir
}
return c.BackupPath("albums")
}
// DisableBackups checks if creating and updating sidecar YAML files should be disabled.
func (c *Config) DisableBackups() bool {
if !c.SidecarWritable() {

View File

@@ -16,16 +16,6 @@ func TestConfig_BackupBasePath(t *testing.T) {
assert.Contains(t, c.BackupBasePath(), "/storage/testdata/backup")
}
func TestConfig_BackupAlbumsPath(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Contains(t, c.BackupAlbumsPath(), "/albums")
}
func TestConfig_BackupIndexPath(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Contains(t, c.BackupIndexPath(), "/storage/testdata/backup/sqlite")
}
func TestConfig_BackupSchedule(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Equal(t, DefaultBackupSchedule, c.BackupSchedule())
@@ -36,13 +26,18 @@ func TestConfig_BackupRetain(t *testing.T) {
assert.Equal(t, DefaultBackupRetain, c.BackupRetain())
}
func TestConfig_BackupIndex(t *testing.T) {
func TestConfig_BackupDatabase(t *testing.T) {
c := NewConfig(CliTestContext())
assert.True(t, c.BackupIndex())
c.options.BackupIndex = false
assert.False(t, c.BackupIndex())
c.options.BackupIndex = true
assert.True(t, c.BackupIndex())
assert.True(t, c.BackupDatabase())
c.options.BackupDatabase = false
assert.False(t, c.BackupDatabase())
c.options.BackupDatabase = true
assert.True(t, c.BackupDatabase())
}
func TestConfig_BackupDatabasePath(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Contains(t, c.BackupDatabasePath(), "/storage/testdata/backup/sqlite")
}
func TestConfig_BackupAlbums(t *testing.T) {
@@ -55,6 +50,11 @@ func TestConfig_BackupAlbums(t *testing.T) {
}
func TestConfig_BackupAlbumsPath(t *testing.T) {
c := NewConfig(CliTestContext())
assert.Contains(t, c.BackupAlbumsPath(), "/albums")
}
func TestConfig_DisableBackups(t *testing.T) {
c := NewConfig(CliTestContext())
assert.False(t, c.DisableBackups())

View File

@@ -158,7 +158,7 @@ var Flags = CliFlags{
}}, {
Flag: cli.BoolFlag{
Name: "sidecar-yaml",
Usage: "export picture metadata to YAML sidecar files",
Usage: "save picture metadata to YAML sidecar files",
EnvVar: EnvVar("SIDECAR_YAML"),
}, DocDefault: "true"}, {
Flag: cli.StringFlag{
@@ -204,13 +204,13 @@ var Flags = CliFlags{
EnvVar: EnvVar("BACKUP_RETAIN"),
}}, {
Flag: cli.BoolFlag{
Name: "backup-index",
Usage: "create index database backups based on the configured schedule",
EnvVar: EnvVar("BACKUP_INDEX"),
Name: "backup-database",
Usage: "create index backups based on the configured schedule",
EnvVar: EnvVar("BACKUP_DATABASE"),
}, DocDefault: "true"}, {
Flag: cli.BoolFlag{
Name: "backup-albums",
Usage: "export album metadata to YAML backup files",
Usage: "save album metadata to YAML backup files",
EnvVar: EnvVar("BACKUP_ALBUMS"),
}, DocDefault: "true"}, {
Flag: cli.IntFlag{

View File

@@ -60,7 +60,7 @@ type Options struct {
BackupPath string `yaml:"BackupPath" json:"-" flag:"backup-path"`
BackupSchedule string `yaml:"BackupSchedule" json:"BackupSchedule" flag:"backup-schedule"`
BackupRetain int `yaml:"BackupRetain" json:"BackupRetain" flag:"backup-retain"`
BackupIndex bool `yaml:"BackupIndex" json:"BackupIndex" flag:"backup-index" default:"true"`
BackupDatabase bool `yaml:"BackupDatabase" json:"BackupDatabase" flag:"backup-database" default:"true"`
BackupAlbums bool `yaml:"BackupAlbums" json:"BackupAlbums" flag:"backup-albums" default:"true"`
IndexWorkers int `yaml:"IndexWorkers" json:"IndexWorkers" flag:"index-workers"`
IndexSchedule string `yaml:"IndexSchedule" json:"IndexSchedule" flag:"index-schedule"`
@@ -216,7 +216,7 @@ func NewOptions(ctx *cli.Context) *Options {
// Enable database backups and YAML exports by default.
c.SidecarYaml = true
c.BackupIndex = true
c.BackupDatabase = true
c.BackupAlbums = true
// Load defaults from YAML file?

View File

@@ -81,8 +81,8 @@ func (c *Config) Report() (rows [][]string, cols []string) {
{"backup-path", c.BackupBasePath()},
{"backup-schedule", c.BackupSchedule()},
{"backup-retain", fmt.Sprintf("%d", c.BackupRetain())},
{"backup-index", fmt.Sprintf("%t", c.BackupIndex())},
{"backup-index-path", c.BackupIndexPath()},
{"backup-database", fmt.Sprintf("%t", c.BackupDatabase())},
{"backup-database-path", c.BackupDatabasePath()},
{"backup-albums", fmt.Sprintf("%t", c.BackupAlbums())},
{"backup-albums-path", c.BackupAlbumsPath()},

View File

@@ -28,8 +28,8 @@ func BackupAlbums(backupPath string, force bool) (count int, err error) {
backupPath = Config().BackupAlbumsPath()
}
log.Infof("exporting album metadata to YAML backup files")
log.Debugf("album backups will be created in %s", clean.Log(backupPath))
log.Debugf("backup: album backups will be stored in %s", clean.Log(backupPath))
log.Infof("backup: saving album metadata in YAML backup files")
var latest time.Time

View File

@@ -20,19 +20,23 @@ import (
"github.com/photoprism/photoprism/pkg/fs"
)
var backupIndexMutex = sync.Mutex{}
var backupDatabaseMutex = sync.Mutex{}
// BackupIndex creates an SQL backup dump with the specified file and path name.
func BackupIndex(backupPath, fileName string, toStdOut, force bool, retain int) (err error) {
// Make sure only one backup/restore operation is running at a time.
backupIndexMutex.Lock()
defer backupIndexMutex.Unlock()
// BackupDatabase creates a database backup dump with the specified file and path name.
func BackupDatabase(backupPath, fileName string, toStdOut, force bool, retain int) (err error) {
// Ensure that only one database backup/restore operation is running at a time.
backupDatabaseMutex.Lock()
defer backupDatabaseMutex.Unlock()
// Backup action shown in logs.
backupAction := "creating"
// Get configuration.
c := Config()
if !toStdOut {
if backupPath == "" {
backupPath = c.BackupIndexPath()
backupPath = c.BackupDatabasePath()
}
// Create the backup path if it does not already exist.
@@ -50,10 +54,12 @@ func BackupIndex(backupPath, fileName string, toStdOut, force bool, retain int)
fileName = filepath.Join(backupPath, backupFile)
}
log.Debugf("backup: database backups will be stored in %s", clean.Log(backupPath))
if _, err = os.Stat(fileName); err == nil && !force {
return fmt.Errorf("%s already exists", clean.Log(filepath.Base(fileName)))
} else if err == nil {
log.Warnf("replacing existing index backup")
backupAction = "replacing"
}
// Create backup path if not exists.
@@ -79,7 +85,7 @@ func BackupIndex(backupPath, fileName string, toStdOut, force bool, retain int)
)
case config.SQLite3:
if !fs.FileExistsNotEmpty(c.DatabaseFile()) {
return fmt.Errorf("sqlite database %s not found", clean.LogQuote(c.DatabaseFile()))
return fmt.Errorf("sqlite database file %s not found", clean.LogQuote(c.DatabaseFile()))
}
cmd = exec.Command(
@@ -94,12 +100,12 @@ func BackupIndex(backupPath, fileName string, toStdOut, force bool, retain int)
// Write to stdout or file.
var f *os.File
if toStdOut {
log.Infof("writing index backup to stdout")
log.Infof("backup: sending database backup to stdout")
f = os.Stdout
} else if f, err = os.OpenFile(fileName, os.O_TRUNC|os.O_RDWR|os.O_CREATE, fs.ModeFile); err != nil {
return fmt.Errorf("failed to create %s: %s", clean.Log(fileName), err)
return fmt.Errorf("failed to create %s (%s)", clean.Log(fileName), err)
} else {
log.Infof("creating index backup in %s", clean.Log(filepath.Base(fileName)))
log.Infof("backup: %s database backup file %s", backupAction, clean.Log(filepath.Base(fileName)))
defer f.Close()
}
@@ -128,20 +134,20 @@ func BackupIndex(backupPath, fileName string, toStdOut, force bool, retain int)
}
if len(files) == 0 {
return fmt.Errorf("found no index backups files in %s", backupPath)
return fmt.Errorf("found no database backup files in %s", backupPath)
} else if len(files) <= retain {
return nil
}
sort.Strings(files)
log.Infof("retaining %s", english.Plural(retain, "index backup", "index backups"))
log.Infof("backup: retaining %s", english.Plural(retain, "database backup", "database backups"))
for i := 0; i < len(files)-retain; i++ {
if err = os.Remove(files[i]); err != nil {
return err
} else {
log.Infof("removed old backup file %s", clean.Log(filepath.Base(files[i])))
log.Infof("backup: removed database backup file %s", clean.Log(filepath.Base(files[i])))
}
}
}

View File

@@ -20,18 +20,18 @@ import (
const SqlBackupFileNamePattern = "[2-9][0-9][0-9][0-9]-[0-1][0-9]-[0-3][0-9].sql"
// RestoreIndex restores the index from an SQL backup dump with the specified file and path name.
func RestoreIndex(backupPath, fileName string, fromStdIn, force bool) (err error) {
// Make sure only one backup/restore operation is running at a time.
backupIndexMutex.Lock()
defer backupIndexMutex.Unlock()
// RestoreDatabase restores the database from a backup file with the specified path and name.
func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err error) {
// Ensure that only one database backup/restore operation is running at a time.
backupDatabaseMutex.Lock()
defer backupDatabaseMutex.Unlock()
c := Config()
// If empty, use default backup file name.
if !fromStdIn && fileName == "" {
if backupPath == "" {
backupPath = c.BackupIndexPath()
backupPath = c.BackupDatabasePath()
}
files, globErr := filepath.Glob(filepath.Join(regexp.QuoteMeta(backupPath), SqlBackupFileNamePattern))
@@ -41,7 +41,7 @@ func RestoreIndex(backupPath, fileName string, fromStdIn, force bool) (err error
}
if len(files) == 0 {
return fmt.Errorf("found no backups files in %s", backupPath)
return fmt.Errorf("found no database backup files in %s", backupPath)
}
sort.Strings(files)
@@ -49,7 +49,7 @@ func RestoreIndex(backupPath, fileName string, fromStdIn, force bool) (err error
fileName = files[len(files)-1]
if !fs.FileExistsNotEmpty(fileName) {
return fmt.Errorf("no backup found in %s", filepath.Base(fileName))
return fmt.Errorf("no database backup found in %s", filepath.Base(fileName))
}
}
@@ -62,9 +62,9 @@ func RestoreIndex(backupPath, fileName string, fromStdIn, force bool) (err error
if counts.Photos == 0 {
// Do nothing;
} else if !force {
return fmt.Errorf("found existing index with %d pictures, backup will not be restored", counts.Photos)
return fmt.Errorf("found an existing index with %d pictures, backup will not be restored", counts.Photos)
} else {
log.Warnf("replacing the existing index with %d pictures", counts.Photos)
log.Warnf("restore: existing index with %d pictures will be replaced", counts.Photos)
}
tables := entity.Entities
@@ -84,7 +84,7 @@ func RestoreIndex(backupPath, fileName string, fromStdIn, force bool) (err error
c.DatabaseName(),
)
case config.SQLite3:
log.Infoln("dropping existing tables")
log.Infoln("restore: dropping existing sqlite database tables")
tables.Drop(c.Db())
cmd = exec.Command(
c.SqliteBin(),
@@ -97,12 +97,12 @@ func RestoreIndex(backupPath, fileName string, fromStdIn, force bool) (err error
// Read from stdin or file.
var f *os.File
if fromStdIn {
log.Infof("restoring index from stdin")
log.Infof("restore: restoring database backup from stdin")
f = os.Stdin
} else if f, err = os.OpenFile(fileName, os.O_RDONLY, 0); err != nil {
return fmt.Errorf("failed to open %s: %s", clean.Log(fileName), err)
} else {
log.Infof("restoring index from %s", clean.Log(fileName))
log.Infof("restore: restoring database backup from %s", clean.Log(filepath.Base(fileName)))
defer f.Close()
}
@@ -128,7 +128,7 @@ func RestoreIndex(backupPath, fileName string, fromStdIn, force bool) (err error
// Run restore command.
if cmdErr := cmd.Run(); cmdErr != nil {
log.Errorf("failed to restore index")
log.Errorf("restore: failed to restore database backup")
if errStr := strings.TrimSpace(stderr.String()); errStr != "" {
return errors.New(errStr)

View File

@@ -27,13 +27,13 @@ func NewBackup(conf *config.Config) *Backup {
// StartScheduled starts a scheduled run of the backup worker based on the current configuration.
func (w *Backup) StartScheduled() {
if err := w.Start(w.conf.BackupIndex(), w.conf.BackupAlbums(), true, w.conf.BackupRetain()); err != nil {
if err := w.Start(w.conf.BackupDatabase(), w.conf.BackupAlbums(), true, w.conf.BackupRetain()); err != nil {
log.Errorf("scheduler: %s (backup)", err)
}
}
// Start creates index and album backups based on the current configuration.
func (w *Backup) Start(index, albums bool, force bool, retain int) (err error) {
func (w *Backup) Start(database, albums bool, force bool, retain int) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("backup: %s (worker panic)\nstack: %s", r, debug.Stack())
@@ -42,7 +42,7 @@ func (w *Backup) Start(index, albums bool, force bool, retain int) (err error) {
}()
// Return if no backups should be created.
if !index && !albums {
if !database && !albums {
return nil
}
@@ -56,12 +56,12 @@ func (w *Backup) Start(index, albums bool, force bool, retain int) (err error) {
// Start creating backups.
start := time.Now()
// Create index database backup.
if index {
backupPath := w.conf.BackupIndexPath()
// Create database backup.
if database {
databasePath := w.conf.BackupDatabasePath()
if err = photoprism.BackupIndex(backupPath, "", false, force, retain); err != nil {
log.Errorf("backup: %s (index)", err)
if err = photoprism.BackupDatabase(databasePath, "", false, force, retain); err != nil {
log.Errorf("backup: %s (database)", err)
}
}
@@ -69,23 +69,23 @@ func (w *Backup) Start(index, albums bool, force bool, retain int) (err error) {
return errors.New("canceled")
}
// Create album YAML file backup.
// Create albums backup.
if albums {
albumsBackupPath := w.conf.BackupAlbumsPath()
albumsPath := w.conf.BackupAlbumsPath()
if count, backupErr := photoprism.BackupAlbums(albumsBackupPath, false); backupErr != nil {
log.Errorf("backup: %s (album)", backupErr.Error())
if count, backupErr := photoprism.BackupAlbums(albumsPath, false); backupErr != nil {
log.Errorf("backup: %s (albums)", backupErr.Error())
} else if count > 0 {
log.Infof("exported %s", english.Plural(count, "album", "albums"))
log.Infof("backup: saved %s", english.Plural(count, "album backup", "album backups"))
}
}
// Update time when worker was last executed.
// Remember time when worker was executed.
w.lastRun = entity.TimeStamp()
elapsed := time.Since(start)
// Show success message.
// Log success message.
log.Infof("backup: completed in %s", elapsed)
return nil