Files
photoprism/internal/commands/backup.go
Michael Mayer 424ed84f2a Backups: Add worker to create backups in the background #4237 #4243
In the next step, this worker can be configured to automatically create
index and/or album backups at regular intervals.

Signed-off-by: Michael Mayer <michael@photoprism.app>
2024-05-10 17:41:32 +02:00

127 lines
3.2 KiB
Go

package commands
import (
"context"
"fmt"
"path/filepath"
"time"
"github.com/dustin/go-humanize/english"
"github.com/urfave/cli"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/pkg/clean"
"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."
// BackupCommand configures the command name, flags, and action.
var BackupCommand = cli.Command{
Name: "backup",
Description: backupDescription,
Usage: "Creates an index backup and optionally album YAML files organized by type",
ArgsUsage: "[filename]",
Flags: backupFlags,
Action: backupAction,
}
var backupFlags = []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "replace existing files",
},
cli.BoolFlag{
Name: "albums, a",
Usage: "create album YAML files organized by type",
},
cli.StringFlag{
Name: "albums-path",
Usage: "custom album files `PATH`",
},
cli.BoolFlag{
Name: "index, i",
Usage: "create index backup",
},
cli.StringFlag{
Name: "index-path",
Usage: "custom index backup `PATH`",
},
}
// backupAction creates a database backup.
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 != ""
albumsPath := ctx.String("albums-path")
backupAlbums := ctx.Bool("albums") || albumsPath != ""
force := ctx.Bool("force")
if !backupIndex && !backupAlbums {
return cli.ShowSubcommandHelp(ctx)
}
start := time.Now()
conf, err := InitConfig(ctx)
_, cancel := context.WithCancel(context.Background())
defer cancel()
if err != nil {
return err
}
conf.RegisterDb()
defer conf.Shutdown()
if backupIndex {
// If empty, use default backup file name.
if fileName == "" {
if !fs.PathWritable(backupPath) {
if backupPath != "" {
log.Warnf("custom index backup path not writable, using default")
}
backupPath = filepath.Join(conf.BackupPath(), conf.DatabaseDriver())
}
backupFile := time.Now().UTC().Format("2006-01-02") + ".sql"
fileName = filepath.Join(backupPath, backupFile)
}
if err = photoprism.BackupIndex(backupPath, fileName, fileName == "-", force); err != nil {
return fmt.Errorf("failed to create %s: %w", clean.Log(fileName), err)
}
}
if backupAlbums {
if !fs.PathWritable(albumsPath) {
if albumsPath != "" {
log.Warnf("album files path not writable, using default")
}
albumsPath = conf.AlbumsPath()
}
log.Infof("saving albums in %s", clean.Log(albumsPath))
if count, backupErr := photoprism.BackupAlbums(albumsPath, true); backupErr != nil {
return backupErr
} else {
log.Infof("created %s", english.Plural(count, "YAML album file", "YAML album files"))
}
}
elapsed := time.Since(start)
log.Infof("completed in %s", elapsed)
return nil
}