Skip to content
This repository was archived by the owner on Apr 3, 2025. It is now read-only.

Commit 758756f

Browse files
committed
fix(database): detect and recover from database corruption
1 parent 1b143bc commit 758756f

File tree

14 files changed

+85
-17
lines changed

14 files changed

+85
-17
lines changed

assets/localization/en.json

+3
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@
373373
"settings.CustomHeader": "Custom Header",
374374
"settings.CustomHeaders": "Custom Headers",
375375
"settings.CustomHeadersDescription": "Add Custom Headers to Requests",
376+
"settings.DatabaseCorruptionDetected": "Database Corruption Detected",
377+
"settings.DatabaseCorruptionDetectedMessageLine1": "Database corruption has been detected and your configuration has been cleared.",
378+
"settings.DatabaseCorruptionDetectedMessageLine2": "Sorry about the inconvenience, all previously created backups should be restorable even when previously exported with a corrupted database.",
376379
"settings.DaysOne": "1 Day",
377380
"settings.DaysCount": "{} Days",
378381
"settings.DefaultPage": "Default Page",

lib/database/config.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class LunaConfig {
2626
error,
2727
stack,
2828
);
29-
LunaDatabase().bootstrap();
29+
LunaDatabase().bootstrap(databaseCorruption: true);
3030
}
3131

3232
LunaState.reset(context);

lib/database/database.dart

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:lunasea/database/box.dart';
22
import 'package:lunasea/database/models/profile.dart';
33
import 'package:lunasea/database/table.dart';
4+
import 'package:lunasea/database/tables/bios.dart';
45
import 'package:lunasea/database/tables/lunasea.dart';
56
import 'package:lunasea/system/platform.dart';
67
import 'package:lunasea/vendor.dart';
@@ -17,17 +18,32 @@ class LunaDatabase {
1718
Future<void> initialize() async {
1819
await Hive.initFlutter(_path);
1920
LunaTable.register();
20-
await LunaBox.open();
21+
await open();
22+
}
2123

22-
if (LunaBox.profiles.isEmpty) await bootstrap();
24+
Future<void> open() async {
25+
try {
26+
await LunaBox.open();
27+
if (LunaBox.profiles.isEmpty) await bootstrap();
28+
} catch (error) {
29+
for (final box in LunaBox.values) {
30+
await Hive.deleteBoxFromDisk(box.key);
31+
}
32+
33+
await LunaBox.open();
34+
await bootstrap(databaseCorruption: true);
35+
}
2336
}
2437

25-
Future<void> bootstrap() async {
38+
Future<void> bootstrap({
39+
bool databaseCorruption = false,
40+
}) async {
41+
const defaultProfile = LunaProfile.DEFAULT_PROFILE;
2642
await clear();
2743

28-
const defaultProfile = LunaProfile.DEFAULT_PROFILE;
2944
LunaBox.profiles.update(defaultProfile, LunaProfile());
3045
LunaSeaDatabase.ENABLED_PROFILE.update(defaultProfile);
46+
BIOSDatabase.DATABASE_CORRUPTION.update(databaseCorruption);
3147
}
3248

3349
Future<void> clear() async {

lib/database/table.dart

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/material.dart';
22
import 'package:lunasea/database/box.dart';
33
import 'package:lunasea/database/models/deprecated.dart';
4+
import 'package:lunasea/database/tables/bios.dart';
45
import 'package:lunasea/database/tables/dashboard.dart';
56
import 'package:lunasea/database/tables/lidarr.dart';
67
import 'package:lunasea/database/tables/lunasea.dart';
@@ -16,6 +17,7 @@ enum LunaTable<T extends LunaTableMixin> {
1617
dashboard<DashboardDatabase>('home', items: DashboardDatabase.values),
1718
lidarr<LidarrDatabase>('lidarr', items: LidarrDatabase.values),
1819
lunasea<LunaSeaDatabase>('lunasea', items: LunaSeaDatabase.values),
20+
bios<BIOSDatabase>('bios', items: BIOSDatabase.values),
1921
nzbget<NZBGetDatabase>('nzbget', items: NZBGetDatabase.values),
2022
overseerr<OverseerrDatabase>('overseerr', items: OverseerrDatabase.values),
2123
radarr<RadarrDatabase>('radarr', items: RadarrDatabase.values),

lib/database/tables/bios.dart

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import 'package:lunasea/database/table.dart';
2+
3+
enum BIOSDatabase<T> with LunaTableMixin<T> {
4+
DATABASE_CORRUPTION<bool>(false),
5+
FIRST_BOOT<bool>(true);
6+
7+
@override
8+
LunaTable get table => LunaTable.bios;
9+
10+
@override
11+
final T fallback;
12+
13+
const BIOSDatabase(this.fallback);
14+
}

lib/main.dart

+16-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
55
import 'package:flutter/scheduler.dart';
66
import 'package:lunasea/core.dart';
77
import 'package:lunasea/database/database.dart';
8+
import 'package:lunasea/database/tables/bios.dart';
89
import 'package:lunasea/firebase/core.dart';
910
import 'package:lunasea/firebase/firestore.dart';
1011
import 'package:lunasea/firebase/messaging.dart';
@@ -18,7 +19,8 @@ import 'package:lunasea/system/network/network.dart';
1819
import 'package:lunasea/system/quick_actions/quick_actions.dart';
1920
import 'package:lunasea/system/window_manager/window_manager.dart';
2021
import 'package:lunasea/system/platform.dart';
21-
import 'package:lunasea/utils/changelog/sheet.dart';
22+
import 'package:lunasea/widgets/sheets/database_corruption/sheet.dart';
23+
import 'package:lunasea/widgets/sheets/changelog/sheet.dart';
2224

2325
/// LunaSea Entry Point: Initialize & Run Application
2426
///
@@ -111,7 +113,8 @@ class _State extends State<LunaOS> {
111113
_initNotifications();
112114
if (LunaQuickActions.isSupported) LunaQuickActions().initialize();
113115

114-
_healthCheck();
116+
await _healthCheck();
117+
BIOSDatabase.FIRST_BOOT.update(false);
115118
}
116119

117120
Future<void> _initNotifications() async {
@@ -128,9 +131,17 @@ class _State extends State<LunaOS> {
128131
}
129132

130133
Future<void> _healthCheck() async {
131-
LunaBuild().isLatestBuildVersion().then((isLatest) {
132-
if (!isLatest.item1) ChangelogSheet().show();
133-
});
134+
final isLatest = LunaBuild().isLatestBuildVersion();
135+
final firstBoot = BIOSDatabase.FIRST_BOOT.read();
136+
137+
if (BIOSDatabase.DATABASE_CORRUPTION.read()) {
138+
DatabaseCorruptionSheet().show();
139+
BIOSDatabase.DATABASE_CORRUPTION.update(false);
140+
}
141+
142+
if (!firstBoot && !isLatest.item1) {
143+
ChangelogSheet().show();
144+
}
134145
}
135146

136147
@override

lib/modules/search/core/api.dart

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@ class NewznabAPI {
1717
baseUrl: '${indexer.host}/api',
1818
headers: {
1919
'User-Agent': _USER_AGENT,
20-
if (indexer.headers.isNotEmpty)
21-
...indexer.headers as Map<String, dynamic>,
20+
if (indexer.headers.isNotEmpty) ...indexer.headers,
2221
},
2322
queryParameters: {
2423
if (indexer.apiKey != '') 'apikey': indexer.apiKey,

lib/modules/settings/routes/system/widgets/build_details.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import 'package:lunasea/system/environment.dart';
55
import 'package:lunasea/system/flavor.dart';
66
import 'package:lunasea/system/platform.dart';
77
import 'package:lunasea/vendor.dart';
8-
import 'package:lunasea/utils/changelog/sheet.dart';
8+
import 'package:lunasea/widgets/sheets/changelog/sheet.dart';
99
import 'package:lunasea/widgets/ui.dart';
1010

1111
class BuildDetails extends ConsumerStatefulWidget {

lib/system/build.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class LunaBuild {
4040
return const Tuple2(false, null);
4141
}
4242

43-
Future<Tuple2<bool, int>> isLatestBuildVersion() async {
43+
Tuple2<bool, int> isLatestBuildVersion() {
4444
const database = LunaSeaDatabase.CHANGELOG_LAST_BUILD_VERSION;
4545
const build = LunaEnvironment.build;
4646

lib/utils/changelog/changelog.dart renamed to lib/widgets/sheets/changelog/changelog.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'package:lunasea/vendor.dart';
2-
import 'package:lunasea/utils/changelog/change.dart';
2+
import 'package:lunasea/widgets/sheets/changelog/change.dart';
33

44
part 'changelog.g.dart';
55

lib/utils/changelog/sheet.dart renamed to lib/widgets/sheets/changelog/sheet.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import 'package:lunasea/system/build.dart';
55
import 'package:lunasea/system/environment.dart';
66
import 'package:lunasea/system/flavor.dart';
77
import 'package:lunasea/system/logger.dart';
8-
import 'package:lunasea/utils/changelog/change.dart';
9-
import 'package:lunasea/utils/changelog/changelog.dart';
108
import 'package:lunasea/utils/links.dart';
119
import 'package:lunasea/vendor.dart';
1210
import 'package:lunasea/widgets/ui.dart';
11+
import 'package:lunasea/widgets/sheets/changelog/change.dart';
12+
import 'package:lunasea/widgets/sheets/changelog/changelog.dart';
1313

1414
class ChangelogSheet extends LunaBottomModalSheet {
1515
static final _defaultMOTD =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:lunasea/vendor.dart';
3+
import 'package:lunasea/widgets/ui.dart';
4+
5+
class DatabaseCorruptionSheet extends LunaBottomModalSheet {
6+
@override
7+
Widget builder(BuildContext context) {
8+
return LunaListViewModal(
9+
children: [
10+
LunaHeader(
11+
text: 'settings.DatabaseCorruptionDetected'.tr(),
12+
subtitle: [
13+
'settings.DatabaseCorruptionDetectedMessageLine1'.tr(),
14+
'settings.DatabaseCorruptionDetectedMessageLine2'.tr(),
15+
].join('\n\n'),
16+
),
17+
],
18+
);
19+
}
20+
}

localization/settings/en.json

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@
7676
"settings.CustomHeader": "Custom Header",
7777
"settings.CustomHeaders": "Custom Headers",
7878
"settings.CustomHeadersDescription": "Add Custom Headers to Requests",
79+
"settings.DatabaseCorruptionDetected": "Database Corruption Detected",
80+
"settings.DatabaseCorruptionDetectedMessageLine1": "Database corruption has been detected and your configuration has been cleared.",
81+
"settings.DatabaseCorruptionDetectedMessageLine2": "Sorry about the inconvenience, all previously created backups should be restorable even when previously exported with a corrupted database.",
7982
"settings.DaysOne": "1 Day",
8083
"settings.DaysCount": "{} Days",
8184
"settings.DefaultPage": "Default Page",

0 commit comments

Comments
 (0)