From 03b9a46a4cbd4aebf9d33eb4d875f29e7abf6515 Mon Sep 17 00:00:00 2001 From: lollipopkit Date: Mon, 4 Dec 2023 15:11:12 +0800 Subject: [PATCH] opt.: `webdav sync` logic --- lib/core/utils/sync/icloud.dart | 20 ++++---- lib/core/utils/sync/webdav.dart | 69 +++++++++++++++---------- lib/data/model/app/backup.dart | 5 +- lib/main.dart | 2 + lib/view/page/backup.dart | 6 +-- pubspec.lock | 90 ++++++++++++++++++--------------- 6 files changed, 112 insertions(+), 80 deletions(-) diff --git a/lib/core/utils/sync/icloud.dart b/lib/core/utils/sync/icloud.dart index 2965bbbb3..caef29c98 100644 --- a/lib/core/utils/sync/icloud.dart +++ b/lib/core/utils/sync/icloud.dart @@ -3,9 +3,9 @@ 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'; @@ -13,6 +13,8 @@ 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], @@ -171,12 +173,12 @@ 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'); } } @@ -184,30 +186,30 @@ abstract final class ICloud { 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; } } diff --git a/lib/core/utils/sync/webdav.dart b/lib/core/utils/sync/webdav.dart index 418a552ff..3edde191f 100644 --- a/lib/core/utils/sync/webdav.dart +++ b/lib/core/utils/sync/webdav.dart @@ -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 @@ -16,13 +16,15 @@ abstract final class Webdav { pwd: Stores.setting.webdavPwd.fetch(), ); + static final _logger = Logger('Webdav'); + static Future 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(); } } @@ -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; @@ -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; @@ -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; @@ -74,34 +76,49 @@ abstract final class Webdav { } static Future 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 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'); + } + } } diff --git a/lib/data/model/app/backup.dart b/lib/data/model/app/backup.dart index d88d9ff20..0dc09ffae 100644 --- a/lib/data/model/app/backup.dart +++ b/lib/data/model/app/backup.dart @@ -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 restore({bool force = false}) async { final curTime = Stores.lastModTime ?? 0; final thisTime = lastModTime ?? 0; @@ -108,7 +111,7 @@ class Backup { Pros.reload(); RebuildNodes.app.rebuild(); - + return true; } diff --git a/lib/main.dart b/lib/main.dart index c234238b3..c50e50b6a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -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'; @@ -95,6 +96,7 @@ Future initApp() async { if (isIOS || isMacOS) { if (Stores.setting.icloudSync.fetch()) ICloud.sync(); } + if (Stores.setting.webdavSync.fetch()) Webdav.sync(); } void _setupProviders() { diff --git a/lib/view/page/backup.dart b/lib/view/page/backup.dart index 9c9d2bb4f..31051252a 100644 --- a/lib/view/page/backup.dart +++ b/lib/view/page/backup.dart @@ -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: [ @@ -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: [ @@ -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: [ diff --git a/pubspec.lock b/pubspec.lock index 5898d1e94..f28c14de2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -25,14 +25,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.0" + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" + url: "https://pub.dev" + source: hosted + version: "2.0.2" archive: dependency: "direct main" description: name: archive - sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.9" args: dependency: transitive description: @@ -93,10 +101,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: @@ -109,10 +117,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" build_runner_core: dependency: transitive description: @@ -133,10 +141,10 @@ packages: dependency: transitive description: name: built_value - sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e" + sha256: "69acb7007eb2a31dc901512bfe0f7b767168be34cb734835d54c070bfa74c1b2" url: "https://pub.dev" source: hosted - version: "8.7.0" + version: "8.8.0" characters: dependency: transitive description: @@ -182,10 +190,10 @@ packages: dependency: transitive description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: b2151ce26a06171005b379ecff6e08d34c470180ffe16b8e14b6d52be292b55f url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.8.0" code_text_field: dependency: "direct main" description: @@ -214,18 +222,18 @@ packages: dependency: "direct main" description: name: countly_flutter - sha256: "8e774b37b926523458dc7f36e4e4975671b4b2108cdfc284371c08b1bc2b475b" + sha256: "06260f1a15c2abfcb0b5c96dc986de640fae27607c6b0cfdba60d5c50cf2173e" url: "https://pub.dev" source: hosted - version: "23.8.3" + version: "23.8.4" cross_file: dependency: transitive description: name: cross_file - sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c" + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e url: "https://pub.dev" source: hosted - version: "0.3.3+6" + version: "0.3.3+8" crypto: dependency: transitive description: @@ -262,10 +270,10 @@ packages: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" dartssh2: dependency: "direct main" description: @@ -278,10 +286,10 @@ packages: dependency: "direct main" description: name: dio - sha256: "417e2a6f9d83ab396ec38ff4ea5da6c254da71e4db765ad737a42af6930140b7" + sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" url: "https://pub.dev" source: hosted - version: "5.3.3" + version: "5.4.0" dynamic_color: dependency: "direct main" description: @@ -376,10 +384,10 @@ packages: dependency: "direct dev" description: name: flutter_native_splash - sha256: d93394f22f73e810bda59e11ebe83329c5511d6460b6b7509c4e1f3c92d6d625 + sha256: c4d899312b36e7454bedfd0a4740275837b99e532d81c8477579d8183db1de6c url: "https://pub.dev" source: hosted - version: "2.3.5" + version: "2.3.6" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -754,10 +762,10 @@ packages: dependency: transitive description: name: petitparser - sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.0.2" pinenacl: dependency: transitive description: @@ -786,10 +794,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" pointycastle: dependency: transitive description: @@ -810,10 +818,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: @@ -906,10 +914,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: @@ -1063,10 +1071,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.1" url_launcher_linux: dependency: transitive description: @@ -1095,10 +1103,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.1" url_launcher_windows: dependency: transitive description: @@ -1111,10 +1119,10 @@ packages: dependency: transitive description: name: uuid - sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7 + sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.1" vector_math: dependency: transitive description: @@ -1169,7 +1177,7 @@ packages: description: path: "." ref: main - resolved-ref: "233b3ebaa01b7bb35a414c9cfd5e2933b3a008ba" + resolved-ref: "88de97ad783c624d81b0dbcc09927b10aabd5580" url: "https://github.com/lollipopkit/webdav_client" source: git version: "1.2.1" @@ -1177,10 +1185,10 @@ packages: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.1.1" xdg_directories: dependency: transitive description: @@ -1193,10 +1201,10 @@ packages: dependency: "direct main" description: name: xml - sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.4.2" + version: "6.5.0" xterm: dependency: "direct main" description: @@ -1223,5 +1231,5 @@ packages: source: hosted version: "0.0.6" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0"