feat: rename/relocate a profile's SQLite logbook (QSOs carried across)

"Choose a dedicated file" points at a fresh/empty file; there was no way to
rename or move an existing logbook WITH its data. RenameLogbook VACUUM INTOs the
active profile's SQLite logbook to a new path, repoints the profile and switches
the live logbook (no restart). Deletes the old file only when it was a dedicated
per-profile file; the shared default logbook.db is left for other profiles.
Errors on a MySQL logbook. UI: "Rename / relocate…" button in the logbook section.
This commit is contained in:
2026-07-24 18:22:17 +02:00
parent 2b3d118d84
commit 77a2350240
6 changed files with 78 additions and 5 deletions
+55
View File
@@ -1806,6 +1806,61 @@ func (a *App) SaveMySQLSettings(s MySQLSettings) error {
return a.switchLogbook(p)
}
// RenameLogbook copies the ACTIVE profile's SQLite logbook to dest (with its
// QSOs), repoints the profile at it and switches the live logbook — no restart.
// Unlike "choose a dedicated file" (which points at a fresh/empty file), this
// carries the data across. The old file is deleted only when it was this
// profile's OWN dedicated file; the shared default logbook.db is left in place
// (other profiles may use it). Errors if the logbook is MySQL.
func (a *App) RenameLogbook(dest string) error {
dest = strings.TrimSpace(dest)
if dest == "" {
return fmt.Errorf("no destination given")
}
p, err := a.profiles.Active(a.ctx)
if err != nil {
return fmt.Errorf("no active profile: %w", err)
}
if p.DB.Backend == "mysql" {
return fmt.Errorf("this profile's logbook is MySQL — no file to rename")
}
old := strings.TrimSpace(p.DB.Path)
wasDedicated := old != ""
if old == "" {
old = a.logbookPath
}
if old == "" || a.logDb == nil {
return fmt.Errorf("no logbook file to rename")
}
if strings.EqualFold(filepath.Clean(dest), filepath.Clean(old)) {
return fmt.Errorf("that is already the current logbook name")
}
if _, err := os.Stat(dest); err == nil {
return fmt.Errorf("a file already exists at %s — pick a new name", dest)
}
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
return fmt.Errorf("create folder: %w", err)
}
safe := strings.ReplaceAll(dest, "'", "''")
if _, err := a.logDb.ExecContext(a.ctx, "VACUUM INTO '"+safe+"'"); err != nil {
return fmt.Errorf("copy logbook: %w", err)
}
p.DB.Backend = "sqlite"
p.DB.Path = dest
if err := a.profiles.SetDB(a.ctx, p.ID, p.DB); err != nil {
return err
}
if err := a.switchLogbook(p); err != nil { // opens dest, closes the old conn
return err
}
if wasDedicated {
for _, f := range []string{old, old + "-wal", old + "-shm"} {
_ = os.Remove(f)
}
}
return nil
}
// TestMySQLConnection pings the shared MySQL database with the given settings
// (no migrations) so the user can validate connectivity from the UI.
func (a *App) TestMySQLConnection(s MySQLSettings) error {