mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-13 20:23:16 +00:00
82dfdf04f9
Resolves issue [320](https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/issues/320). Co-authored-by: NaiJi <naijiworld@protonmail.com> Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/pulls/324 Reviewed-by: Inex Code <inex.code@selfprivacy.org>
259 lines
8.7 KiB
Dart
259 lines
8.7 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
import 'package:selfprivacy/config/get_it_config.dart';
|
|
import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
|
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
|
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
|
import 'package:selfprivacy/logic/models/backup.dart';
|
|
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
|
|
import 'package:selfprivacy/logic/models/initialize_repository_input.dart';
|
|
import 'package:selfprivacy/logic/models/service.dart';
|
|
|
|
part 'backups_state.dart';
|
|
|
|
class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
|
BackupsCubit(final ServerInstallationCubit serverInstallationCubit)
|
|
: super(
|
|
serverInstallationCubit,
|
|
const BackupsState(preventActions: true),
|
|
);
|
|
|
|
final ServerApi api = ServerApi();
|
|
final BackblazeApi backblaze = BackblazeApi();
|
|
|
|
@override
|
|
Future<void> load() async {
|
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
|
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
|
|
final BackupConfiguration? backupConfig =
|
|
await api.getBackupsConfiguration();
|
|
final List<Backup> backups = await api.getBackups();
|
|
backups.sort((final a, final b) => b.time.compareTo(a.time));
|
|
emit(
|
|
state.copyWith(
|
|
backblazeBucket: bucket,
|
|
isInitialized: backupConfig?.isInitialized,
|
|
autobackupPeriod: backupConfig?.autobackupPeriod ?? Duration.zero,
|
|
backups: backups,
|
|
preventActions: false,
|
|
refreshing: false,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> initializeBackups() async {
|
|
emit(state.copyWith(preventActions: true));
|
|
final String? encryptionKey =
|
|
(await api.getBackupsConfiguration())?.encryptionKey;
|
|
if (encryptionKey == null) {
|
|
getIt<NavigationService>()
|
|
.showSnackBar("Couldn't get encryption key from your server.");
|
|
emit(state.copyWith(preventActions: false));
|
|
return;
|
|
}
|
|
|
|
final BackblazeBucket bucket;
|
|
|
|
if (state.backblazeBucket == null) {
|
|
final String domain = serverInstallationCubit
|
|
.state.serverDomain!.domainName
|
|
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
|
final int serverId = serverInstallationCubit.state.serverDetails!.id;
|
|
String bucketName = 'selfprivacy-$domain-$serverId';
|
|
// If bucket name is too long, shorten it
|
|
if (bucketName.length > 49) {
|
|
bucketName = bucketName.substring(0, 49);
|
|
}
|
|
final String bucketId = await backblaze.createBucket(bucketName);
|
|
|
|
final BackblazeApplicationKey key = await backblaze.createKey(bucketId);
|
|
bucket = BackblazeBucket(
|
|
bucketId: bucketId,
|
|
bucketName: bucketName,
|
|
applicationKey: key.applicationKey,
|
|
applicationKeyId: key.applicationKeyId,
|
|
encryptionKey: encryptionKey,
|
|
);
|
|
|
|
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
|
|
emit(state.copyWith(backblazeBucket: bucket));
|
|
} else {
|
|
bucket = state.backblazeBucket!;
|
|
}
|
|
|
|
final GenericResult result = await api.initializeRepository(
|
|
InitializeRepositoryInput(
|
|
provider: BackupsProviderType.backblaze,
|
|
locationId: bucket.bucketId,
|
|
locationName: bucket.bucketName,
|
|
login: bucket.applicationKeyId,
|
|
password: bucket.applicationKey,
|
|
),
|
|
);
|
|
if (result.success == false) {
|
|
getIt<NavigationService>()
|
|
.showSnackBar(result.message ?? 'Unknown error');
|
|
emit(state.copyWith(preventActions: false));
|
|
return;
|
|
}
|
|
await updateBackups();
|
|
getIt<NavigationService>().showSnackBar(
|
|
'Backups repository is now initializing. It may take a while.',
|
|
);
|
|
|
|
emit(state.copyWith(preventActions: false));
|
|
}
|
|
|
|
Future<void> reuploadKey() async {
|
|
emit(state.copyWith(preventActions: true));
|
|
BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
|
|
if (bucket == null) {
|
|
emit(state.copyWith(isInitialized: false));
|
|
} else {
|
|
String login = bucket.applicationKeyId;
|
|
String password = bucket.applicationKey;
|
|
if (login.isEmpty || password.isEmpty) {
|
|
final BackblazeApplicationKey key =
|
|
await backblaze.createKey(bucket.bucketId);
|
|
login = key.applicationKeyId;
|
|
password = key.applicationKey;
|
|
bucket = BackblazeBucket(
|
|
bucketId: bucket.bucketId,
|
|
bucketName: bucket.bucketName,
|
|
encryptionKey: bucket.encryptionKey,
|
|
applicationKey: password,
|
|
applicationKeyId: login,
|
|
);
|
|
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
|
|
emit(state.copyWith(backblazeBucket: bucket));
|
|
}
|
|
final GenericResult result = await api.initializeRepository(
|
|
InitializeRepositoryInput(
|
|
provider: BackupsProviderType.backblaze,
|
|
locationId: bucket.bucketId,
|
|
locationName: bucket.bucketName,
|
|
login: login,
|
|
password: password,
|
|
),
|
|
);
|
|
if (result.success == false) {
|
|
getIt<NavigationService>()
|
|
.showSnackBar(result.message ?? 'Unknown error');
|
|
emit(state.copyWith(preventActions: false));
|
|
return;
|
|
} else {
|
|
emit(state.copyWith(preventActions: false));
|
|
getIt<NavigationService>().showSnackBar('backup.reuploaded_key'.tr());
|
|
await updateBackups();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Deprecated("we don't have states")
|
|
Duration refreshTimeFromState() => const Duration(seconds: 60);
|
|
|
|
Future<void> updateBackups({final bool useTimer = false}) async {
|
|
emit(state.copyWith(refreshing: true));
|
|
final backups = await api.getBackups();
|
|
backups.sort((final a, final b) => b.time.compareTo(a.time));
|
|
final backupConfig = await api.getBackupsConfiguration();
|
|
|
|
emit(
|
|
state.copyWith(
|
|
backups: backups,
|
|
refreshTimer: refreshTimeFromState(),
|
|
refreshing: false,
|
|
isInitialized: backupConfig?.isInitialized ?? false,
|
|
autobackupPeriod: backupConfig?.autobackupPeriod,
|
|
),
|
|
);
|
|
if (useTimer) {
|
|
Timer(state.refreshTimer, () => updateBackups(useTimer: true));
|
|
}
|
|
}
|
|
|
|
Future<void> forceUpdateBackups() async {
|
|
emit(state.copyWith(preventActions: true));
|
|
getIt<NavigationService>().showSnackBar('backup.refetching_list'.tr());
|
|
await api.forceBackupListReload();
|
|
emit(state.copyWith(preventActions: false));
|
|
}
|
|
|
|
Future<void> createMultipleBackups(final List<Service> services) async {
|
|
emit(state.copyWith(preventActions: true));
|
|
for (final service in services) {
|
|
await api.startBackup(service.id);
|
|
}
|
|
await updateBackups();
|
|
emit(state.copyWith(preventActions: false));
|
|
}
|
|
|
|
Future<void> createBackup(final String serviceId) async {
|
|
emit(state.copyWith(preventActions: true));
|
|
await api.startBackup(serviceId);
|
|
await updateBackups();
|
|
emit(state.copyWith(preventActions: false));
|
|
}
|
|
|
|
Future<void> restoreBackup(
|
|
final String backupId,
|
|
final BackupRestoreStrategy strategy,
|
|
) async {
|
|
emit(state.copyWith(preventActions: true));
|
|
await api.restoreBackup(backupId, strategy);
|
|
emit(state.copyWith(preventActions: false));
|
|
}
|
|
|
|
Future<void> setAutobackupPeriod(final Duration? period) async {
|
|
emit(state.copyWith(preventActions: true));
|
|
final result = await api.setAutobackupPeriod(period: period?.inMinutes);
|
|
if (result.success == false) {
|
|
getIt<NavigationService>()
|
|
.showSnackBar(result.message ?? 'Unknown error');
|
|
emit(state.copyWith(preventActions: false));
|
|
} else {
|
|
getIt<NavigationService>()
|
|
.showSnackBar('backup.autobackup_period_set'.tr());
|
|
emit(
|
|
state.copyWith(
|
|
preventActions: false,
|
|
autobackupPeriod: period ?? Duration.zero,
|
|
),
|
|
);
|
|
}
|
|
await updateBackups();
|
|
}
|
|
|
|
Future<void> forgetSnapshot(final String snapshotId) async {
|
|
final result = await api.forgetSnapshot(snapshotId);
|
|
if (!result.success) {
|
|
getIt<NavigationService>().showSnackBar('jobs.generic_error'.tr());
|
|
return;
|
|
}
|
|
|
|
if (result.data == false) {
|
|
getIt<NavigationService>()
|
|
.showSnackBar('backup.forget_snapshot_error'.tr());
|
|
}
|
|
|
|
// Optimistic update
|
|
final backups = state.backups;
|
|
final index =
|
|
backups.indexWhere((final snapshot) => snapshot.id == snapshotId);
|
|
if (index != -1) {
|
|
backups.removeAt(index);
|
|
emit(state.copyWith(backups: backups));
|
|
}
|
|
|
|
await updateBackups();
|
|
}
|
|
|
|
@override
|
|
void clear() async {
|
|
emit(const BackupsState());
|
|
}
|
|
}
|