197 lines
5.2 KiB
Go
197 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func sqlitePathChanged(current, next RecorderSettings) bool {
|
|
return normalizeDatabaseType(current.DatabaseType) == databaseTypeSQLite &&
|
|
normalizeDatabaseType(next.DatabaseType) == databaseTypeSQLite &&
|
|
normalizeSQLitePath(current.SQLitePath) != normalizeSQLitePath(next.SQLitePath)
|
|
}
|
|
|
|
func moveSQLiteDatabaseForSettings(current, next RecorderSettings) error {
|
|
currentConfig, err := buildModelStoreConfigFromRecorderSettings(current)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
nextConfig, err := buildModelStoreConfigFromRecorderSettings(next)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return moveSQLiteDatabaseFiles(currentConfig.SQLitePath, nextConfig.SQLitePath)
|
|
}
|
|
|
|
func restoreModelStoreForSettings(rec RecorderSettings) {
|
|
config, err := buildModelStoreConfigFromRecorderSettings(rec)
|
|
if err != nil {
|
|
return
|
|
}
|
|
store := NewModelStore(config)
|
|
if err := store.Load(); err != nil {
|
|
_ = store.Close()
|
|
return
|
|
}
|
|
setModelStore(store)
|
|
}
|
|
|
|
func moveSQLiteDatabaseFiles(sourcePath, targetPath string) error {
|
|
sourcePath = filepath.Clean(strings.TrimSpace(sourcePath))
|
|
targetPath = filepath.Clean(strings.TrimSpace(targetPath))
|
|
if sourcePath == "" || targetPath == "" {
|
|
return errors.New("sqlite source/target path fehlt")
|
|
}
|
|
if sourcePath == targetPath {
|
|
return nil
|
|
}
|
|
|
|
sourceInfo, err := os.Stat(sourcePath)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if sourceInfo.IsDir() {
|
|
return appErrorf("sqlite source path ist ein ordner: %s", sourcePath)
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, target := range append([]string{targetPath}, sqliteSidecarPaths(targetPath)...) {
|
|
if _, err := os.Stat(target); err == nil {
|
|
return appErrorf("Ziel-Datei existiert bereits: %s", target)
|
|
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
}
|
|
sourceImageDir := sqliteProfileImageDir(sourcePath)
|
|
targetImageDir := sqliteProfileImageDir(targetPath)
|
|
if !sameCleanPath(sourceImageDir, targetImageDir) {
|
|
if _, err := os.Stat(sourceImageDir); err == nil {
|
|
if _, err := os.Stat(targetImageDir); err == nil {
|
|
return appErrorf("Ziel-Profilbildordner existiert bereits: %s", targetImageDir)
|
|
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
} else if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if store := getModelStore(); store != nil && store.isSQLite() && store.db != nil {
|
|
_, _ = store.db.Exec(`PRAGMA wal_checkpoint(TRUNCATE);`)
|
|
_ = store.Close()
|
|
}
|
|
|
|
if err := moveDatabaseFile(sourcePath, targetPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
sourceSidecars := sqliteSidecarPaths(sourcePath)
|
|
targetSidecars := sqliteSidecarPaths(targetPath)
|
|
for i, source := range sourceSidecars {
|
|
if _, err := os.Stat(source); errors.Is(err, os.ErrNotExist) {
|
|
continue
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
if err := moveDatabaseFile(source, targetSidecars[i]); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if !sameCleanPath(sourceImageDir, targetImageDir) {
|
|
if err := moveSQLiteProfileImageDir(sourceImageDir, targetImageDir); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func sqliteProfileImageDir(sqlitePath string) string {
|
|
return filepath.Join(filepath.Dir(filepath.Clean(strings.TrimSpace(sqlitePath))), modelProfileImageDirName)
|
|
}
|
|
|
|
func sameCleanPath(a, b string) bool {
|
|
return strings.EqualFold(filepath.Clean(strings.TrimSpace(a)), filepath.Clean(strings.TrimSpace(b)))
|
|
}
|
|
|
|
func moveSQLiteProfileImageDir(sourceDir, targetDir string) error {
|
|
sourceDir = filepath.Clean(strings.TrimSpace(sourceDir))
|
|
targetDir = filepath.Clean(strings.TrimSpace(targetDir))
|
|
if sourceDir == "" || targetDir == "" || sameCleanPath(sourceDir, targetDir) {
|
|
return nil
|
|
}
|
|
|
|
info, err := os.Stat(sourceDir)
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() {
|
|
return appErrorf("Profilbildpfad ist kein Ordner: %s", sourceDir)
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(targetDir), 0755); err != nil {
|
|
return err
|
|
}
|
|
if err := renameWithRetry(sourceDir, targetDir); err == nil {
|
|
return nil
|
|
}
|
|
if err := copyDirectory(sourceDir, targetDir); err != nil {
|
|
return err
|
|
}
|
|
return os.RemoveAll(sourceDir)
|
|
}
|
|
|
|
func copyDirectory(sourceDir, targetDir string) error {
|
|
sourceDir = filepath.Clean(strings.TrimSpace(sourceDir))
|
|
targetDir = filepath.Clean(strings.TrimSpace(targetDir))
|
|
return filepath.WalkDir(sourceDir, func(path string, entry os.DirEntry, walkErr error) error {
|
|
if walkErr != nil {
|
|
return walkErr
|
|
}
|
|
rel, err := filepath.Rel(sourceDir, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
target := filepath.Join(targetDir, rel)
|
|
if entry.IsDir() {
|
|
return os.MkdirAll(target, 0755)
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil {
|
|
return err
|
|
}
|
|
_, err = copyDatabaseFile(path, target)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func sqliteSidecarPaths(path string) []string {
|
|
return []string{
|
|
path + "-wal",
|
|
path + "-shm",
|
|
path + "-journal",
|
|
}
|
|
}
|
|
|
|
func moveDatabaseFile(sourcePath, targetPath string) error {
|
|
if err := renameWithRetry(sourcePath, targetPath); err == nil {
|
|
return nil
|
|
}
|
|
|
|
if _, err := copyDatabaseFile(sourcePath, targetPath); err != nil {
|
|
return err
|
|
}
|
|
return removeWithRetry(sourcePath)
|
|
}
|