From aac4b2773b965749b370a4eb446c460fcc3e65f3 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Sat, 9 Sep 2023 10:22:43 +0300 Subject: [PATCH] feat(backups): Show the snapshot creation reason --- assets/translations/en.json | 9 +- .../graphql_maps/schema/backups.graphql | 3 +- .../graphql_maps/schema/backups.graphql.dart | 27 +++ .../graphql_maps/schema/schema.graphql | 25 +++ .../graphql_maps/schema/schema.graphql.dart | 207 ++++++++++++++++++ lib/logic/models/backup.dart | 12 + lib/ui/pages/backups/snapshot_modal.dart | 12 + 7 files changed, 293 insertions(+), 2 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 99c8d209..1885fd73 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -220,7 +220,14 @@ "snapshot_modal_inplace_option_title": "Replace in place", "snapshot_modal_inplace_option_description": "Less free space needed, but more risk. Replaces current data with the snapshot data during the download.", "snapshot_modal_service_not_found": "This is a snapshot of a service you don't have on your server anymore. Usually this shouldn't happen, and we cannot do the automatic restore. You can still download the snapshot and restore it manually. Contact SelfPrivacy support if you need help.", - "restore_started": "Restore started, check the jobs list for the current status" + "restore_started": "Restore started, check the jobs list for the current status", + "snapshot_reason_title": "Creation reason", + "snapshot_reasons": { + "auto": "Created automatically", + "explicit": "Created by your explicit request", + "pre_restore": "Created as a precaution before risky restore", + "unknown": "Unknown" + } }, "storage": { "card_title": "Server Storage", diff --git a/lib/logic/api_maps/graphql_maps/schema/backups.graphql b/lib/logic/api_maps/graphql_maps/schema/backups.graphql index 9b60564c..410b7343 100644 --- a/lib/logic/api_maps/graphql_maps/schema/backups.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/backups.graphql @@ -20,6 +20,7 @@ query AllBackupSnapshots { displayName id } + reason } } } @@ -98,4 +99,4 @@ mutation ForgetSnapshot($snapshotId: String!) { ...basicMutationReturnFields } } -} \ No newline at end of file +} diff --git a/lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart index 7a814aac..9dcd9e91 100644 --- a/lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/backups.graphql.dart @@ -1486,6 +1486,13 @@ const documentNodeQueryAllBackupSnapshots = DocumentNode(definitions: [ ), ]), ), + FieldNode( + name: NameNode(value: 'reason'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), FieldNode( name: NameNode(value: '__typename'), alias: null, @@ -1801,6 +1808,7 @@ class Query$AllBackupSnapshots$backup$allSnapshots { required this.id, required this.createdAt, required this.service, + required this.reason, this.$__typename = 'SnapshotInfo', }); @@ -1809,12 +1817,14 @@ class Query$AllBackupSnapshots$backup$allSnapshots { final l$id = json['id']; final l$createdAt = json['createdAt']; final l$service = json['service']; + final l$reason = json['reason']; final l$$__typename = json['__typename']; return Query$AllBackupSnapshots$backup$allSnapshots( id: (l$id as String), createdAt: dateTimeFromJson(l$createdAt), service: Query$AllBackupSnapshots$backup$allSnapshots$service.fromJson( (l$service as Map)), + reason: fromJson$Enum$BackupReason((l$reason as String)), $__typename: (l$$__typename as String), ); } @@ -1825,6 +1835,8 @@ class Query$AllBackupSnapshots$backup$allSnapshots { final Query$AllBackupSnapshots$backup$allSnapshots$service service; + final Enum$BackupReason reason; + final String $__typename; Map toJson() { @@ -1835,6 +1847,8 @@ class Query$AllBackupSnapshots$backup$allSnapshots { _resultData['createdAt'] = dateTimeToJson(l$createdAt); final l$service = service; _resultData['service'] = l$service.toJson(); + final l$reason = reason; + _resultData['reason'] = toJson$Enum$BackupReason(l$reason); final l$$__typename = $__typename; _resultData['__typename'] = l$$__typename; return _resultData; @@ -1845,11 +1859,13 @@ class Query$AllBackupSnapshots$backup$allSnapshots { final l$id = id; final l$createdAt = createdAt; final l$service = service; + final l$reason = reason; final l$$__typename = $__typename; return Object.hashAll([ l$id, l$createdAt, l$service, + l$reason, l$$__typename, ]); } @@ -1878,6 +1894,11 @@ class Query$AllBackupSnapshots$backup$allSnapshots { if (l$service != lOther$service) { return false; } + final l$reason = reason; + final lOther$reason = other.reason; + if (l$reason != lOther$reason) { + return false; + } final l$$__typename = $__typename; final lOther$$__typename = other.$__typename; if (l$$__typename != lOther$$__typename) { @@ -1910,6 +1931,7 @@ abstract class CopyWith$Query$AllBackupSnapshots$backup$allSnapshots { String? id, DateTime? createdAt, Query$AllBackupSnapshots$backup$allSnapshots$service? service, + Enum$BackupReason? reason, String? $__typename, }); CopyWith$Query$AllBackupSnapshots$backup$allSnapshots$service @@ -1933,6 +1955,7 @@ class _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots Object? id = _undefined, Object? createdAt = _undefined, Object? service = _undefined, + Object? reason = _undefined, Object? $__typename = _undefined, }) => _then(Query$AllBackupSnapshots$backup$allSnapshots( @@ -1943,6 +1966,9 @@ class _CopyWithImpl$Query$AllBackupSnapshots$backup$allSnapshots service: service == _undefined || service == null ? _instance.service : (service as Query$AllBackupSnapshots$backup$allSnapshots$service), + reason: reason == _undefined || reason == null + ? _instance.reason + : (reason as Enum$BackupReason), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String), @@ -1965,6 +1991,7 @@ class _CopyWithStubImpl$Query$AllBackupSnapshots$backup$allSnapshots String? id, DateTime? createdAt, Query$AllBackupSnapshots$backup$allSnapshots$service? service, + Enum$BackupReason? reason, String? $__typename, }) => _res; diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql b/lib/logic/api_maps/graphql_maps/schema/schema.graphql index 368c2b90..cb154193 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql @@ -75,6 +75,22 @@ type AutoUpgradeSettingsMutationReturn implements MutationReturnInterface { allowReboot: Boolean! } +type AutobackupQuotas { + last: Int! + daily: Int! + weekly: Int! + monthly: Int! + yearly: Int! +} + +input AutobackupQuotasInput { + last: Int! + daily: Int! + weekly: Int! + monthly: Int! + yearly: Int! +} + type Backup { configuration: BackupConfiguration! allSnapshots: [SnapshotInfo!]! @@ -85,6 +101,7 @@ type BackupConfiguration { encryptionKey: String! isInitialized: Boolean! autobackupPeriod: Int + autobackupQuotas: AutobackupQuotas! locationName: String locationId: String } @@ -93,6 +110,7 @@ type BackupMutations { initializeRepository(repository: InitializeRepositoryInput!): GenericBackupConfigReturn! removeRepository: GenericBackupConfigReturn! setAutobackupPeriod(period: Int = null): GenericBackupConfigReturn! + setAutobackupQuotas(quotas: AutobackupQuotasInput!): GenericBackupConfigReturn! startBackup(serviceId: String!): GenericJobMutationReturn! restoreBackup(snapshotId: String!, strategy: RestoreStrategy! = DOWNLOAD_VERIFY_OVERWRITE): GenericJobMutationReturn! forgetSnapshot(snapshotId: String!): GenericMutationReturn! @@ -106,6 +124,12 @@ enum BackupProvider { FILE } +enum BackupReason { + EXPLICIT + AUTO + PRE_RESTORE +} + """Date with time (isoformat)""" scalar DateTime @@ -326,6 +350,7 @@ type SnapshotInfo { id: String! service: Service! createdAt: DateTime! + reason: BackupReason! } input SshMutationInput { diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart index 8a78a6f8..538a05ff 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart @@ -141,6 +141,185 @@ class _CopyWithStubImpl$Input$AutoUpgradeSettingsInput _res; } +class Input$AutobackupQuotasInput { + factory Input$AutobackupQuotasInput({ + required int last, + required int daily, + required int weekly, + required int monthly, + required int yearly, + }) => + Input$AutobackupQuotasInput._({ + r'last': last, + r'daily': daily, + r'weekly': weekly, + r'monthly': monthly, + r'yearly': yearly, + }); + + Input$AutobackupQuotasInput._(this._$data); + + factory Input$AutobackupQuotasInput.fromJson(Map data) { + final result$data = {}; + final l$last = data['last']; + result$data['last'] = (l$last as int); + final l$daily = data['daily']; + result$data['daily'] = (l$daily as int); + final l$weekly = data['weekly']; + result$data['weekly'] = (l$weekly as int); + final l$monthly = data['monthly']; + result$data['monthly'] = (l$monthly as int); + final l$yearly = data['yearly']; + result$data['yearly'] = (l$yearly as int); + return Input$AutobackupQuotasInput._(result$data); + } + + Map _$data; + + int get last => (_$data['last'] as int); + int get daily => (_$data['daily'] as int); + int get weekly => (_$data['weekly'] as int); + int get monthly => (_$data['monthly'] as int); + int get yearly => (_$data['yearly'] as int); + Map toJson() { + final result$data = {}; + final l$last = last; + result$data['last'] = l$last; + final l$daily = daily; + result$data['daily'] = l$daily; + final l$weekly = weekly; + result$data['weekly'] = l$weekly; + final l$monthly = monthly; + result$data['monthly'] = l$monthly; + final l$yearly = yearly; + result$data['yearly'] = l$yearly; + return result$data; + } + + CopyWith$Input$AutobackupQuotasInput + get copyWith => CopyWith$Input$AutobackupQuotasInput( + this, + (i) => i, + ); + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Input$AutobackupQuotasInput) || + runtimeType != other.runtimeType) { + return false; + } + final l$last = last; + final lOther$last = other.last; + if (l$last != lOther$last) { + return false; + } + final l$daily = daily; + final lOther$daily = other.daily; + if (l$daily != lOther$daily) { + return false; + } + final l$weekly = weekly; + final lOther$weekly = other.weekly; + if (l$weekly != lOther$weekly) { + return false; + } + final l$monthly = monthly; + final lOther$monthly = other.monthly; + if (l$monthly != lOther$monthly) { + return false; + } + final l$yearly = yearly; + final lOther$yearly = other.yearly; + if (l$yearly != lOther$yearly) { + return false; + } + return true; + } + + @override + int get hashCode { + final l$last = last; + final l$daily = daily; + final l$weekly = weekly; + final l$monthly = monthly; + final l$yearly = yearly; + return Object.hashAll([ + l$last, + l$daily, + l$weekly, + l$monthly, + l$yearly, + ]); + } +} + +abstract class CopyWith$Input$AutobackupQuotasInput { + factory CopyWith$Input$AutobackupQuotasInput( + Input$AutobackupQuotasInput instance, + TRes Function(Input$AutobackupQuotasInput) then, + ) = _CopyWithImpl$Input$AutobackupQuotasInput; + + factory CopyWith$Input$AutobackupQuotasInput.stub(TRes res) = + _CopyWithStubImpl$Input$AutobackupQuotasInput; + + TRes call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + }); +} + +class _CopyWithImpl$Input$AutobackupQuotasInput + implements CopyWith$Input$AutobackupQuotasInput { + _CopyWithImpl$Input$AutobackupQuotasInput( + this._instance, + this._then, + ); + + final Input$AutobackupQuotasInput _instance; + + final TRes Function(Input$AutobackupQuotasInput) _then; + + static const _undefined = {}; + + TRes call({ + Object? last = _undefined, + Object? daily = _undefined, + Object? weekly = _undefined, + Object? monthly = _undefined, + Object? yearly = _undefined, + }) => + _then(Input$AutobackupQuotasInput._({ + ..._instance._$data, + if (last != _undefined && last != null) 'last': (last as int), + if (daily != _undefined && daily != null) 'daily': (daily as int), + if (weekly != _undefined && weekly != null) 'weekly': (weekly as int), + if (monthly != _undefined && monthly != null) + 'monthly': (monthly as int), + if (yearly != _undefined && yearly != null) 'yearly': (yearly as int), + })); +} + +class _CopyWithStubImpl$Input$AutobackupQuotasInput + implements CopyWith$Input$AutobackupQuotasInput { + _CopyWithStubImpl$Input$AutobackupQuotasInput(this._res); + + TRes _res; + + call({ + int? last, + int? daily, + int? weekly, + int? monthly, + int? yearly, + }) => + _res; +} + class Input$InitializeRepositoryInput { factory Input$InitializeRepositoryInput({ required Enum$BackupProvider provider, @@ -1310,6 +1489,34 @@ Enum$BackupProvider fromJson$Enum$BackupProvider(String value) { } } +enum Enum$BackupReason { EXPLICIT, AUTO, PRE_RESTORE, $unknown } + +String toJson$Enum$BackupReason(Enum$BackupReason e) { + switch (e) { + case Enum$BackupReason.EXPLICIT: + return r'EXPLICIT'; + case Enum$BackupReason.AUTO: + return r'AUTO'; + case Enum$BackupReason.PRE_RESTORE: + return r'PRE_RESTORE'; + case Enum$BackupReason.$unknown: + return r'$unknown'; + } +} + +Enum$BackupReason fromJson$Enum$BackupReason(String value) { + switch (value) { + case r'EXPLICIT': + return Enum$BackupReason.EXPLICIT; + case r'AUTO': + return Enum$BackupReason.AUTO; + case r'PRE_RESTORE': + return Enum$BackupReason.PRE_RESTORE; + default: + return Enum$BackupReason.$unknown; + } +} + enum Enum$DnsProvider { CLOUDFLARE, DIGITALOCEAN, DESEC, $unknown } String toJson$Enum$DnsProvider(Enum$DnsProvider e) { diff --git a/lib/logic/models/backup.dart b/lib/logic/models/backup.dart index 2199e223..de5f9b44 100644 --- a/lib/logic/models/backup.dart +++ b/lib/logic/models/backup.dart @@ -11,6 +11,7 @@ class Backup { time: snapshot.createdAt, serviceId: snapshot.service.id, fallbackServiceName: snapshot.service.displayName, + reason: snapshot.reason, ); Backup({ @@ -18,6 +19,7 @@ class Backup { required this.id, required this.serviceId, required this.fallbackServiceName, + required this.reason, }); // Time of the backup @@ -26,6 +28,16 @@ class Backup { final String id; final String serviceId; final String fallbackServiceName; + final Enum$BackupReason reason; +} + +extension BackupReasonExtension on Enum$BackupReason { + String get displayName => switch (this) { + Enum$BackupReason.AUTO => 'backup.snapshot_reasons.auto', + Enum$BackupReason.EXPLICIT => 'backup.snapshot_reasons.explicit', + Enum$BackupReason.PRE_RESTORE => 'backup.snapshot_reasons.pre_restore', + Enum$BackupReason.$unknown => 'backup.snapshot_reasons.unknown', + }; } class BackupConfiguration { diff --git a/lib/ui/pages/backups/snapshot_modal.dart b/lib/ui/pages/backups/snapshot_modal.dart index 0f1bac3d..9d714c25 100644 --- a/lib/ui/pages/backups/snapshot_modal.dart +++ b/lib/ui/pages/backups/snapshot_modal.dart @@ -99,6 +99,18 @@ class _SnapshotModalState extends State { ), ), SnapshotIdListTile(snapshotId: widget.snapshot.id), + ListTile( + leading: Icon( + Icons.info_outline, + color: Theme.of(context).colorScheme.onSurface, + ), + title: Text( + 'backup.snapshot_reason_title'.tr(), + ), + subtitle: Text( + widget.snapshot.reason.displayName.tr(), + ), + ), if (service != null) Column( children: [