feat: split settings DB from the QSO logbook (own file per profile)

Design flaw: opslog.db held BOTH the config (settings + profiles) AND the SQLite
logbook, so the only way to a separate SQLite logbook — the whole-app "change
database location" pointer — swapped the config too, wiping the operator's setup
(reported: creating a new SQLite for a visiting op lost all profiles/settings).

Now the two are separate files:
- Settings database: settings + profiles. Fresh installs name it settings.db;
  existing installs keep opslog.db.
- Logbook (QSOs): a dedicated logbook.db next to the settings db, or a
  per-profile file (profile.ProfileDB.Path), or MySQL. connectLogbook opens the
  logbook file (db.Open creates+migrates on first use); the settings db is used
  as the logbook only if the split ever fails.

One-time, non-destructive migration at startup: if there's no logbook file yet
but the settings db already holds QSOs (legacy combined opslog.db), seed
logbook.db with a clean copy via VACUUM INTO — contacts move to the logbook, the
originals stay in the settings db as an untouched backup.

UI: the Database panel now shows the settings database and this profile's logbook
as two clearly labelled sections (+ "Open folder"), and the logbook selector is
SQLite (default logbook.db, or a dedicated file via "Choose a dedicated file…")
vs MySQL — no more confusing shared/separate choice. New binding RevealDataFolder.
This commit is contained in:
2026-07-24 18:13:08 +02:00
parent 638ffcb326
commit 2b3d118d84
8 changed files with 205 additions and 131 deletions
+8 -6
View File
@@ -286,9 +286,10 @@ const en: Dict = {
'prof.hint': 'Switch between operating identities (home / portable / SOTA / contest). Pick a profile here, then edit its fields in the other sections (Station Information, etc.) — changes are saved against the selected profile.', 'prof.active': 'ACTIVE', 'prof.duplicate': 'Duplicate', 'prof.delete': 'Delete', 'prof.profileName': 'Profile name',
'prof.configId': 'Configuration ID', 'prof.description': 'Description', 'prof.new': 'New', 'prof.newTitle': 'Create a new empty profile', 'prof.dupTitle': 'Clone the selected profile (keeps all its fields)', 'prof.setActive': 'Set active', 'prof.setActiveTitle': 'Activate the selected profile — new QSOs will use its MY_* fields', 'prof.deleteTitle': 'Delete the selected profile', 'prof.cantDeleteLast': 'Cannot delete the last profile', 'prof.activeSuffix': ' (active)', 'prof.viewingNote': "You're viewing {name}. The active profile is {active} — its values are stamped on new QSOs. Click Set active to switch.",
// Database panel
'db.optSqlite': 'SQLite — shared local database (default)', 'db.optSqliteFile': 'SQLite — separate file (this profile only)', 'db.optMysql': 'MySQL — shared server (multi-operator)', 'db.profileHint': 'This is the logbook for the active profile. Different profiles can point at different databases — switching profile switches the logbook.',
'db.logbookFile': "This profile's logbook file", 'db.logbookFileHint': "Only this profile's QSOs live here. Your settings and profiles stay in the shared app database, so switching this never affects your other profiles — ideal for a visiting operator. A new name is created automatically.", 'db.chooseFile': 'Choose file', 'db.switchedSqliteFile': 'Logbook now uses a separate SQLite file.',
'db.appDb': 'Shared application database', 'db.appDbHint': 'Holds your settings, profiles and (for this profile) the QSOs. Changing its location moves the WHOLE install — settings and profiles included.',
'db.optSqlite': 'SQLite — local file', 'db.optMysql': 'MySQL — shared server (multi-operator)', 'db.profileHint': 'This is the logbook for the active profile. Different profiles can point at different databases — switching profile switches the logbook.',
'db.logbookLabel': 'Logbook', 'db.openFolder': 'Open folder', 'db.dedicatedFile': 'dedicated file', 'db.useDefaultLogbook': 'Use the default logbook',
'db.logbookFile': "This profile's logbook file", 'db.logbookFileHint': "Your QSOs live here — separate from the settings database. By default it's logbook.db next to your settings; choose a dedicated file to keep a visiting operator's contacts apart. A new file is created automatically.", 'db.chooseFile': 'Choose a dedicated file…', 'db.switchedSqliteFile': 'Logbook now uses a dedicated SQLite file.',
'db.appDb': 'Settings database (settings + profiles)', 'db.appDbHint': 'Holds your settings and profiles — NOT your QSOs (those are in the logbook below). Changing its location moves the whole install.',
'db.saveSwitch': 'Save & switch logbook', 'db.switchedMysql': 'Logbook switched to MySQL ✓', 'db.switchedSqlite': 'Logbook switched to local SQLite ✓',
'db.backend': 'Backend', 'db.configLocal': 'settings stay in the local SQLite file', 'db.connectUse': 'Connect & use',
'db.savedRestart': 'Saved. Restart OpsLog to open this logbook:', 'db.restartNow': 'Restart OpsLog', 'db.restartHint': '(reopens automatically)',
@@ -643,9 +644,10 @@ const fr: Dict = {
'prof.deleteConfirm': 'Supprimer le profil « {name} » ? Tous ses réglages seront perdus.', 'prof.dupPrompt': 'Nom du nouveau profil (copie de « {name} ») :', 'prof.dupSuffix': '{name} Copie', 'prof.newPrompt': 'Nom du nouveau profil :', 'prof.newDefault': 'Nouveau profil',
'prof.hint': "Bascule entre tes identités d'opération (maison / portable / SOTA / contest). Choisis un profil ici, puis édite ses champs dans les autres sections (Informations station, etc.) — les changements sont enregistrés sur le profil sélectionné.", 'prof.active': 'ACTIF', 'prof.duplicate': 'Dupliquer', 'prof.delete': 'Supprimer', 'prof.profileName': 'Nom du profil',
'prof.configId': 'ID de configuration', 'prof.description': 'Description', 'prof.new': 'Nouveau', 'prof.newTitle': 'Créer un nouveau profil vierge', 'prof.dupTitle': 'Cloner le profil sélectionné (garde tous ses champs)', 'prof.setActive': 'Activer', 'prof.setActiveTitle': 'Activer le profil sélectionné — les nouveaux QSO utiliseront ses champs MY_*', 'prof.deleteTitle': 'Supprimer le profil sélectionné', 'prof.cantDeleteLast': 'Impossible de supprimer le dernier profil', 'prof.activeSuffix': ' (actif)', 'prof.viewingNote': 'Tu consultes {name}. Le profil actif est {active} — ses valeurs sont inscrites sur les nouveaux QSO. Clique « Activer » pour basculer.',
'db.optSqlite': 'SQLite — base locale partagée (défaut)', 'db.optSqliteFile': 'SQLite — fichier séparé (ce profil seulement)', 'db.optMysql': 'MySQL — serveur partagé (multi-opérateur)', 'db.profileHint': 'Ceci est le journal du profil actif. Des profils différents peuvent pointer vers des bases différentes — changer de profil change le journal.',
'db.logbookFile': 'Fichier journal de ce profil', 'db.logbookFileHint': "Seuls les QSO de ce profil y sont stockés. Tes réglages et profils restent dans la base applicative partagée, donc changer ceci n'affecte jamais tes autres profils — idéal pour un opérateur de passage. Un nouveau nom est créé automatiquement.", 'db.chooseFile': 'Choisir le fichier', 'db.switchedSqliteFile': 'Le journal utilise désormais un fichier SQLite séparé.',
'db.appDb': 'Base applicative partagée', 'db.appDbHint': "Contient tes réglages, tes profils et (pour ce profil) les QSO. Changer son emplacement déplace TOUTE l'installation — réglages et profils compris.",
'db.optSqlite': 'SQLite — fichier local', 'db.optMysql': 'MySQL — serveur partagé (multi-opérateur)', 'db.profileHint': 'Ceci est le journal du profil actif. Des profils différents peuvent pointer vers des bases différentes — changer de profil change le journal.',
'db.logbookLabel': 'Journal', 'db.openFolder': 'Ouvrir le dossier', 'db.dedicatedFile': 'fichier dédié', 'db.useDefaultLogbook': 'Utiliser le journal par défaut',
'db.logbookFile': 'Fichier journal de ce profil', 'db.logbookFileHint': "Tes QSO sont ici — séparés de la base de réglages. Par défaut c'est logbook.db à côté de tes réglages ; choisis un fichier dédié pour isoler les contacts d'un opérateur de passage. Un nouveau fichier est créé automatiquement.", 'db.chooseFile': 'Choisir un fichier dédié…', 'db.switchedSqliteFile': 'Le journal utilise désormais un fichier SQLite dédié.',
'db.appDb': 'Base de réglages (réglages + profils)', 'db.appDbHint': "Contient tes réglages et tes profils — PAS tes QSO (ceux-ci sont dans le journal ci-dessous). Changer son emplacement déplace toute l'installation.",
'db.saveSwitch': 'Enregistrer & basculer le journal', 'db.switchedMysql': 'Journal basculé vers MySQL ✓', 'db.switchedSqlite': 'Journal basculé vers SQLite local ✓',
'db.backend': 'Base de données', 'db.configLocal': 'les réglages restent dans le fichier SQLite local', 'db.connectUse': 'Connecter et utiliser',
'db.savedRestart': 'Enregistré. Redémarrez OpsLog pour ouvrir cette base :', 'db.restartNow': 'Redémarrer OpsLog', 'db.restartHint': '(réouverture automatique)',