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) }