nsfwapp/backend/settings_sqlite_move.go
2026-06-23 21:56:13 +02:00

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