Skip to content

Commit

Permalink
opt.: webdav sync logic
Browse files Browse the repository at this point in the history
  • Loading branch information
lollipopkit committed Dec 4, 2023
1 parent 5a982ae commit 03b9a46
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 80 deletions.
20 changes: 11 additions & 9 deletions lib/core/utils/sync/icloud.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:icloud_storage/icloud_storage.dart';
import 'package:logging/logging.dart';
import 'package:toolbox/data/model/app/backup.dart';
import 'package:toolbox/data/model/app/sync.dart';
import 'package:toolbox/data/res/logger.dart';

import '../../../data/model/app/error.dart';
import '../../../data/res/path.dart';

abstract final class ICloud {
static const _containerId = 'iCloud.tech.lolli.serverbox';

static final _logger = Logger('iCloud');

/// Upload file to iCloud
///
/// - [relativePath] is the path relative to [docDir],
Expand Down Expand Up @@ -171,43 +173,43 @@ abstract final class ICloud {

return SyncResult(up: uploadFiles, down: downloadFiles, err: errs);
} catch (e, s) {
Loggers.app.warning('iCloud sync: $relativePaths failed', e, s);
_logger.warning('Sync: $relativePaths failed', e, s);
return SyncResult(up: uploadFiles, down: downloadFiles, err: {
'Generic': ICloudErr(type: ICloudErrType.generic, message: '$e')
});
} finally {
Loggers.app.info('iCloud sync, up: $uploadFiles, down: $downloadFiles');
_logger.info('Sync, up: $uploadFiles, down: $downloadFiles');
}
}

static Future<void> sync() async {
try {
final result = await download(relativePath: Paths.bakName);
if (result != null) {
Loggers.app.warning('Download backup failed: $result');
_logger.warning('Download backup failed: $result');
return;
}
} catch (e, s) {
Loggers.app.warning('Download backup failed', e, s);
_logger.warning('Download backup failed', e, s);
}
final dlFile = await File(await Paths.bak).readAsString();
final dlBak = await compute(Backup.fromJsonString, dlFile);
final restore = await dlBak.restore();
switch (restore) {
case true:
Loggers.app.info('Restore from iCloud (${dlBak.lastModTime}) success');
_logger.info('Restore from ${dlBak.lastModTime} success');
break;
case false:
await Backup.backup();
final uploadResult = await upload(relativePath: Paths.bakName);
if (uploadResult != null) {
Loggers.app.warning('Upload iCloud backup failed: $uploadResult');
_logger.warning('Upload backup failed: $uploadResult');
} else {
Loggers.app.info('Upload iCloud backup success');
_logger.info('Upload backup success');
}
break;
case null:
Loggers.app.info('Skip iCloud sync');
_logger.info('Skip sync');
break;
}
}
Expand Down
69 changes: 43 additions & 26 deletions lib/core/utils/sync/webdav.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
import 'package:toolbox/data/model/app/backup.dart';
import 'package:toolbox/data/model/app/error.dart';
import 'package:toolbox/data/res/logger.dart';
import 'package:toolbox/data/res/path.dart';
import 'package:toolbox/data/res/store.dart';
// ignore: implementation_imports
Expand All @@ -16,13 +16,15 @@ abstract final class Webdav {
pwd: Stores.setting.webdavPwd.fetch(),
);

static final _logger = Logger('Webdav');

static Future<String?> test(String url, String user, String pwd) async {
final client = WebdavClient(url: url, user: user, pwd: pwd);
try {
await client.ping();
return null;
} catch (e, s) {
Loggers.app.warning('Webdav test failed', e, s);
_logger.warning('Test failed', e, s);
return e.toString();
}
}
Expand All @@ -37,7 +39,7 @@ abstract final class Webdav {
relativePath,
);
} catch (e, s) {
Loggers.app.warning('Webdav upload failed', e, s);
_logger.warning('Upload $relativePath failed', e, s);
return WebdavErr(type: WebdavErrType.generic, message: '$e');
}
return null;
Expand All @@ -47,7 +49,7 @@ abstract final class Webdav {
try {
await _client.remove(relativePath);
} catch (e, s) {
Loggers.app.warning('Webdav delete failed', e, s);
_logger.warning('Delete $relativePath failed', e, s);
return WebdavErr(type: WebdavErrType.generic, message: '$e');
}
return null;
Expand All @@ -62,8 +64,8 @@ abstract final class Webdav {
relativePath,
localPath ?? '${await Paths.doc}/$relativePath',
);
} catch (e, s) {
Loggers.app.warning('Webdav download failed', e, s);
} catch (e) {
_logger.warning('Download $relativePath failed');
return WebdavErr(type: WebdavErrType.generic, message: '$e');
}
return null;
Expand All @@ -74,34 +76,49 @@ abstract final class Webdav {
}

static Future<void> sync() async {
try {
final result = await download(relativePath: Paths.bakName);
if (result != null) {
Loggers.app.warning('Download backup failed: $result');
return;
}
} catch (e, s) {
Loggers.app.warning('Download backup failed', e, s);
final result = await download(relativePath: Paths.bakName);
if (result != null) {
await backup();
return;
}
final dlFile = await compute(
(message) async {
try {
final file = await File(message).readAsString();
final bak = Backup.fromJsonString(file);
return bak;
} catch (_) {
return null;
}
},
await Paths.bak,
);
if (dlFile == null) {
await backup();
return;
}
final dlFile = await File(await Paths.bak).readAsString();
final dlBak = await compute(Backup.fromJsonString, dlFile);
final restore = await dlBak.restore();
final restore = await dlFile.restore();
switch (restore) {
case true:
Loggers.app.info('Restore from iCloud (${dlBak.lastModTime}) success');
_logger.info('Restore from ${dlFile.lastModTime} success');
break;
case false:
await Backup.backup();
final uploadResult = await upload(relativePath: Paths.bakName);
if (uploadResult != null) {
Loggers.app.warning('Upload iCloud backup failed: $uploadResult');
} else {
Loggers.app.info('Upload iCloud backup success');
}
await backup();
break;
case null:
Loggers.app.info('Skip iCloud sync');
_logger.info('Skip sync');
break;
}
}

/// Create a local backup and upload it to WebDAV
static Future<void> backup() async {
await Backup.backup();
final uploadResult = await upload(relativePath: Paths.bakName);
if (uploadResult != null) {
_logger.warning('Upload failed: $uploadResult');
} else {
_logger.info('Upload success');
}
}
}
5 changes: 4 additions & 1 deletion lib/data/model/app/backup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class Backup {
return path;
}

/// - Return null if same time
/// - Return false if local is newer
/// - Return true if restore success
Future<bool?> restore({bool force = false}) async {
final curTime = Stores.lastModTime ?? 0;
final thisTime = lastModTime ?? 0;
Expand Down Expand Up @@ -108,7 +111,7 @@ class Backup {

Pros.reload();
RebuildNodes.app.rebuild();

return true;
}

Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:toolbox/core/channel/bg_run.dart';
import 'package:toolbox/core/utils/sync/icloud.dart';
import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/core/utils/sync/webdav.dart';
import 'package:toolbox/data/res/logger.dart';
import 'package:toolbox/data/res/provider.dart';
import 'package:toolbox/data/res/store.dart';
Expand Down Expand Up @@ -95,6 +96,7 @@ Future<void> initApp() async {
if (isIOS || isMacOS) {
if (Stores.setting.icloudSync.fetch()) ICloud.sync();
}
if (Stores.setting.webdavSync.fetch()) Webdav.sync();
}

void _setupProviders() {
Expand Down
6 changes: 3 additions & 3 deletions lib/view/page/backup.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class BackupPage extends StatelessWidget {
Widget _buildFile(BuildContext context) {
return CardX(
ExpandTile(
leading: const Icon(Icons.file_open, size: 19),
leading: const Icon(Icons.file_open),
title: Text(l10n.files),
initiallyExpanded: true,
children: [
Expand Down Expand Up @@ -139,7 +139,7 @@ class BackupPage extends StatelessWidget {
Widget _buildIcloud(BuildContext context) {
return CardX(
ExpandTile(
leading: const Icon(Icons.cloud, size: 19),
leading: const Icon(Icons.cloud),
title: const Text('iCloud'),
initiallyExpanded: true,
children: [
Expand Down Expand Up @@ -231,7 +231,7 @@ class BackupPage extends StatelessWidget {
Widget _buildWebdav(BuildContext context) {
return CardX(
ExpandTile(
leading: const Icon(Icons.storage, size: 19),
leading: const Icon(Icons.storage),
title: const Text('WebDAV'),
initiallyExpanded: !(isIOS || isMacOS),
children: [
Expand Down
Loading

0 comments on commit 03b9a46

Please sign in to comment.