Skip to content

Commit

Permalink
fix(database): detect and recover from database corruption
Browse files Browse the repository at this point in the history
  • Loading branch information
JagandeepBrar committed Aug 21, 2022
1 parent 1b143bc commit 758756f
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 17 deletions.
3 changes: 3 additions & 0 deletions assets/localization/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@
"settings.CustomHeader": "Custom Header",
"settings.CustomHeaders": "Custom Headers",
"settings.CustomHeadersDescription": "Add Custom Headers to Requests",
"settings.DatabaseCorruptionDetected": "Database Corruption Detected",
"settings.DatabaseCorruptionDetectedMessageLine1": "Database corruption has been detected and your configuration has been cleared.",
"settings.DatabaseCorruptionDetectedMessageLine2": "Sorry about the inconvenience, all previously created backups should be restorable even when previously exported with a corrupted database.",
"settings.DaysOne": "1 Day",
"settings.DaysCount": "{} Days",
"settings.DefaultPage": "Default Page",
Expand Down
2 changes: 1 addition & 1 deletion lib/database/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class LunaConfig {
error,
stack,
);
LunaDatabase().bootstrap();
LunaDatabase().bootstrap(databaseCorruption: true);
}

LunaState.reset(context);
Expand Down
24 changes: 20 additions & 4 deletions lib/database/database.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:lunasea/database/box.dart';
import 'package:lunasea/database/models/profile.dart';
import 'package:lunasea/database/table.dart';
import 'package:lunasea/database/tables/bios.dart';
import 'package:lunasea/database/tables/lunasea.dart';
import 'package:lunasea/system/platform.dart';
import 'package:lunasea/vendor.dart';
Expand All @@ -17,17 +18,32 @@ class LunaDatabase {
Future<void> initialize() async {
await Hive.initFlutter(_path);
LunaTable.register();
await LunaBox.open();
await open();
}

if (LunaBox.profiles.isEmpty) await bootstrap();
Future<void> open() async {
try {
await LunaBox.open();
if (LunaBox.profiles.isEmpty) await bootstrap();
} catch (error) {
for (final box in LunaBox.values) {
await Hive.deleteBoxFromDisk(box.key);
}

await LunaBox.open();
await bootstrap(databaseCorruption: true);
}
}

Future<void> bootstrap() async {
Future<void> bootstrap({
bool databaseCorruption = false,
}) async {
const defaultProfile = LunaProfile.DEFAULT_PROFILE;
await clear();

const defaultProfile = LunaProfile.DEFAULT_PROFILE;
LunaBox.profiles.update(defaultProfile, LunaProfile());
LunaSeaDatabase.ENABLED_PROFILE.update(defaultProfile);
BIOSDatabase.DATABASE_CORRUPTION.update(databaseCorruption);
}

Future<void> clear() async {
Expand Down
2 changes: 2 additions & 0 deletions lib/database/table.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:lunasea/database/box.dart';
import 'package:lunasea/database/models/deprecated.dart';
import 'package:lunasea/database/tables/bios.dart';
import 'package:lunasea/database/tables/dashboard.dart';
import 'package:lunasea/database/tables/lidarr.dart';
import 'package:lunasea/database/tables/lunasea.dart';
Expand All @@ -16,6 +17,7 @@ enum LunaTable<T extends LunaTableMixin> {
dashboard<DashboardDatabase>('home', items: DashboardDatabase.values),
lidarr<LidarrDatabase>('lidarr', items: LidarrDatabase.values),
lunasea<LunaSeaDatabase>('lunasea', items: LunaSeaDatabase.values),
bios<BIOSDatabase>('bios', items: BIOSDatabase.values),
nzbget<NZBGetDatabase>('nzbget', items: NZBGetDatabase.values),
overseerr<OverseerrDatabase>('overseerr', items: OverseerrDatabase.values),
radarr<RadarrDatabase>('radarr', items: RadarrDatabase.values),
Expand Down
14 changes: 14 additions & 0 deletions lib/database/tables/bios.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:lunasea/database/table.dart';

enum BIOSDatabase<T> with LunaTableMixin<T> {
DATABASE_CORRUPTION<bool>(false),
FIRST_BOOT<bool>(true);

@override
LunaTable get table => LunaTable.bios;

@override
final T fallback;

const BIOSDatabase(this.fallback);
}
21 changes: 16 additions & 5 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:lunasea/core.dart';
import 'package:lunasea/database/database.dart';
import 'package:lunasea/database/tables/bios.dart';
import 'package:lunasea/firebase/core.dart';
import 'package:lunasea/firebase/firestore.dart';
import 'package:lunasea/firebase/messaging.dart';
Expand All @@ -18,7 +19,8 @@ import 'package:lunasea/system/network/network.dart';
import 'package:lunasea/system/quick_actions/quick_actions.dart';
import 'package:lunasea/system/window_manager/window_manager.dart';
import 'package:lunasea/system/platform.dart';
import 'package:lunasea/utils/changelog/sheet.dart';
import 'package:lunasea/widgets/sheets/database_corruption/sheet.dart';
import 'package:lunasea/widgets/sheets/changelog/sheet.dart';

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

_healthCheck();
await _healthCheck();
BIOSDatabase.FIRST_BOOT.update(false);
}

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

Future<void> _healthCheck() async {
LunaBuild().isLatestBuildVersion().then((isLatest) {
if (!isLatest.item1) ChangelogSheet().show();
});
final isLatest = LunaBuild().isLatestBuildVersion();
final firstBoot = BIOSDatabase.FIRST_BOOT.read();

if (BIOSDatabase.DATABASE_CORRUPTION.read()) {
DatabaseCorruptionSheet().show();
BIOSDatabase.DATABASE_CORRUPTION.update(false);
}

if (!firstBoot && !isLatest.item1) {
ChangelogSheet().show();
}
}

@override
Expand Down
3 changes: 1 addition & 2 deletions lib/modules/search/core/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ class NewznabAPI {
baseUrl: '${indexer.host}/api',
headers: {
'User-Agent': _USER_AGENT,
if (indexer.headers.isNotEmpty)
...indexer.headers as Map<String, dynamic>,
if (indexer.headers.isNotEmpty) ...indexer.headers,
},
queryParameters: {
if (indexer.apiKey != '') 'apikey': indexer.apiKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import 'package:lunasea/system/environment.dart';
import 'package:lunasea/system/flavor.dart';
import 'package:lunasea/system/platform.dart';
import 'package:lunasea/vendor.dart';
import 'package:lunasea/utils/changelog/sheet.dart';
import 'package:lunasea/widgets/sheets/changelog/sheet.dart';
import 'package:lunasea/widgets/ui.dart';

class BuildDetails extends ConsumerStatefulWidget {
Expand Down
2 changes: 1 addition & 1 deletion lib/system/build.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class LunaBuild {
return const Tuple2(false, null);
}

Future<Tuple2<bool, int>> isLatestBuildVersion() async {
Tuple2<bool, int> isLatestBuildVersion() {
const database = LunaSeaDatabase.CHANGELOG_LAST_BUILD_VERSION;
const build = LunaEnvironment.build;

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:lunasea/vendor.dart';
import 'package:lunasea/utils/changelog/change.dart';
import 'package:lunasea/widgets/sheets/changelog/change.dart';

part 'changelog.g.dart';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import 'package:lunasea/system/build.dart';
import 'package:lunasea/system/environment.dart';
import 'package:lunasea/system/flavor.dart';
import 'package:lunasea/system/logger.dart';
import 'package:lunasea/utils/changelog/change.dart';
import 'package:lunasea/utils/changelog/changelog.dart';
import 'package:lunasea/utils/links.dart';
import 'package:lunasea/vendor.dart';
import 'package:lunasea/widgets/ui.dart';
import 'package:lunasea/widgets/sheets/changelog/change.dart';
import 'package:lunasea/widgets/sheets/changelog/changelog.dart';

class ChangelogSheet extends LunaBottomModalSheet {
static final _defaultMOTD =
Expand Down
20 changes: 20 additions & 0 deletions lib/widgets/sheets/database_corruption/sheet.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:lunasea/vendor.dart';
import 'package:lunasea/widgets/ui.dart';

class DatabaseCorruptionSheet extends LunaBottomModalSheet {
@override
Widget builder(BuildContext context) {
return LunaListViewModal(
children: [
LunaHeader(
text: 'settings.DatabaseCorruptionDetected'.tr(),
subtitle: [
'settings.DatabaseCorruptionDetectedMessageLine1'.tr(),
'settings.DatabaseCorruptionDetectedMessageLine2'.tr(),
].join('\n\n'),
),
],
);
}
}
3 changes: 3 additions & 0 deletions localization/settings/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
"settings.CustomHeader": "Custom Header",
"settings.CustomHeaders": "Custom Headers",
"settings.CustomHeadersDescription": "Add Custom Headers to Requests",
"settings.DatabaseCorruptionDetected": "Database Corruption Detected",
"settings.DatabaseCorruptionDetectedMessageLine1": "Database corruption has been detected and your configuration has been cleared.",
"settings.DatabaseCorruptionDetectedMessageLine2": "Sorry about the inconvenience, all previously created backups should be restorable even when previously exported with a corrupted database.",
"settings.DaysOne": "1 Day",
"settings.DaysCount": "{} Days",
"settings.DefaultPage": "Default Page",
Expand Down

0 comments on commit 758756f

Please sign in to comment.