From fa470c144209b114cb26a6baef13f231dbae5890 Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 20 Jul 2026 18:23:29 -0400 Subject: [PATCH] fix(#335): store the drift DB in app-support, not OneDrive-synced Documents drift_flutter's native default is getApplicationDocumentsDirectory(), which on Windows resolves to the user's Documents folder - redirected into OneDrive on most machines (verified: drift_flutter 0.3.1 connect.dart). A live SQLite file syncing to OneDrive risks lock contention and corruption, and it is simply the wrong place for app data. The DB now opens in getApplicationSupportDirectory() (%APPDATA% on Windows), the same place SharedPreferences already lives, so all app data sits together. Existing installs already have a DB in the old location, so opening a fresh one there would abandon their migrated history. _appSupportDatabaseDirectory() relocates it once on first run: it moves offband_store.sqlite and its -wal/-shm sidecars from the documents dir to the support dir before drift opens, and never deletes a source without a successful copy. If relocation fails it is logged loudly and a fresh DB is created, with the migration re-running from SharedPreferences rather than silently losing anything. Web is unaffected: path_provider has no web backend and drift ignores databaseDirectory there, using OPFS/IndexedDB. Adds `path` as a direct dependency (was transitive). flutter analyze clean, tests pass. --- lib/storage/drift/offband_database.dart | 68 ++++++++++++++++++++++++- pubspec.lock | 2 +- pubspec.yaml | 1 + 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/lib/storage/drift/offband_database.dart b/lib/storage/drift/offband_database.dart index 9ca9eff..2d14954 100644 --- a/lib/storage/drift/offband_database.dart +++ b/lib/storage/drift/offband_database.dart @@ -1,5 +1,11 @@ +import 'dart:io'; + import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; + +import '../../utils/app_logger.dart'; part 'offband_database.g.dart'; @@ -23,12 +29,25 @@ class OffbandDatabase extends _$OffbandDatabase { OffbandDatabase([QueryExecutor? executor]) : super(executor ?? _defaultExecutor()); + static const String _dbName = 'offband_store'; + /// `drift_flutter` selects the platform backend: native SQLite on desktop /// and mobile, WASM on web. `web:` names the assets that must be shipped for - /// the web build (`sqlite3.wasm`, `drift_worker.dart.js`). + /// the web build (`sqlite3.wasm`, `drift_worker.js`). static QueryExecutor _defaultExecutor() { return driftDatabase( - name: 'offband_store', + name: _dbName, + // Native default is getApplicationDocumentsDirectory(), which on Windows + // is the user's Documents folder — redirected into OneDrive on most + // machines. A live SQLite file syncing to OneDrive risks lock contention + // and corruption, and it is the wrong place for app data. Use the + // application-support dir instead (%APPDATA% on Windows), where + // SharedPreferences already lives. path_provider has no web + // implementation, so this only applies off web; web ignores + // databaseDirectory and uses OPFS/IndexedDB. + native: DriftNativeOptions( + databaseDirectory: _appSupportDatabaseDirectory, + ), web: DriftWebOptions( sqlite3Wasm: Uri.parse('sqlite3.wasm'), // Filename matches the asset published by the drift release, which is @@ -40,6 +59,51 @@ class OffbandDatabase extends _$OffbandDatabase { ); } + /// Resolves the application-support directory, and relocates a database left + /// in the old documents location by an earlier build. + /// + /// Builds before this fix opened the DB under getApplicationDocumentsDirectory + /// (OneDrive on Windows). Existing installs already have data there, so this + /// moves the file - and its `-wal` / `-shm` sidecars - to the new location on + /// first run, once, before drift opens it. Never deletes the source without a + /// successful move; a failed move leaves the old file so no data is stranded. + static Future _appSupportDatabaseDirectory() async { + final support = await getApplicationSupportDirectory(); + final newPath = p.join(support.path, '$_dbName.sqlite'); + + if (!File(newPath).existsSync()) { + try { + final docs = await getApplicationDocumentsDirectory(); + final oldPath = p.join(docs.path, '$_dbName.sqlite'); + if (File(oldPath).existsSync() && oldPath != newPath) { + for (final suffix in ['', '-wal', '-shm']) { + final src = File('$oldPath$suffix'); + if (src.existsSync()) { + src.copySync('$newPath$suffix'); + src.deleteSync(); + } + } + appLogger.info( + 'Relocated drift DB out of the documents dir (OneDrive on ' + 'Windows) into the app-support dir: $oldPath -> $newPath', + tag: 'Storage', + ); + } + } catch (e) { + // Relocation is best-effort: if it fails, drift opens a fresh DB at the + // correct location and the migration re-runs from SharedPreferences. + // Loud, never silent (SAFELANE 6). + appLogger.error( + 'Failed to relocate the drift DB from the documents dir: $e. ' + 'A fresh DB will be created at the app-support location.', + tag: 'Storage', + ); + } + } + + return support; + } + @override int get schemaVersion => 1; diff --git a/pubspec.lock b/pubspec.lock index be2ed1c..e31ec5c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -911,7 +911,7 @@ packages: source: hosted version: "3.2.1" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" diff --git a/pubspec.yaml b/pubspec.yaml index e93e05a..d385dd6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -79,6 +79,7 @@ dependencies: # re-download both assets. tool/check_drift_assets.dart enforces the match. drift: 2.34.2 drift_flutter: 0.3.1 + path: ^1.9.1 hooks: user_defines: