opt.: backup restore

This commit is contained in:
lollipopkit
2024-01-25 20:00:40 +08:00
parent c910696735
commit 292a29a611
10 changed files with 176 additions and 91 deletions

View File

@@ -78,12 +78,30 @@ extension BoxX on Box {
}
extension StoreX on PersistentStore {
_StoreProperty<T> property<T>(String key, T defaultValue) {
return _StoreProperty<T>(box, key, defaultValue);
_StoreProperty<T> property<T>(
String key,
T defaultValue, {
bool updateLastModified = true,
}) {
return _StoreProperty<T>(
box,
key,
defaultValue,
updateLastModified: updateLastModified,
);
}
_StoreListProperty<T> listProperty<T>(String key, List<T> defaultValue) {
return _StoreListProperty<T>(box, key, defaultValue);
_StoreListProperty<T> listProperty<T>(
String key,
List<T> defaultValue, {
bool updateLastModified = true,
}) {
return _StoreListProperty<T>(
box,
key,
defaultValue,
updateLastModified: updateLastModified,
);
}
}
@@ -95,11 +113,17 @@ abstract class StorePropertyBase<T> {
}
class _StoreProperty<T> implements StorePropertyBase<T> {
_StoreProperty(this._box, this._key, this.defaultValue);
_StoreProperty(
this._box,
this._key,
this.defaultValue, {
this.updateLastModified = true,
});
final Box _box;
final String _key;
T defaultValue;
bool updateLastModified;
@override
ValueListenable<T> listenable() {
@@ -117,7 +141,7 @@ class _StoreProperty<T> implements StorePropertyBase<T> {
@override
Future<void> put(T value) {
_box.updateLastModified();
if (updateLastModified) _box.updateLastModified();
return _box.put(_key, value);
}
@@ -128,11 +152,17 @@ class _StoreProperty<T> implements StorePropertyBase<T> {
}
class _StoreListProperty<T> implements StorePropertyBase<List<T>> {
_StoreListProperty(this._box, this._key, this.defaultValue);
_StoreListProperty(
this._box,
this._key,
this.defaultValue, {
this.updateLastModified = true,
});
final Box _box;
final String _key;
List<T> defaultValue;
bool updateLastModified;
@override
ValueListenable<List<T>> listenable() {
@@ -152,6 +182,7 @@ class _StoreListProperty<T> implements StorePropertyBase<List<T>> {
@override
Future<void> put(List<T> value) {
if (updateLastModified) _box.updateLastModified();
return _box.put(_key, value);
}

View File

@@ -205,23 +205,13 @@ abstract final class ICloud {
}
final dlFile = await File(await Paths.bak).readAsString();
final dlBak = await Computer.shared.start(Backup.fromJsonString, dlFile);
final restore = await dlBak.restore();
switch (restore) {
case true:
_logger.info('Restore from ${dlBak.lastModTime} success');
break;
case false:
await Backup.backup();
final uploadResult = await upload(relativePath: Paths.bakName);
if (uploadResult != null) {
_logger.warning('Upload backup failed: $uploadResult');
} else {
_logger.info('Upload backup success');
}
break;
case null:
_logger.info('Skip sync');
break;
await dlBak.restore();
await Backup.backup();
final uploadResult = await upload(relativePath: Paths.bakName);
if (uploadResult != null) {
_logger.warning('Upload backup failed: $uploadResult');
} else {
_logger.info('Upload backup success');
}
}
}

View File

@@ -117,18 +117,8 @@ abstract final class Webdav {
await backup();
return;
}
final restore = await dlFile.restore();
switch (restore) {
case true:
_logger.info('Restore from ${dlFile.lastModTime} success');
break;
case false:
await backup();
break;
case null:
_logger.info('Skip sync');
break;
}
await dlFile.restore();
await backup();
}
/// Create a local backup and upload it to WebDAV

View File

@@ -86,34 +86,116 @@ class Backup {
/// - Return null if same time
/// - Return false if local is newer
/// - Return true if restore success
Future<bool?> restore({bool force = false}) async {
Future<void> restore({bool force = false}) async {
final curTime = Stores.lastModTime ?? 0;
final bakTime = lastModTime ?? 0;
if (curTime == bakTime && !force) {
return null;
final shouldRestore = force || curTime < bakTime;
// Settings
final nowSettingsKeys = Stores.setting.box.keys.toSet();
final bakSettingsKeys = settings.keys.toSet();
final newSettingsKeys = bakSettingsKeys.difference(nowSettingsKeys);
final delSettingsKeys = nowSettingsKeys.difference(bakSettingsKeys);
final updateSettingsKeys = nowSettingsKeys.intersection(bakSettingsKeys);
for (final k in newSettingsKeys) {
Stores.setting.box.put(k, settings[k]);
}
if (curTime > bakTime && !force) {
return false;
if (shouldRestore) {
for (final k in delSettingsKeys) {
Stores.setting.box.delete(k);
}
for (final k in updateSettingsKeys) {
Stores.setting.box.put(k, settings[k]);
}
}
for (final s in settings.keys) {
Stores.setting.box.put(s, settings[s]);
}
for (final s in snippets) {
// Snippets
final nowSnippets = Stores.snippet.fetch().toSet();
final bakSnippets = snippets.toSet();
final newSnippets = bakSnippets.difference(nowSnippets);
final delSnippets = nowSnippets.difference(bakSnippets);
final updateSnippets = nowSnippets.intersection(bakSnippets);
for (final s in newSnippets) {
Stores.snippet.put(s);
}
for (final s in spis) {
if (shouldRestore) {
for (final s in delSnippets) {
Stores.snippet.delete(s);
}
for (final s in updateSnippets) {
Stores.snippet.put(s);
}
}
// ServerPrivateInfo
final nowSpis = Stores.server.fetch().toSet();
final bakSpis = spis.toSet();
final newSpis = bakSpis.difference(nowSpis);
final delSpis = nowSpis.difference(bakSpis);
final updateSpis = nowSpis.intersection(bakSpis);
for (final s in newSpis) {
Stores.server.put(s);
}
for (final s in keys) {
if (shouldRestore) {
for (final s in delSpis) {
Stores.server.delete(s.id);
}
for (final s in updateSpis) {
Stores.server.put(s);
}
}
// PrivateKeyInfo
final nowKeys = Stores.key.fetch().toSet();
final bakKeys = keys.toSet();
final newKeys = bakKeys.difference(nowKeys);
final delKeys = nowKeys.difference(bakKeys);
final updateKeys = nowKeys.intersection(bakKeys);
for (final s in newKeys) {
Stores.key.put(s);
}
for (final s in history.keys) {
if (shouldRestore) {
for (final s in delKeys) {
Stores.key.delete(s);
}
for (final s in updateKeys) {
Stores.key.put(s);
}
}
// History
final nowHistory = Stores.history.box.keys.toSet();
final bakHistory = history.keys.toSet();
final newHistory = bakHistory.difference(nowHistory);
final delHistory = nowHistory.difference(bakHistory);
final updateHistory = nowHistory.intersection(bakHistory);
for (final s in newHistory) {
Stores.history.box.put(s, history[s]);
}
for (final k in container.keys) {
final val = container[k];
if (val != null && val is String && val.isNotEmpty) {
Stores.docker.put(k, val);
if (shouldRestore) {
for (final s in delHistory) {
Stores.history.box.delete(s);
}
for (final s in updateHistory) {
Stores.history.box.put(s, history[s]);
}
}
// Container
final nowContainer = Stores.docker.box.keys.toSet();
final bakContainer = container.keys.toSet();
final newContainer = bakContainer.difference(nowContainer);
final delContainer = nowContainer.difference(bakContainer);
final updateContainer = nowContainer.intersection(bakContainer);
for (final s in newContainer) {
Stores.docker.put(s, container[s]);
}
if (shouldRestore) {
for (final s in delContainer) {
Stores.docker.box.delete(s);
}
for (final s in updateContainer) {
Stores.docker.put(s, container[s]);
}
}
@@ -122,8 +204,6 @@ class Backup {
Pros.reload();
RebuildNodes.app.rebuild();
return true;
}
Backup.fromJsonString(String raw)

View File

@@ -2,9 +2,9 @@
class BuildData {
static const String name = "ServerBox";
static const int build = 717;
static const int build = 719;
static const String engine = "3.16.8";
static const String buildAt = "2024-01-22 16:31:50";
static const int modifications = 6;
static const String buildAt = "2024-01-23 22:32:15";
static const int modifications = 3;
static const int script = 36;
}

View File

@@ -201,10 +201,10 @@ class SettingStore extends PersistentStore {
late final showSuspendTip = property('showSuspendTip', true);
/// Webdav sync
late final webdavSync = property('webdavSync', false);
late final webdavUrl = property('webdavUrl', '');
late final webdavUser = property('webdavUser', '');
late final webdavPwd = property('webdavPwd', '');
late final webdavSync = property('webdavSync', false, updateLastModified: false);
late final webdavUrl = property('webdavUrl', '', updateLastModified: false);
late final webdavUser = property('webdavUser', '', updateLastModified: false);
late final webdavPwd = property('webdavPwd', '', updateLastModified: false);
/// Whether collapse UI items by default
late final collapseUIDefault = property('collapseUIDefault', true);