diff --git a/.vscode/launch.json b/.vscode/launch.json index 2b83ac77..aea512cb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -31,7 +31,7 @@ ] }, { - "name": "debug (production)", + "name": "debug (production flavor)", "request": "launch", "type": "dart", "args": [ @@ -41,7 +41,7 @@ }, { - "name": "debug (nightly)", + "name": "debug (nightly flavor)", "request": "launch", "type": "dart", "args": [ diff --git a/assets/translations/en.json b/assets/translations/en.json index 9f672acd..234e0702 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -46,12 +46,10 @@ }, "console_page": { "title": "Console", - "waiting": "Waiting for initialization…", "copy": "Copy", "copy_raw": "Raw response", "history_empty": "No data yet", "error": "Error", - "log": "Log", "rest_api_request": "Rest API Request", "rest_api_response": "Rest API Response", "graphql_request": "GraphQL Request", diff --git a/lib/config/config.dart b/lib/config/config.dart new file mode 100644 index 00000000..0b76d915 --- /dev/null +++ b/lib/config/config.dart @@ -0,0 +1,18 @@ +import 'package:flutter/foundation.dart'; + +/// internal app configuration +const config = InternalConfig( + shouldDebugPrint: kDebugMode, +); + +class InternalConfig { + const InternalConfig({ + required this.shouldDebugPrint, + }); + + final bool shouldDebugPrint; + + // example of other possible fields + // final String appVersion; + // +} diff --git a/lib/config/get_it_config.dart b/lib/config/get_it_config.dart index 5f6d55cb..d570e005 100644 --- a/lib/config/get_it_config.dart +++ b/lib/config/get_it_config.dart @@ -3,6 +3,7 @@ import 'package:selfprivacy/logic/get_it/api_config.dart'; import 'package:selfprivacy/logic/get_it/api_connection_repository.dart'; import 'package:selfprivacy/logic/get_it/console_model.dart'; import 'package:selfprivacy/logic/get_it/navigation.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; export 'package:selfprivacy/logic/get_it/api_config.dart'; export 'package:selfprivacy/logic/get_it/api_connection_repository.dart'; @@ -14,9 +15,10 @@ final GetIt getIt = GetIt.instance; Future getItSetup() async { getIt.registerSingleton(NavigationService()); getIt.registerSingleton(ConsoleModel()); + getIt.registerSingleton(ResourcesModel()..init()); + getIt.registerSingleton(WizardDataModel()..init()); final apiConfigModel = ApiConfigModel(); - await apiConfigModel.init(); getIt.registerSingleton(apiConfigModel); getIt.registerSingleton( diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 85a86583..a5054674 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -1,75 +1,227 @@ -import 'dart:convert'; - -import 'package:flutter/services.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; +import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart'; +import 'package:selfprivacy/logic/models/hive/server.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart'; +import 'package:selfprivacy/utils/app_logger.dart'; import 'package:selfprivacy/utils/platform_adapter.dart'; +import 'package:selfprivacy/utils/secure_storage.dart'; class HiveConfig { + static final log = const AppLogger(name: 'hive_config').log; + + /// bump on schema changes + static const version = 2; + static Future init() async { final String? storagePath = PlatformAdapter.storagePath; - print('HiveConfig: Custom storage path: $storagePath'); + log('set custom storage path to: "$storagePath"'); + await Hive.initFlutter(storagePath); - Hive.registerAdapter(UserAdapter()); - Hive.registerAdapter(ServerHostingDetailsAdapter()); - Hive.registerAdapter(ServerDomainAdapter()); - Hive.registerAdapter(BackupsCredentialAdapter()); - Hive.registerAdapter(BackblazeBucketAdapter()); - Hive.registerAdapter(ServerProviderVolumeAdapter()); - Hive.registerAdapter(UserTypeAdapter()); - Hive.registerAdapter(DnsProviderTypeAdapter()); - Hive.registerAdapter(ServerProviderTypeAdapter()); - Hive.registerAdapter(BackupsProviderTypeAdapter()); - await Hive.openBox(BNames.appSettingsBox); + registerAdapters(); + await decryptBoxes(); + await performMigrations(); + } + static void registerAdapters() { try { - final HiveAesCipher cipher = HiveAesCipher( - await getEncryptedKey(BNames.serverInstallationEncryptionKey), + Hive.registerAdapter(UserAdapter()); + Hive.registerAdapter(ServerHostingDetailsAdapter()); + Hive.registerAdapter(ServerDomainAdapter()); + Hive.registerAdapter(BackupsCredentialAdapter()); + Hive.registerAdapter(ServerProviderVolumeAdapter()); + Hive.registerAdapter(BackblazeBucketAdapter()); + Hive.registerAdapter(ServerProviderCredentialAdapter()); + Hive.registerAdapter(DnsProviderCredentialAdapter()); + Hive.registerAdapter(ServerAdapter()); + Hive.registerAdapter(DnsProviderTypeAdapter()); + Hive.registerAdapter(ServerProviderTypeAdapter()); + Hive.registerAdapter(UserTypeAdapter()); + Hive.registerAdapter(BackupsProviderTypeAdapter()); + Hive.registerAdapter(ServerInstallationWizardDataAdapter()); + log('successfully registered every adapter'); + } catch (error, stackTrace) { + log( + 'error registering adapters', + error: error, + stackTrace: stackTrace, ); + rethrow; + } + } - await Hive.openBox(BNames.usersDeprecated); - await Hive.openBox(BNames.usersBox, encryptionCipher: cipher); + static Future getCipher() async { + List? key = await SecureStorage.getKey(); + if (key == null) { + key = Hive.generateSecureKey(); + await SecureStorage.setKey(key); + } + return HiveAesCipher(key); + } - final Box deprecatedUsers = Hive.box(BNames.usersDeprecated); - if (deprecatedUsers.isNotEmpty) { - final Box users = Hive.box(BNames.usersBox); - await users.addAll(deprecatedUsers.values.toList()); - await deprecatedUsers.clear(); - } + static Future decryptBoxes() async { + try { + // load encrypted boxes into memory + final HiveAesCipher cipher = await getCipher(); await Hive.openBox( BNames.serverInstallationBox, encryptionCipher: cipher, ); - } on PlatformException catch (e) { - print('HiveConfig: Error while opening boxes: $e'); + await Hive.openBox( + BNames.resourcesBox, + encryptionCipher: cipher, + ); + await Hive.openBox( + BNames.wizardDataBox, + encryptionCipher: cipher, + ); + log('successfully decrypted boxes'); + } catch (error, stackTrace) { + log( + 'error initializing encrypted boxes', + error: error, + stackTrace: stackTrace, + ); rethrow; } } - static Future getEncryptedKey(final String encKey) async { - const FlutterSecureStorage secureStorage = FlutterSecureStorage(); + // migrations + + static Future performMigrations() async { try { - final bool hasEncryptionKey = - await secureStorage.containsKey(key: encKey); - if (!hasEncryptionKey) { - final List key = Hive.generateSecureKey(); - await secureStorage.write(key: encKey, value: base64UrlEncode(key)); + // perform migration check + final localSettingsBox = await Hive.openBox(BNames.appSettingsBox); + + // if it is an initial app launch, we do not need to perform any migrations + final savedVersion = localSettingsBox.isEmpty + ? version + // if box was initialized, but database version was not introduced in + // it yet, it means that we have initial value + : await localSettingsBox.get(BNames.databaseVersion, defaultValue: 1); + + /// launch migrations based on version + if (savedVersion < version) { + if (savedVersion < 2) { + await migrateFrom1To2(); + } + + /// add new migrations here, like: + /// if (version < 3) {...}, etc. } - final String? string = await secureStorage.read(key: encKey); - return base64Url.decode(string!); - } on PlatformException catch (e) { - print('HiveConfig: Error while getting encryption key: $e'); + /// update saved version after successfull migrations + await localSettingsBox.put(BNames.databaseVersion, version); + } catch (error, stackTrace) { + log( + 'error running db migrations', + error: error, + stackTrace: stackTrace, + ); rethrow; } } + + /// introduce and populate resourcesBox + static Future migrateFrom1To2() async { + final Box resourcesBox = Hive.box(BNames.resourcesBox); + if (resourcesBox.isEmpty) { + final Box serverInstallationBox = Hive.box(BNames.serverInstallationBox); + + final ServerHostingDetails? serverDetails = + serverInstallationBox.get(BNames.serverDetails); + + // move server provider config + + final ServerProviderType? serverProvider = + serverInstallationBox.get(BNames.serverProvider) ?? + serverDetails?.provider; + final String? serverProviderKey = + serverInstallationBox.get(BNames.hetznerKey); + + if (serverProviderKey != null && serverProvider.isSpecified) { + final ServerProviderCredential serverProviderCredential = + ServerProviderCredential( + tokenId: null, + token: serverProviderKey, + provider: serverProvider!, + associatedServerIds: [if (serverDetails != null) serverDetails.id], + ); + + await resourcesBox.put( + BNames.serverProviderTokens, + [serverProviderCredential], + ); + } + + final String? serverLocation = + serverInstallationBox.get(BNames.serverLocation); + final String? serverType = + serverInstallationBox.get(BNames.serverTypeIdentifier); + final ServerDomain? serverDomain = + serverInstallationBox.get(BNames.serverDomain); + + if (serverDetails != null && serverDomain != null) { + await resourcesBox.put( + BNames.servers, + [ + Server( + domain: serverDomain, + hostingDetails: serverDetails.copyWith( + serverLocation: serverLocation, + serverType: serverType, + ), + ), + ], + ); + } + + // move dns config + final String? dnsProviderKey = + serverInstallationBox.get(BNames.cloudFlareKey); + final DnsProviderType? dnsProvider = + serverInstallationBox.get(BNames.dnsProvider) ?? + serverDomain?.provider; + + if (dnsProviderKey != null && dnsProvider.isSpecified) { + final DnsProviderCredential dnsProviderCredential = + DnsProviderCredential( + tokenId: null, + token: dnsProviderKey, + provider: dnsProvider!, + associatedDomainNames: [ + if (serverDomain != null) serverDomain.domainName, + ], + ); + + await resourcesBox + .put(BNames.dnsProviderTokens, [dnsProviderCredential]); + } + + // move backblaze (backups) config + final BackupsCredential? backblazeCredential = + serverInstallationBox.get(BNames.backblazeCredential); + final BackblazeBucket? backblazeBucket = + serverInstallationBox.get(BNames.backblazeBucket); + + if (backblazeCredential != null) { + await resourcesBox + .put(BNames.backupsProviderTokens, [backblazeCredential]); + } + + if (backblazeBucket != null) { + await resourcesBox.put(BNames.backblazeBucket, backblazeBucket); + } + } + log('successfully migrated db from 1 to 2 version'); + } } /// Mappings for the different boxes and their keys @@ -77,6 +229,9 @@ class BNames { /// App settings box. Contains app settings like [darkThemeModeOn], [shouldShowOnboarding] static String appSettingsBox = 'appSettings'; + /// An integer with last saved version of the database + static String databaseVersion = 'databaseVersion'; + /// A boolean field of [appSettingsBox] box. static String darkThemeModeOn = 'isDarkModeOn'; @@ -89,9 +244,6 @@ class BNames { /// A string field static String appLocale = 'appLocale'; - /// Encryption key to decrypt [serverInstallationBox] and [usersBox] box. - static String serverInstallationEncryptionKey = 'key'; - /// Server installation box. Contains server details and provider tokens. static String serverInstallationBox = 'appConfig'; @@ -122,7 +274,7 @@ class BNames { /// A String field of [serverInstallationBox] box. static String cloudFlareKey = 'cloudFlareKey'; - /// A String field of [serverTypeIdentifier] box. + /// A String field of [serverInstallationBox] box. static String serverTypeIdentifier = 'serverTypeIdentifier'; /// A [User] field of [serverInstallationBox] box. @@ -149,9 +301,24 @@ class BNames { /// A boolean field of [serverInstallationBox] box. static String isRecoveringServer = 'isRecoveringServer'; - /// Deprecated users box as it is unencrypted - static String usersDeprecated = 'users'; + /// Resources and provider tokens box, + static String resourcesBox = 'resourcesBox'; - /// Box with users - static String usersBox = 'usersEncrypted'; + /// Server Provider Tokens of [resourcesBox] box. + static String serverProviderTokens = 'serverProviderTokens'; + + /// DNS Provider Tokens of [resourcesBox] box. + static String dnsProviderTokens = 'dnsProviderTokens'; + + /// Backups Provider Tokens of [resourcesBox] box. + static String backupsProviderTokens = 'backupsProviderTokens'; + + /// Servers of [resourcesBox] box. + static String servers = 'servers'; + + /// Wizard data box + static String wizardDataBox = 'wizardDataBox'; + + /// Server installation wizard data of [wizardDataBox] box. + static String serverInstallationWizardData = 'serverInstallationWizardData'; } diff --git a/lib/logic/api_maps/graphql_maps/graphql_api_map.dart b/lib/logic/api_maps/graphql_maps/graphql_api_map.dart index d2823a56..75183cd1 100644 --- a/lib/logic/api_maps/graphql_maps/graphql_api_map.dart +++ b/lib/logic/api_maps/graphql_maps/graphql_api_map.dart @@ -5,6 +5,7 @@ import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:http/io_client.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/console_log.dart'; void _addConsoleLog(final ConsoleLog message) => @@ -120,7 +121,7 @@ abstract class GraphQLApiMap { String get _token { String token = ''; - final serverDetails = getIt().serverDetails; + final serverDetails = getIt().serverDetails; if (serverDetails != null) { token = serverDetails.apiToken; } diff --git a/lib/logic/api_maps/graphql_maps/server_api/server_api.dart b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart index e2efb41b..ac384c01 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart @@ -9,6 +9,7 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphq import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/backup.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; @@ -57,7 +58,7 @@ class ServerApi extends GraphQLApiMap String customToken; @override String? get rootAddress => - overrideDomain ?? getIt().serverDomain?.domainName; + overrideDomain ?? getIt().serverDomain?.domainName; String? overrideDomain; Future getApiVersion() async { diff --git a/lib/logic/api_maps/rest_maps/backblaze.dart b/lib/logic/api_maps/rest_maps/backblaze.dart index 65f17463..37d87dd8 100644 --- a/lib/logic/api_maps/rest_maps/backblaze.dart +++ b/lib/logic/api_maps/rest_maps/backblaze.dart @@ -4,6 +4,7 @@ import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/backup.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; @@ -39,7 +40,7 @@ class BackblazeApi extends RestApiMap { ); if (isWithToken) { final BackupsCredential? backblazeCredential = - getIt().backblazeCredential; + getIt().backblazeCredential; final String token = backblazeCredential!.applicationKey; options.headers = {'Authorization': 'Basic $token'}; } @@ -59,7 +60,7 @@ class BackblazeApi extends RestApiMap { Future getAuthorizationToken() async { final Dio client = await getClient(); final BackupsCredential? backblazeCredential = - getIt().backblazeCredential; + getIt().backblazeCredential; if (backblazeCredential == null) { throw Exception('Backblaze credential is null'); } @@ -124,7 +125,7 @@ class BackblazeApi extends RestApiMap { Future createBucket(final String bucketName) async { final BackblazeApiAuth auth = await getAuthorizationToken(); final BackupsCredential? backblazeCredential = - getIt().backblazeCredential; + getIt().backblazeCredential; final Dio client = await getClient(); client.options.baseUrl = auth.apiUrl; final Response response = await client.post( @@ -161,7 +162,7 @@ class BackblazeApi extends RestApiMap { final Response response = await client.post( '$apiPrefix/b2_create_key', data: { - 'accountId': getIt().backblazeCredential!.keyId, + 'accountId': getIt().backblazeCredential!.keyId, 'bucketId': bucketId, 'capabilities': ['listBuckets', 'listFiles', 'readFiles', 'writeFiles'], 'keyName': 'selfprivacy-restricted-server-key', @@ -192,7 +193,7 @@ class BackblazeApi extends RestApiMap { final Response response = await client.get( '$apiPrefix/b2_list_buckets', queryParameters: { - 'accountId': getIt().backblazeCredential!.keyId, + 'accountId': getIt().backblazeCredential!.keyId, }, options: Options( headers: {'Authorization': auth.authorizationToken}, diff --git a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart index fa325ef5..aa5bf88e 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_api.dart @@ -4,6 +4,7 @@ import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/json/dns_providers/cloudflare_dns_info.dart'; class CloudflareApi extends RestApiMap { @@ -27,7 +28,7 @@ class CloudflareApi extends RestApiMap { responseType: ResponseType.json, ); if (isWithToken) { - final String? token = getIt().dnsProviderKey; + final String? token = getIt().dnsProviderKey; assert(token != null); options.headers = {'Authorization': 'Bearer $token'}; } diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart index 2bc15c22..1584e819 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart @@ -4,6 +4,7 @@ import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/json/dns_providers/desec_dns_info.dart'; class DesecApi extends RestApiMap { @@ -27,7 +28,7 @@ class DesecApi extends RestApiMap { responseType: ResponseType.json, ); if (isWithToken) { - final String? token = getIt().dnsProviderKey; + final String? token = getIt().dnsProviderKey; assert(token != null); options.headers = {'Authorization': 'Token $token'}; } diff --git a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart index 10da51e3..0a2cea53 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart @@ -4,6 +4,7 @@ import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/json/dns_providers/digital_ocean_dns_info.dart'; class DigitalOceanDnsApi extends RestApiMap { @@ -27,7 +28,7 @@ class DigitalOceanDnsApi extends RestApiMap { responseType: ResponseType.json, ); if (isWithToken) { - final String? token = getIt().dnsProviderKey; + final String? token = getIt().dnsProviderKey; assert(token != null); options.headers = {'Authorization': 'Bearer $token'}; } diff --git a/lib/logic/api_maps/rest_maps/rest_api_map.dart b/lib/logic/api_maps/rest_maps/rest_api_map.dart index 5426248f..3e06a803 100644 --- a/lib/logic/api_maps/rest_maps/rest_api_map.dart +++ b/lib/logic/api_maps/rest_maps/rest_api_map.dart @@ -1,19 +1,25 @@ import 'dart:async'; import 'dart:convert'; -import 'dart:developer'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; +import 'package:flutter/foundation.dart'; import 'package:pretty_dio_logger/pretty_dio_logger.dart'; +import 'package:selfprivacy/config/config.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/models/console_log.dart'; +import 'package:selfprivacy/utils/app_logger.dart'; abstract class RestApiMap { + static final log = const AppLogger(name: 'rest_api_map').log; + Future getClient({final BaseOptions? customOptions}) async { final Dio dio = Dio(customOptions ?? (await options)); - if (hasLogger) { - dio.interceptors.add(PrettyDioLogger()); + if (hasLogger && config.shouldDebugPrint) { + dio.interceptors.add( + PrettyDioLogger(logPrint: (final object) => debugPrint('$object')), + ); } dio.interceptors.add(ConsoleInterceptor()); dio.httpClientAdapter = IOHttpClientAdapter( @@ -28,14 +34,13 @@ abstract class RestApiMap { dio.interceptors.add( InterceptorsWrapper( - onError: (final DioException e, final ErrorInterceptorHandler handler) { - print(e.requestOptions.path); - print(e.requestOptions.data); + onError: ( + final DioException exception, + final ErrorInterceptorHandler handler, + ) { + log('got dio exception:', error: exception); - print(e.message); - print(e.response); - - return handler.next(e); + return handler.next(exception); }, ), ); @@ -103,7 +108,6 @@ class ConsoleInterceptor extends InterceptorsWrapper { final ErrorInterceptorHandler handler, ) async { final Response? response = err.response; - log(err.toString()); addConsoleLog( ManualConsoleLog.warning( diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart index 26f1cc8b..d9ec1e5b 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart @@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/json/digital_ocean_server_info.dart'; import 'package:selfprivacy/utils/password_generator.dart'; @@ -30,7 +31,7 @@ class DigitalOceanApi extends RestApiMap { responseType: ResponseType.json, ); if (isWithToken) { - final String? token = getIt().serverProviderKey; + final String? token = getIt().serverProviderKey; assert(token != null); options.headers = {'Authorization': 'Bearer $token'}; } diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart index 84e65cd7..ef731d80 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart @@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; import 'package:selfprivacy/logic/api_maps/tls_options.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; @@ -31,7 +32,7 @@ class HetznerApi extends RestApiMap { responseType: ResponseType.json, ); if (isWithToken) { - final String? token = getIt().serverProviderKey; + final String? token = getIt().serverProviderKey; assert(token != null); options.headers = {'Authorization': 'Bearer $token'}; } diff --git a/lib/logic/bloc/backups/backups_bloc.dart b/lib/logic/bloc/backups/backups_bloc.dart index 5c0e9e64..4e8930a6 100644 --- a/lib/logic/bloc/backups/backups_bloc.dart +++ b/lib/logic/bloc/backups/backups_bloc.dart @@ -6,6 +6,7 @@ import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/backup.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; @@ -108,7 +109,7 @@ class BackupsBloc extends Bloc { final BackupsServerLoaded event, final Emitter emit, ) async { - BackblazeBucket? bucket = getIt().backblazeBucket; + BackblazeBucket? bucket = getIt().backblazeBucket; final backups = getIt().apiData.backups; final backupConfig = getIt().apiData.backupConfig; if (backupConfig.data == null || backups.data == null) { @@ -227,7 +228,7 @@ class BackupsBloc extends Bloc { emit(BackupsUnititialized()); return; } - final BackblazeBucket? bucket = getIt().backblazeBucket; + final BackblazeBucket? bucket = getIt().backblazeBucket; emit( BackupsInitialized( backblazeBucket: bucket, diff --git a/lib/logic/cubit/metrics/metrics_repository.dart b/lib/logic/cubit/metrics/metrics_repository.dart index 0c6a82ef..561d75ef 100644 --- a/lib/logic/cubit/metrics/metrics_repository.dart +++ b/lib/logic/cubit/metrics/metrics_repository.dart @@ -2,6 +2,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/providers/providers_controller.dart'; class MetricsLoadException implements Exception { @@ -30,7 +31,7 @@ class MetricsRepository { break; } - final serverId = getIt().serverDetails!.id; + final serverId = getIt().serverDetails!.id; final result = await ProvidersController.currentServerProvider!.getMetrics( serverId, start, diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart index d6e548f8..09b9e845 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/server_metadata.dart'; import 'package:selfprivacy/logic/models/ssh_settings.dart'; @@ -43,7 +44,7 @@ class ServerDetailsCubit final serverProviderApi = ProvidersController.currentServerProvider; final dnsProviderApi = ProvidersController.currentDnsProvider; if (serverProviderApi != null && dnsProviderApi != null) { - final serverId = getIt().serverDetails?.id ?? 0; + final serverId = getIt().serverDetails?.id ?? 0; final metadataResult = await serverProviderApi.getMetadata(serverId); metadataResult.data.add( ServerMetadataEntity( @@ -60,7 +61,7 @@ class ServerDetailsCubit } void check() async { - final bool isReadyToCheck = getIt().serverDetails != null; + final bool isReadyToCheck = getIt().serverDetails != null; try { if (isReadyToCheck) { emit(const ServerDetailsLoading()); diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 66e9e2f2..4f1a52f3 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -15,6 +15,7 @@ import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart'; import 'package:selfprivacy/logic/models/launch_installation_data.dart'; import 'package:selfprivacy/logic/models/price.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart'; @@ -222,12 +223,14 @@ class ServerInstallationCubit extends Cubit { provider: BackupsProviderType.backblaze, ); final BackblazeBucket? bucket; - await repository.saveBackblazeKey(backblazeCredential); + await repository.saveBackupsCredential(backblazeCredential); if (state is ServerInstallationRecovery) { final configuration = await ServerApi( customToken: (state as ServerInstallationRecovery).serverDetails!.apiToken, isWithToken: true, + overrideDomain: + (state as ServerInstallationRecovery).serverDomain!.domainName, ).getBackupsConfiguration(); if (configuration != null) { try { @@ -401,7 +404,7 @@ class ServerInstallationCubit extends Cubit { ); timer = Timer(pauseDuration, () async { final ServerHostingDetails serverDetails = await repository.restart(); - await repository.saveIsServerResetedFirstTime(true); + await repository.saveIsServerRebootedFirstTime(true); await repository.saveServerDetails(serverDetails); final ServerInstallationNotFinished newState = dataState.copyWith( @@ -442,7 +445,7 @@ class ServerInstallationCubit extends Cubit { ); timer = Timer(pauseDuration, () async { final ServerHostingDetails serverDetails = await repository.restart(); - await repository.saveIsServerResetedSecondTime(true); + await repository.saveIsServerRebootedSecondTime(true); await repository.saveServerDetails(serverDetails); final ServerInstallationNotFinished newState = dataState.copyWith( @@ -577,10 +580,12 @@ class ServerInstallationCubit extends Cubit { final ServerProviderType serverProvider = await ServerApi( customToken: serverDetails.apiToken, isWithToken: true, + overrideDomain: serverDomain.domainName, ).getServerProviderType(); final dnsProvider = await ServerApi( customToken: serverDetails.apiToken, isWithToken: true, + overrideDomain: serverDomain.domainName, ).getDnsProviderType(); if (serverProvider == ServerProviderType.unknown || dnsProvider == DnsProviderType.unknown) { @@ -762,6 +767,7 @@ class ServerInstallationCubit extends Cubit { final dnsProviderType = await ServerApi( customToken: dataState.serverDetails!.apiToken, isWithToken: true, + overrideDomain: serverDomain.domainName, ).getDnsProviderType(); await repository.saveDomain( ServerDomain( @@ -769,6 +775,7 @@ class ServerInstallationCubit extends Cubit { provider: dnsProviderType, ), ); + await repository.setDnsApiToken(token); emit( dataState.copyWith( serverDomain: ServerDomain( @@ -785,21 +792,18 @@ class ServerInstallationCubit extends Cubit { final BackupsCredential backblazeCredential, ) async { await repository.saveIsServerStarted(true); - await repository.saveIsServerResetedFirstTime(true); - await repository.saveIsServerResetedSecondTime(true); - await repository.saveHasFinalChecked(true); + await repository.saveIsServerRebootedFirstTime(true); + await repository.saveIsServerRebootedSecondTime(true); await repository.saveIsRecoveringServer(false); final serverType = await ProvidersController.currentServerProvider! .getServerType(state.serverDetails!.id); await repository.saveServerType(serverType.data!); await ProvidersController.currentServerProvider! .trySetServerLocation(serverType.data!.location.identifier); - final User mainUser = await repository.getMainUser(); - await repository.saveRootUser(mainUser); + await repository.saveHasFinalChecked(true); final ServerInstallationRecovery updatedState = (state as ServerInstallationRecovery).copyWith( backblazeCredential: backblazeCredential, - rootUser: mainUser, serverTypeIdentificator: serverType.data!.identifier, ); emit(updatedState.finish()); diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index de8a426d..995e5922 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -9,10 +9,15 @@ import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/api_maps/tls_options.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; +import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart'; +import 'package:selfprivacy/logic/models/hive/server.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart'; import 'package:selfprivacy/logic/models/json/device_token.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart'; @@ -34,22 +39,25 @@ class ServerAuthorizationException implements Exception { class ServerInstallationRepository { Box box = Hive.box(BNames.serverInstallationBox); - Box usersBox = Hive.box(BNames.usersBox); Future load() async { - final String? providerApiToken = getIt().serverProviderKey; - final String? location = getIt().serverLocation; - final String? dnsApiToken = getIt().dnsProviderKey; - final String? serverTypeIdentificator = getIt().serverType; - final ServerDomain? serverDomain = getIt().serverDomain; - final DnsProviderType? dnsProvider = getIt().dnsProvider; + final ServerInstallationWizardData? wizardData = + getIt().serverInstallation; + final List servers = getIt().servers; + final String? providerApiToken = getIt().serverProviderKey; + final String? location = getIt().serverLocation; + final String? dnsApiToken = getIt().dnsProviderKey; + final String? serverTypeIdentificator = getIt().serverType; + final ServerDomain? serverDomain = getIt().serverDomain; + final DnsProviderType? dnsProvider = getIt().dnsProvider; final ServerProviderType? serverProvider = - getIt().serverProvider; + getIt().serverProvider; final BackupsCredential? backblazeCredential = - getIt().backblazeCredential; + getIt().backblazeCredential; final ServerHostingDetails? serverDetails = - getIt().serverDetails; + getIt().serverDetails; + // TODO: Init server providers in another place if (serverProvider != null || (serverDetails != null && serverDetails.provider != ServerProviderType.unknown)) { @@ -73,85 +81,48 @@ class ServerInstallationRepository { ); } - if (box.get(BNames.hasFinalChecked, defaultValue: false)) { - TlsOptions.verifyCertificate = true; - if (serverTypeIdentificator == null && serverDetails != null) { - final finalServerType = await ProvidersController.currentServerProvider! - .getServerType(serverDetails.id); - await saveServerType(finalServerType.data!); - await ProvidersController.currentServerProvider! - .trySetServerLocation(finalServerType.data!.location.identifier); - return ServerInstallationFinished( - installationDialoguePopUp: null, - providerApiToken: providerApiToken!, - serverTypeIdentificator: finalServerType.data!.identifier, - dnsApiToken: dnsApiToken!, - serverDomain: serverDomain!, - backblazeCredential: backblazeCredential!, - serverDetails: serverDetails, - rootUser: box.get(BNames.rootUser), - isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), - isServerResetedFirstTime: - box.get(BNames.isServerResetedFirstTime, defaultValue: false), - isServerResetedSecondTime: - box.get(BNames.isServerResetedSecondTime, defaultValue: false), + // If we don't have any wizard data, we either have a server set up, or we are starting from scratch + // This behaviour shall change when we introduce multitenancy + if (wizardData == null) { + if (servers.isEmpty) { + // We don't have anything set up, so we start from scratch + return ServerInstallationNotFinished.fromWizardData( + ServerInstallationWizardData.empty(), ); } else { + // We have a server set up, so we load it + TlsOptions.verifyCertificate = true; return ServerInstallationFinished( - installationDialoguePopUp: null, providerApiToken: providerApiToken!, serverTypeIdentificator: serverTypeIdentificator!, dnsApiToken: dnsApiToken!, serverDomain: serverDomain!, backblazeCredential: backblazeCredential!, serverDetails: serverDetails!, - rootUser: box.get(BNames.rootUser), - isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), - isServerResetedFirstTime: - box.get(BNames.isServerResetedFirstTime, defaultValue: false), - isServerResetedSecondTime: - box.get(BNames.isServerResetedSecondTime, defaultValue: false), ); } } - if (box.get(BNames.isRecoveringServer, defaultValue: false) && - serverDomain != null) { + if (wizardData.isRecoveringServer && wizardData.serverDomain != null) { return ServerInstallationRecovery( - providerApiToken: providerApiToken, - dnsApiToken: dnsApiToken, - serverDomain: serverDomain, - serverTypeIdentificator: serverTypeIdentificator, - backblazeCredential: backblazeCredential, - serverDetails: serverDetails, - rootUser: box.get(BNames.rootUser), + providerApiToken: wizardData.serverProviderKey, + dnsApiToken: wizardData.dnsProviderKey, + serverDomain: wizardData.serverDomain, + serverTypeIdentificator: wizardData.serverTypeIdentifier, + backblazeCredential: wizardData.backupsCredential, + serverDetails: wizardData.serverDetails, currentStep: _getCurrentRecoveryStep( - providerApiToken, - dnsApiToken, - serverDomain, - serverDetails, + wizardData.serverProviderKey, + wizardData.dnsProviderKey, + wizardData.serverDomain!, + wizardData.serverDetails, ), - recoveryCapabilities: await getRecoveryCapabilities(serverDomain), + recoveryCapabilities: + await getRecoveryCapabilities(wizardData.serverDomain!), ); } - return ServerInstallationNotFinished( - providerApiToken: providerApiToken, - dnsApiToken: dnsApiToken, - serverDomain: serverDomain, - serverTypeIdentificator: serverTypeIdentificator, - backblazeCredential: backblazeCredential, - serverDetails: serverDetails, - rootUser: box.get(BNames.rootUser), - isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), - isServerResetedFirstTime: - box.get(BNames.isServerResetedFirstTime, defaultValue: false), - isServerResetedSecondTime: - box.get(BNames.isServerResetedSecondTime, defaultValue: false), - isLoading: box.get(BNames.isLoading, defaultValue: false), - dnsMatches: null, - customSshKey: null, - ); + return ServerInstallationNotFinished.fromWizardData(wizardData); } RecoveryStep _getCurrentRecoveryStep( @@ -177,7 +148,9 @@ class ServerInstallationRepository { void clearAppConfig() { box.clear(); - usersBox.clear(); + getIt().clear(); + getIt().clear(); + getIt().clear(); } Future startServer( @@ -209,6 +182,9 @@ class ServerInstallationRepository { if (!domainResult.success || domainResult.data.isEmpty) { return false; } + await getIt().removeDnsProviderToken( + getIt().dnsProviderCredentials.first, + ); return domainResult.data.any( (final serverDomain) => serverDomain.domainName == domain, @@ -216,7 +192,12 @@ class ServerInstallationRepository { } Future createDkimRecord(final ServerDomain domain) async { - final ServerApi api = ServerApi(); + final ServerApi api = ServerApi( + overrideDomain: domain.domainName, + customToken: + getIt().serverInstallation!.serverDetails!.apiToken, + isWithToken: true, + ); late DnsRecord record; try { @@ -233,14 +214,26 @@ class ServerInstallationRepository { } Future isHttpServerWorking() async { - final ServerApi api = ServerApi(); + final ServerApi api = ServerApi( + overrideDomain: + getIt().serverInstallation!.serverDomain!.domainName, + customToken: + getIt().serverInstallation!.serverDetails!.apiToken, + isWithToken: true, + ); return api.isHttpServerWorking(); } Future restart() async { - final server = getIt().serverDetails!; + final server = getIt().serverInstallation!.serverDetails!; - final result = await ServerApi().reboot(); + final result = await ServerApi( + overrideDomain: + getIt().serverInstallation!.serverDomain!.domainName, + customToken: + getIt().serverInstallation!.serverDetails!.apiToken, + isWithToken: true, + ).reboot(); if (result.success && result.data != null) { server.copyWith(startTime: result.data); @@ -252,7 +245,7 @@ class ServerInstallationRepository { } Future powerOn() async { - final server = getIt().serverDetails!; + final server = getIt().serverDetails!; return startServer(server); } @@ -436,174 +429,119 @@ class ServerInstallationRepository { ); } - Future getMainUser() async { - final ServerApi serverApi = ServerApi(); - const User fallbackUser = User( - isFoundOnServer: false, - type: UserType.primary, - note: "Couldn't find main user on server, API is outdated", - login: 'UNKNOWN', - sshKeys: [], - ); - - final String? serverApiVersion = await serverApi.getApiVersion(); - final users = await serverApi.getAllUsers(); - if (serverApiVersion == null || users.isEmpty) { - return fallbackUser; - } - try { - final Version parsedVersion = Version.parse(serverApiVersion); - if (!VersionConstraint.parse('>=1.2.5').allows(parsedVersion)) { - return fallbackUser; - } - return users.firstWhere( - (final User user) => user.type == UserType.primary, - ); - } on FormatException { - return fallbackUser; - } - } - Future> getServersOnProviderAccount() async => (await ProvidersController.currentServerProvider!.getServers()).data; Future saveServerDetails( final ServerHostingDetails serverDetails, ) async { - await getIt().setServerDetails(serverDetails); + await getIt().setServerDetails(serverDetails); } Future deleteServerDetails() async { - await box.delete(BNames.serverDetails); - await getIt().init(); + await getIt().deleteServerDetails(); } Future saveServerProviderType(final ServerProviderType type) async { - await getIt().storeServerProviderType(type); + await getIt().setServerProviderType(type); } Future saveDnsProviderType(final DnsProviderType type) async { - await getIt().setDnsProviderType(type); + await getIt().setDnsProviderType(type); } Future saveServerProviderKey(final String key) async { - await getIt().setServerProviderKey(key); + await getIt().setServerProviderKey(key); + await getIt().addServerProviderToken( + ServerProviderCredential( + tokenId: null, + token: key, + provider: + getIt().serverInstallation!.serverProviderType!, + associatedServerIds: [], + ), + ); } Future saveServerType(final ServerType serverType) async { - await getIt().setServerTypeIdentifier( + await getIt().setServerTypeIdentifier( serverType.identifier, ); - await getIt().setServerLocation( + await getIt().setServerLocation( serverType.location.identifier, ); } - Future deleteServerProviderKey() async { - await box.delete(BNames.hetznerKey); - await getIt().init(); - } - - Future saveBackblazeKey( - final BackupsCredential backblazeCredential, + Future saveBackupsCredential( + final BackupsCredential backupsCredential, ) async { - await getIt().setBackblazeCredential(backblazeCredential); - } - - Future deleteBackblazeKey() async { - await box.delete(BNames.backblazeCredential); - await getIt().init(); + await getIt().setBackupsCredential(backupsCredential); } Future setDnsApiToken(final String key) async { - await getIt().setDnsProviderKey(key); - } - - Future deleteDnsProviderKey() async { - await box.delete(BNames.cloudFlareKey); - await getIt().init(); + await getIt().setDnsProviderKey(key); + await getIt().addDnsProviderToken( + DnsProviderCredential( + tokenId: null, + token: key, + provider: getIt().serverInstallation!.dnsProviderType!, + associatedDomainNames: [], + ), + ); } Future saveDomain(final ServerDomain serverDomain) async { - await getIt().setServerDomain(serverDomain); + await getIt().setServerDomain(serverDomain); } Future deleteDomain() async { - await box.delete(BNames.serverDomain); - await getIt().init(); + await getIt().deleteServerDomain(); } Future saveIsServerStarted(final bool value) async { - await box.put(BNames.isServerStarted, value); + await getIt().setIsServerStarted(value); } - Future saveIsServerResetedFirstTime(final bool value) async { - await box.put(BNames.isServerResetedFirstTime, value); + Future saveIsServerRebootedFirstTime(final bool value) async { + await getIt().setIsServerRebootedFirstTime(value); } - Future saveIsServerResetedSecondTime(final bool value) async { - await box.put(BNames.isServerResetedSecondTime, value); + Future saveIsServerRebootedSecondTime(final bool value) async { + await getIt().setIsServerRebootedSecondTime(value); } Future saveRootUser(final User rootUser) async { - await box.put(BNames.rootUser, rootUser); + await getIt().setRootUser(rootUser); } Future saveIsRecoveringServer(final bool value) async { - await box.put(BNames.isRecoveringServer, value); + await getIt().setIsRecoveringServer(value); } Future saveHasFinalChecked(final bool value) async { - await box.put(BNames.hasFinalChecked, value); - } - - Future deleteServer(final ServerDomain serverDomain) async { - final ServerApi api = ServerApi(); - final dnsRecords = await api.getDnsRecords(); - final GenericResult removalResult = - await ProvidersController.currentDnsProvider!.removeDomainRecords( - domain: serverDomain, - records: dnsRecords, + // We are finished here. Time to save the state and finish the wizard + // TODO: A lot of null checks are skipped here. Implication that every value exists might become false in the future. + // TODO: We would actually want to handle token creation elsewhere. + await getIt().moveServerTypeToServerDetails(); + final ServerInstallationWizardData wizardData = + getIt().serverInstallation!; + await getIt().addServer( + Server( + hostingDetails: wizardData.serverDetails!, + domain: wizardData.serverDomain!, + ), ); - - if (!removalResult.success) { - getIt().showSnackBar( - 'modals.dns_removal_error'.tr(), - ); - return false; - } - - final deletionResult = - await ProvidersController.currentServerProvider!.deleteServer( - serverDomain.domainName, + await getIt().associateServerWithToken( + wizardData.serverDetails!.id, + wizardData.serverProviderKey!, ); - - if (!deletionResult.success) { - getIt().showSnackBar( - 'modals.server_validators_error'.tr(), - ); - return false; - } - - await box.put(BNames.hasFinalChecked, false); - await box.put(BNames.isServerStarted, false); - await box.put(BNames.isServerResetedFirstTime, false); - await box.put(BNames.isServerResetedSecondTime, false); - await box.put(BNames.isLoading, false); - await box.put(BNames.serverDetails, null); - - return true; - } - - Future deleteServerRelatedRecords() async { - await box.deleteAll([ - BNames.serverDetails, - BNames.isServerStarted, - BNames.isServerResetedFirstTime, - BNames.isServerResetedSecondTime, - BNames.hasFinalChecked, - BNames.isLoading, - ]); - await getIt().init(); + await getIt().associateDomainWithToken( + wizardData.serverDomain!.domainName, + wizardData.dnsProviderKey!, + ); + await getIt().addBackupsCredential( + wizardData.backupsCredential!, + ); + await getIt().clearServerInstallation(); } } diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index 8636ffee..f19ba930 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -54,7 +54,7 @@ abstract class ServerInstallationState extends Equatable { ServerSetupProgress get progress => ServerSetupProgress .values[_fulfillmentList.where((final el) => el!).length]; - int get porgressBar { + int get progressBar { if (progress.index < 6) { return progress.index; } else if (progress.index < 10) { @@ -119,7 +119,7 @@ class TimerState extends ServerInstallationNotFinished { enum ServerSetupProgress { nothingYet, serverProviderFilled, - servertTypeFilled, + serverTypeFilled, dnsProviderFilled, backblazeFilled, domainFilled, @@ -147,6 +147,26 @@ class ServerInstallationNotFinished extends ServerInstallationState { super.serverDetails, super.installationDialoguePopUp, }); + + ServerInstallationNotFinished.fromWizardData( + final ServerInstallationWizardData data, + ) : this( + providerApiToken: data.serverProviderKey, + dnsApiToken: data.dnsProviderKey, + serverDomain: data.serverDomain, + serverTypeIdentificator: data.serverTypeIdentifier, + backblazeCredential: data.backupsCredential, + serverDetails: data.serverDetails, + rootUser: data.rootUser, + isServerStarted: data.isServerStarted, + isServerResetedFirstTime: data.isServerResetedFirstTime, + isServerResetedSecondTime: data.isServerResetedSecondTime, + isLoading: data.isLoading, + dnsMatches: null, + customSshKey: null, + installationDialoguePopUp: null, + ); + final bool isLoading; final Map? dnsMatches; final String? customSshKey; @@ -211,12 +231,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, - rootUser: rootUser!, serverDetails: serverDetails!, - isServerStarted: isServerStarted, - isServerResetedFirstTime: isServerResetedFirstTime, - isServerResetedSecondTime: isServerResetedSecondTime, - installationDialoguePopUp: installationDialoguePopUp, ); } @@ -247,13 +262,14 @@ class ServerInstallationFinished extends ServerInstallationState { required String super.dnsApiToken, required BackupsCredential super.backblazeCredential, required ServerDomain super.serverDomain, - required User super.rootUser, required ServerHostingDetails super.serverDetails, - required super.isServerStarted, - required super.isServerResetedFirstTime, - required super.isServerResetedSecondTime, - required super.installationDialoguePopUp, - }); + }) : super( + rootUser: null, + isServerStarted: true, + isServerResetedFirstTime: true, + isServerResetedSecondTime: true, + installationDialoguePopUp: null, + ); @override List get props => [ @@ -302,9 +318,9 @@ class ServerInstallationRecovery extends ServerInstallationState { super.dnsApiToken, super.backblazeCredential, super.serverDomain, - super.rootUser, super.serverDetails, }) : super( + rootUser: null, isServerStarted: true, isServerResetedFirstTime: true, isServerResetedSecondTime: true, @@ -334,7 +350,6 @@ class ServerInstallationRecovery extends ServerInstallationState { final String? dnsApiToken, final BackupsCredential? backblazeCredential, final ServerDomain? serverDomain, - final User? rootUser, final ServerHostingDetails? serverDetails, final RecoveryStep? currentStep, final ServerRecoveryCapabilities? recoveryCapabilities, @@ -346,7 +361,6 @@ class ServerInstallationRecovery extends ServerInstallationState { dnsApiToken: dnsApiToken ?? this.dnsApiToken, backblazeCredential: backblazeCredential ?? this.backblazeCredential, serverDomain: serverDomain ?? this.serverDomain, - rootUser: rootUser ?? this.rootUser, serverDetails: serverDetails ?? this.serverDetails, currentStep: currentStep ?? this.currentStep, recoveryCapabilities: recoveryCapabilities ?? this.recoveryCapabilities, @@ -358,11 +372,6 @@ class ServerInstallationRecovery extends ServerInstallationState { dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, - rootUser: rootUser!, serverDetails: serverDetails!, - isServerStarted: true, - isServerResetedFirstTime: true, - isServerResetedSecondTime: true, - installationDialoguePopUp: null, ); } diff --git a/lib/logic/get_it/api_config.dart b/lib/logic/get_it/api_config.dart index 33632f38..65c64a3e 100644 --- a/lib/logic/get_it/api_config.dart +++ b/lib/logic/get_it/api_config.dart @@ -1,115 +1,18 @@ import 'package:hive/hive.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; -import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; -import 'package:selfprivacy/logic/models/hive/server_details.dart'; -import 'package:selfprivacy/logic/models/hive/server_domain.dart'; class ApiConfigModel { final Box _box = Hive.box(BNames.serverInstallationBox); - ServerHostingDetails? get serverDetails => _serverDetails; - String? get serverProviderKey => _serverProviderKey; - String? get serverLocation => _serverLocation; - String? get serverType => _serverType; - String? get dnsProviderKey => _dnsProviderKey; - ServerProviderType? get serverProvider => _serverProvider; - DnsProviderType? get dnsProvider => _dnsProvider; - - BackupsCredential? get backblazeCredential => _backblazeCredential; - ServerDomain? get serverDomain => _serverDomain; - BackblazeBucket? get backblazeBucket => _backblazeBucket; - static const localeCodeFallback = 'en'; String? _localeCode; + String get localeCode => _localeCode ?? localeCodeFallback; Future setLocaleCode(final String value) async => _localeCode = value; Future resetLocaleCode() async => _localeCode = null; - String? _serverProviderKey; - String? _serverLocation; - String? _dnsProviderKey; - String? _serverType; - ServerProviderType? _serverProvider; - DnsProviderType? _dnsProvider; - ServerHostingDetails? _serverDetails; - BackupsCredential? _backblazeCredential; - ServerDomain? _serverDomain; - BackblazeBucket? _backblazeBucket; - - Future storeServerProviderType(final ServerProviderType value) async { - await _box.put(BNames.serverProvider, value); - _serverProvider = value; - } - - Future setDnsProviderType(final DnsProviderType value) async { - await _box.put(BNames.dnsProvider, value); - _dnsProvider = value; - } - - Future setServerProviderKey(final String value) async { - await _box.put(BNames.hetznerKey, value); - _serverProviderKey = value; - } - - Future setDnsProviderKey(final String value) async { - await _box.put(BNames.cloudFlareKey, value); - _dnsProviderKey = value; - } - - Future setServerTypeIdentifier(final String typeIdentifier) async { - await _box.put(BNames.serverTypeIdentifier, typeIdentifier); - _serverType = typeIdentifier; - } - - Future setServerLocation(final String serverLocation) async { - await _box.put(BNames.serverLocation, serverLocation); - _serverLocation = serverLocation; - } - - Future setBackblazeCredential(final BackupsCredential value) async { - await _box.put(BNames.backblazeCredential, value); - _backblazeCredential = value; - } - - Future setServerDomain(final ServerDomain value) async { - await _box.put(BNames.serverDomain, value); - _serverDomain = value; - } - - Future setServerDetails(final ServerHostingDetails value) async { - await _box.put(BNames.serverDetails, value); - _serverDetails = value; - } - Future setBackblazeBucket(final BackblazeBucket value) async { await _box.put(BNames.backblazeBucket, value); - _backblazeBucket = value; - } - - void clear() { - _serverProviderKey = null; - _dnsProvider = null; - _serverLocation = null; - _dnsProviderKey = null; - _backblazeCredential = null; - _serverDomain = null; - _serverDetails = null; - _backblazeBucket = null; - _serverType = null; - _serverProvider = null; - } - - Future init() async { - _serverProviderKey = _box.get(BNames.hetznerKey); - _serverLocation = _box.get(BNames.serverLocation); - _dnsProviderKey = _box.get(BNames.cloudFlareKey); - _backblazeCredential = _box.get(BNames.backblazeCredential); - _serverDomain = _box.get(BNames.serverDomain); - _serverDetails = _box.get(BNames.serverDetails); - _backblazeBucket = _box.get(BNames.backblazeBucket); - _serverType = _box.get(BNames.serverTypeIdentifier); - _serverProvider = _box.get(BNames.serverProvider); - _dnsProvider = _box.get(BNames.dnsProvider); } } diff --git a/lib/logic/get_it/api_connection_repository.dart b/lib/logic/get_it/api_connection_repository.dart index b46c9e81..a849c8b9 100644 --- a/lib/logic/get_it/api_connection_repository.dart +++ b/lib/logic/get_it/api_connection_repository.dart @@ -6,6 +6,7 @@ import 'package:pub_semver/pub_semver.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/backup.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; @@ -246,14 +247,12 @@ class ApiConnectionRepository { } ServerHostingDetails? get serverDetails => - getIt().serverDetails; - ServerDomain? get serverDomain => getIt().serverDomain; + getIt().serverDetails; + ServerDomain? get serverDomain => getIt().serverDomain; void init() async { - final serverDetails = getIt().serverDetails; - final hasFinalChecked = - box.get(BNames.hasFinalChecked, defaultValue: false); - if (serverDetails == null || !hasFinalChecked) { + final serverDetails = getIt().serverDetails; + if (serverDetails == null) { return; } connectionStatus = ConnectionStatus.reconnecting; @@ -281,6 +280,12 @@ class ApiConnectionRepository { ); } + void clear() async { + connectionStatus = ConnectionStatus.nonexistent; + _connectionStatusStream.add(connectionStatus); + _timer?.cancel(); + } + Future _refetchEverything(final Version version) async { await _apiData.serverJobs .refetchData(version, () => _dataStream.add(_apiData)); @@ -302,7 +307,7 @@ class ApiConnectionRepository { } Future reload(final Timer? timer) async { - final serverDetails = getIt().serverDetails; + final serverDetails = getIt().serverDetails; if (serverDetails == null) { return; } diff --git a/lib/logic/get_it/resources_model.dart b/lib/logic/get_it/resources_model.dart new file mode 100644 index 00000000..b2055381 --- /dev/null +++ b/lib/logic/get_it/resources_model.dart @@ -0,0 +1,342 @@ +import 'package:hive/hive.dart'; +import 'package:selfprivacy/config/hive_config.dart'; +import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; +import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart'; +import 'package:selfprivacy/logic/models/hive/server.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart'; +import 'package:selfprivacy/logic/models/hive/user.dart'; +import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart'; + +class ResourcesModel { + final Box _box = Hive.box(BNames.resourcesBox); + + List get serverProviderCredentials => + _serverProviderTokens; + List get dnsProviderCredentials => _dnsProviderTokens; + List get backupsCredentials => _backupsCredentials; + List get servers => _servers; + BackblazeBucket? get backblazeBucket => _backblazeBucket; + + List _serverProviderTokens = []; + List _dnsProviderTokens = []; + List _backupsCredentials = []; + List _servers = []; + // TODO: As we will add support for other backup storages, we should + // refactor this. + BackblazeBucket? _backblazeBucket; + + @Deprecated('Compatibility getter') + ServerHostingDetails? get serverDetails => + _servers.firstOrNull?.hostingDetails; + @Deprecated('Compatibility getter') + String? get serverProviderKey => _serverProviderTokens.firstOrNull?.token; + @Deprecated('Compatibility getter') + String? get serverLocation => + _servers.firstOrNull?.hostingDetails.serverLocation; + @Deprecated('Compatibility getter') + String? get serverType => _servers.firstOrNull?.hostingDetails.serverType; + @Deprecated('Compatibility getter') + String? get dnsProviderKey => _dnsProviderTokens.firstOrNull?.token; + @Deprecated('Compatibility getter') + ServerProviderType? get serverProvider => + _serverProviderTokens.firstOrNull?.provider; + @Deprecated('Compatibility getter') + DnsProviderType? get dnsProvider => _dnsProviderTokens.firstOrNull?.provider; + @Deprecated('Compatibility getter') + BackupsCredential? get backblazeCredential => _backupsCredentials.firstOrNull; + @Deprecated('Compatibility getter') + ServerDomain? get serverDomain => _servers.firstOrNull?.domain; + + Future addServerProviderToken( + final ServerProviderCredential token, + ) async { + _serverProviderTokens.add(token); + await _box.put(BNames.serverProviderTokens, _serverProviderTokens); + } + + Future associateServerWithToken( + final int serverId, + final String token, + ) async { + _serverProviderTokens + .firstWhere( + (final credential) => credential.token == token, + ) + .associatedServerIds + .add(serverId); + await _box.put(BNames.serverProviderTokens, _serverProviderTokens); + } + + Future removeServerProviderToken( + final ServerProviderCredential token, + ) async { + _serverProviderTokens.remove(token); + await _box.put(BNames.serverProviderTokens, _serverProviderTokens); + } + + Future addDnsProviderToken(final DnsProviderCredential token) async { + // Check if this token already exists + if (_dnsProviderTokens + .any((final credential) => credential.token == token.token)) { + throw Exception('Token already exists'); + } + _dnsProviderTokens.add(token); + await _box.put(BNames.dnsProviderTokens, _dnsProviderTokens); + } + + Future associateDomainWithToken( + final String domain, + final String token, + ) async { + _dnsProviderTokens + .firstWhere( + (final credential) => credential.token == token, + ) + .associatedDomainNames + .add(domain); + await _box.put(BNames.dnsProviderTokens, _dnsProviderTokens); + } + + Future removeDnsProviderToken(final DnsProviderCredential token) async { + _dnsProviderTokens.remove(token); + await _box.put(BNames.dnsProviderTokens, _dnsProviderTokens); + } + + Future addBackupsCredential(final BackupsCredential credential) async { + _backupsCredentials.add(credential); + await _box.put(BNames.backupsProviderTokens, _backupsCredentials); + } + + Future removeBackupsCredential( + final BackupsCredential credential, + ) async { + _backupsCredentials.remove(credential); + await _box.put(BNames.backupsProviderTokens, _backupsCredentials); + } + + Future addServer(final Server server) async { + _servers.add(server); + await _box.put(BNames.servers, _servers); + } + + Future removeServer(final Server server) async { + _servers.remove(server); + await _box.put(BNames.servers, _servers); + } + + Future setBackblazeBucket(final BackblazeBucket bucket) async { + _backblazeBucket = bucket; + await _box.put(BNames.backblazeBucket, _backblazeBucket); + } + + Future removeBackblazeBucket() async { + _backblazeBucket = null; + await _box.delete(BNames.backblazeBucket); + } + + void clear() { + _servers.clear(); + _serverProviderTokens.clear(); + _dnsProviderTokens.clear(); + _backupsCredentials.clear(); + _backblazeBucket = null; + + _box.clear(); + _box.compact(); + } + + void init() { + _serverProviderTokens = _box + .get( + BNames.serverProviderTokens, + defaultValue: [], + ) + .map( + (final e) => e as ServerProviderCredential, + ) + .toList(); + _dnsProviderTokens = _box + .get( + BNames.dnsProviderTokens, + defaultValue: [], + ) + .map((final e) => e as DnsProviderCredential) + .toList(); + _backupsCredentials = _box + .get( + BNames.backupsProviderTokens, + defaultValue: [], + ) + .map((final e) => e as BackupsCredential) + .toList(); + _servers = _box + .get( + BNames.servers, + defaultValue: [], + ) + .map((final e) => e as Server) + .toList(); + _backblazeBucket = _box.get(BNames.backblazeBucket); + } +} + +class WizardDataModel { + final Box _box = Hive.box(BNames.wizardDataBox); + + ServerInstallationWizardData? get serverInstallation => _serverInstallation; + + ServerInstallationWizardData? _serverInstallation; + + Future setServerProviderType(final ServerProviderType provider) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverProviderType: provider); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setServerProviderKey(final String key) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverProviderKey: key); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setDnsProviderType(final DnsProviderType provider) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(dnsProviderType: provider); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setDnsProviderKey(final String key) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(dnsProviderKey: key); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setServerTypeIdentifier(final String identifier) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverTypeIdentifier: identifier); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setServerLocation(final String location) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverLocation: location); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future moveServerTypeToServerDetails() async { + final details = _serverInstallation?.serverDetails; + if (details != null) { + if (_serverInstallation?.serverTypeIdentifier != null && + _serverInstallation?.serverLocation != null) { + _serverInstallation = _serverInstallation?.copyWith( + serverDetails: () => details.copyWith( + serverType: _serverInstallation?.serverTypeIdentifier, + serverLocation: _serverInstallation?.serverLocation, + ), + ); + await _box.put( + BNames.serverInstallationWizardData, + _serverInstallation, + ); + } + } + } + + Future setServerDetails(final ServerHostingDetails details) async { + final detailsWithServerType = details.copyWith( + serverLocation: _serverInstallation?.serverLocation, + serverType: _serverInstallation?.serverTypeIdentifier, + ); + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverDetails: () => detailsWithServerType); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future deleteServerDetails() async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverDetails: () => null); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setBackupsCredential(final BackupsCredential credential) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(backupsCredential: credential); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setServerDomain(final ServerDomain domain) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverDomain: () => domain); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future deleteServerDomain() async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(serverDomain: () => null); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setIsServerStarted(final bool isStarted) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(isServerStarted: isStarted); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setIsServerRebootedFirstTime(final bool isRebooted) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(isServerResetedFirstTime: isRebooted); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setIsServerRebootedSecondTime(final bool isRebooted) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(isServerResetedSecondTime: isRebooted); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setRootUser(final User user) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(rootUser: user); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future setIsRecoveringServer(final bool isRecovering) async { + _serverInstallation = + (_serverInstallation ?? ServerInstallationWizardData.empty()) + .copyWith(isRecoveringServer: isRecovering); + await _box.put(BNames.serverInstallationWizardData, _serverInstallation); + } + + Future clearServerInstallation() async { + _serverInstallation = null; + await _box.delete(BNames.serverInstallationWizardData); + } + + Future clear() async { + await _box.clear(); + await _box.compact(); + } + + void init() { + _serverInstallation = + _box.get(BNames.serverInstallationWizardData, defaultValue: null); + } +} diff --git a/lib/logic/models/hive/README.md b/lib/logic/models/hive/README.md index d50da34c..1183db87 100644 --- a/lib/logic/models/hive/README.md +++ b/lib/logic/models/hive/README.md @@ -3,13 +3,19 @@ 1. User 2. ServerHostingDetails 3. ServerDomain -4. BackblazeCredential -5. ServerVolume +4. BackupsCredential +5. ServerProviderVolume 6. BackblazeBucket +7. ServerProviderCredential +8. DnsProviderCredential +9. Server + +## Wizards store +60. ServerInstallationWizardData ## Enums 100. DnsProvider 101. ServerProvider 102. UserType -103. BackupsProvider +103. BackupsProviderType diff --git a/lib/logic/models/hive/backups_credential.dart b/lib/logic/models/hive/backups_credential.dart index 0c0cf48d..b091a1a1 100644 --- a/lib/logic/models/hive/backups_credential.dart +++ b/lib/logic/models/hive/backups_credential.dart @@ -5,6 +5,7 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.da part 'backups_credential.g.dart'; +// TODO: Make a constant type. @HiveType(typeId: 4) class BackupsCredential { BackupsCredential({ diff --git a/lib/logic/models/hive/dns_provider_credential.dart b/lib/logic/models/hive/dns_provider_credential.dart new file mode 100644 index 00000000..709559aa --- /dev/null +++ b/lib/logic/models/hive/dns_provider_credential.dart @@ -0,0 +1,27 @@ +import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; + +part 'dns_provider_credential.g.dart'; + +// TODO: Make a constant type. +@HiveType(typeId: 8) +class DnsProviderCredential { + DnsProviderCredential({ + required this.tokenId, + required this.token, + required this.provider, + required this.associatedDomainNames, + }); + + @HiveField(0) + final String? tokenId; + + @HiveField(1) + final String token; + + @HiveField(2) + final DnsProviderType provider; + + @HiveField(3) + final List associatedDomainNames; +} diff --git a/lib/logic/models/hive/dns_provider_credential.g.dart b/lib/logic/models/hive/dns_provider_credential.g.dart new file mode 100644 index 00000000..e78d2237 --- /dev/null +++ b/lib/logic/models/hive/dns_provider_credential.g.dart @@ -0,0 +1,50 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'dns_provider_credential.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class DnsProviderCredentialAdapter extends TypeAdapter { + @override + final int typeId = 8; + + @override + DnsProviderCredential read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return DnsProviderCredential( + tokenId: fields[0] as String?, + token: fields[1] as String, + provider: fields[2] as DnsProviderType, + associatedDomainNames: (fields[3] as List).cast(), + ); + } + + @override + void write(BinaryWriter writer, DnsProviderCredential obj) { + writer + ..writeByte(4) + ..writeByte(0) + ..write(obj.tokenId) + ..writeByte(1) + ..write(obj.token) + ..writeByte(2) + ..write(obj.provider) + ..writeByte(3) + ..write(obj.associatedDomainNames); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is DnsProviderCredentialAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/logic/models/hive/server.dart b/lib/logic/models/hive/server.dart new file mode 100644 index 00000000..90712aaf --- /dev/null +++ b/lib/logic/models/hive/server.dart @@ -0,0 +1,20 @@ +import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; + +part 'server.g.dart'; + +// TODO: Make a constant type. +@HiveType(typeId: 9) +class Server { + Server({ + required this.hostingDetails, + required this.domain, + }); + + @HiveField(0) + final ServerHostingDetails hostingDetails; + + @HiveField(1) + final ServerDomain domain; +} diff --git a/lib/logic/models/hive/server.g.dart b/lib/logic/models/hive/server.g.dart new file mode 100644 index 00000000..ba83e895 --- /dev/null +++ b/lib/logic/models/hive/server.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class ServerAdapter extends TypeAdapter { + @override + final int typeId = 9; + + @override + Server read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return Server( + hostingDetails: fields[0] as ServerHostingDetails, + domain: fields[1] as ServerDomain, + ); + } + + @override + void write(BinaryWriter writer, Server obj) { + writer + ..writeByte(2) + ..writeByte(0) + ..write(obj.hostingDetails) + ..writeByte(1) + ..write(obj.domain); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ServerAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/logic/models/hive/server_details.dart b/lib/logic/models/hive/server_details.dart index c19be012..9a11165c 100644 --- a/lib/logic/models/hive/server_details.dart +++ b/lib/logic/models/hive/server_details.dart @@ -12,6 +12,8 @@ class ServerHostingDetails { required this.volume, required this.apiToken, required this.provider, + this.serverLocation, + this.serverType, this.startTime, }); @@ -21,12 +23,13 @@ class ServerHostingDetails { @HiveField(1) final int id; - @HiveField(3) - final DateTime? createTime; - + // TODO: Check if it is still needed @HiveField(2) final DateTime? startTime; + @HiveField(3) + final DateTime? createTime; + // TODO: Check if it is still needed @HiveField(4) final ServerProviderVolume volume; @@ -37,9 +40,21 @@ class ServerHostingDetails { @HiveField(6, defaultValue: ServerProviderType.hetzner) final ServerProviderType provider; - ServerHostingDetails copyWith({final DateTime? startTime}) => + @HiveField(7) + final String? serverLocation; + + @HiveField(8) + final String? serverType; + + ServerHostingDetails copyWith({ + final DateTime? startTime, + final String? serverLocation, + final String? serverType, + }) => ServerHostingDetails( startTime: startTime ?? this.startTime, + serverLocation: serverLocation ?? this.serverLocation, + serverType: serverType ?? this.serverType, createTime: createTime, id: id, ip4: ip4, @@ -103,3 +118,7 @@ enum ServerProviderType { unknown => 'Unknown', }; } + +extension ServerProviderTypeIsSpecified on ServerProviderType? { + bool get isSpecified => this != null && this != ServerProviderType.unknown; +} diff --git a/lib/logic/models/hive/server_details.g.dart b/lib/logic/models/hive/server_details.g.dart index 491344b9..1ace42fc 100644 --- a/lib/logic/models/hive/server_details.g.dart +++ b/lib/logic/models/hive/server_details.g.dart @@ -25,6 +25,8 @@ class ServerHostingDetailsAdapter extends TypeAdapter { provider: fields[6] == null ? ServerProviderType.hetzner : fields[6] as ServerProviderType, + serverLocation: fields[7] as String?, + serverType: fields[8] as String?, startTime: fields[2] as DateTime?, ); } @@ -32,21 +34,25 @@ class ServerHostingDetailsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, ServerHostingDetails obj) { writer - ..writeByte(7) + ..writeByte(9) ..writeByte(0) ..write(obj.ip4) ..writeByte(1) ..write(obj.id) - ..writeByte(3) - ..write(obj.createTime) ..writeByte(2) ..write(obj.startTime) + ..writeByte(3) + ..write(obj.createTime) ..writeByte(4) ..write(obj.volume) ..writeByte(5) ..write(obj.apiToken) ..writeByte(6) - ..write(obj.provider); + ..write(obj.provider) + ..writeByte(7) + ..write(obj.serverLocation) + ..writeByte(8) + ..write(obj.serverType); } @override diff --git a/lib/logic/models/hive/server_domain.dart b/lib/logic/models/hive/server_domain.dart index f86e228f..63387145 100644 --- a/lib/logic/models/hive/server_domain.dart +++ b/lib/logic/models/hive/server_domain.dart @@ -57,3 +57,7 @@ enum DnsProviderType { unknown => 'Unknown', }; } + +extension DnsProviderTypeIsSpecified on DnsProviderType? { + bool get isSpecified => this != null && this != DnsProviderType.unknown; +} diff --git a/lib/logic/models/hive/server_provider_credential.dart b/lib/logic/models/hive/server_provider_credential.dart new file mode 100644 index 00000000..995739e0 --- /dev/null +++ b/lib/logic/models/hive/server_provider_credential.dart @@ -0,0 +1,27 @@ +import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; + +part 'server_provider_credential.g.dart'; + +// TODO: Make a constant type. +@HiveType(typeId: 7) +class ServerProviderCredential { + ServerProviderCredential({ + required this.tokenId, + required this.token, + required this.provider, + required this.associatedServerIds, + }); + + @HiveField(0) + final String? tokenId; + + @HiveField(1) + final String token; + + @HiveField(2) + final ServerProviderType provider; + + @HiveField(3) + final List associatedServerIds; +} diff --git a/lib/logic/models/hive/server_provider_credential.g.dart b/lib/logic/models/hive/server_provider_credential.g.dart new file mode 100644 index 00000000..00beb2ee --- /dev/null +++ b/lib/logic/models/hive/server_provider_credential.g.dart @@ -0,0 +1,51 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_provider_credential.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class ServerProviderCredentialAdapter + extends TypeAdapter { + @override + final int typeId = 7; + + @override + ServerProviderCredential read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return ServerProviderCredential( + tokenId: fields[0] as String?, + token: fields[1] as String, + provider: fields[2] as ServerProviderType, + associatedServerIds: (fields[3] as List).cast(), + ); + } + + @override + void write(BinaryWriter writer, ServerProviderCredential obj) { + writer + ..writeByte(4) + ..writeByte(0) + ..write(obj.tokenId) + ..writeByte(1) + ..write(obj.token) + ..writeByte(2) + ..write(obj.provider) + ..writeByte(3) + ..write(obj.associatedServerIds); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ServerProviderCredentialAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/logic/models/hive/wizards_data/server_installation_wizard_data.dart b/lib/logic/models/hive/wizards_data/server_installation_wizard_data.dart new file mode 100644 index 00000000..ccbae528 --- /dev/null +++ b/lib/logic/models/hive/wizards_data/server_installation_wizard_data.dart @@ -0,0 +1,130 @@ +import 'package:flutter/cupertino.dart'; +import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/models/hive/backups_credential.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/hive/user.dart'; + +part 'server_installation_wizard_data.g.dart'; + +@HiveType(typeId: 60) +class ServerInstallationWizardData { + const ServerInstallationWizardData({ + required this.hasFinalChecked, + required this.isServerStarted, + required this.isServerResetedFirstTime, + required this.isServerResetedSecondTime, + required this.isLoading, + required this.isRecoveringServer, + required this.rootUser, + required this.serverProviderType, + required this.serverProviderKey, + required this.dnsProviderType, + required this.dnsProviderKey, + required this.backupsCredential, + required this.serverTypeIdentifier, + required this.serverLocation, + required this.serverDetails, + required this.serverDomain, + }); + + ServerInstallationWizardData.empty() + : this( + hasFinalChecked: false, + isServerStarted: false, + isServerResetedFirstTime: false, + isServerResetedSecondTime: false, + isLoading: false, + isRecoveringServer: false, + rootUser: null, + serverProviderType: null, + serverProviderKey: null, + dnsProviderType: null, + dnsProviderKey: null, + backupsCredential: null, + serverTypeIdentifier: null, + serverLocation: null, + serverDetails: null, + serverDomain: null, + ); + + // Bool flags used by installer + @HiveField(0) + final bool hasFinalChecked; + @HiveField(1) + final bool isServerStarted; + @HiveField(2) + final bool isServerResetedFirstTime; + @HiveField(3) + final bool isServerResetedSecondTime; + @HiveField(4) + final bool isLoading; + + @HiveField(5) + final bool isRecoveringServer; + + @HiveField(6) + final User? rootUser; + + @HiveField(7) + final ServerProviderType? serverProviderType; + @HiveField(8) + final String? serverProviderKey; + + @HiveField(9) + final DnsProviderType? dnsProviderType; + @HiveField(10) + final String? dnsProviderKey; + + @HiveField(11) + final BackupsCredential? backupsCredential; + + @HiveField(12) + final String? serverTypeIdentifier; + @HiveField(13) + final String? serverLocation; + @HiveField(14) + final ServerHostingDetails? serverDetails; + @HiveField(15) + final ServerDomain? serverDomain; + + ServerInstallationWizardData copyWith({ + final bool? hasFinalChecked, + final bool? isServerStarted, + final bool? isServerResetedFirstTime, + final bool? isServerResetedSecondTime, + final bool? isLoading, + final bool? isRecoveringServer, + final User? rootUser, + final ServerProviderType? serverProviderType, + final String? serverProviderKey, + final DnsProviderType? dnsProviderType, + final String? dnsProviderKey, + final BackupsCredential? backupsCredential, + final String? serverTypeIdentifier, + final String? serverLocation, + final ValueGetter? serverDetails, + final ValueGetter? serverDomain, + }) => + ServerInstallationWizardData( + hasFinalChecked: hasFinalChecked ?? this.hasFinalChecked, + isServerStarted: isServerStarted ?? this.isServerStarted, + isServerResetedFirstTime: + isServerResetedFirstTime ?? this.isServerResetedFirstTime, + isServerResetedSecondTime: + isServerResetedSecondTime ?? this.isServerResetedSecondTime, + isLoading: isLoading ?? this.isLoading, + isRecoveringServer: isRecoveringServer ?? this.isRecoveringServer, + rootUser: rootUser ?? this.rootUser, + serverProviderType: serverProviderType ?? this.serverProviderType, + serverProviderKey: serverProviderKey ?? this.serverProviderKey, + dnsProviderType: dnsProviderType ?? this.dnsProviderType, + dnsProviderKey: dnsProviderKey ?? this.dnsProviderKey, + backupsCredential: backupsCredential ?? this.backupsCredential, + serverTypeIdentifier: serverTypeIdentifier ?? this.serverTypeIdentifier, + serverLocation: serverLocation ?? this.serverLocation, + serverDetails: + serverDetails != null ? serverDetails() : this.serverDetails, + serverDomain: serverDomain != null ? serverDomain() : this.serverDomain, + ); +} diff --git a/lib/logic/models/hive/wizards_data/server_installation_wizard_data.g.dart b/lib/logic/models/hive/wizards_data/server_installation_wizard_data.g.dart new file mode 100644 index 00000000..cad2a199 --- /dev/null +++ b/lib/logic/models/hive/wizards_data/server_installation_wizard_data.g.dart @@ -0,0 +1,87 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_installation_wizard_data.dart'; + +// ************************************************************************** +// TypeAdapterGenerator +// ************************************************************************** + +class ServerInstallationWizardDataAdapter + extends TypeAdapter { + @override + final int typeId = 60; + + @override + ServerInstallationWizardData read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return ServerInstallationWizardData( + hasFinalChecked: fields[0] as bool, + isServerStarted: fields[1] as bool, + isServerResetedFirstTime: fields[2] as bool, + isServerResetedSecondTime: fields[3] as bool, + isLoading: fields[4] as bool, + isRecoveringServer: fields[5] as bool, + rootUser: fields[6] as User?, + serverProviderType: fields[7] as ServerProviderType?, + serverProviderKey: fields[8] as String?, + dnsProviderType: fields[9] as DnsProviderType?, + dnsProviderKey: fields[10] as String?, + backupsCredential: fields[11] as BackupsCredential?, + serverTypeIdentifier: fields[12] as String?, + serverLocation: fields[13] as String?, + serverDetails: fields[14] as ServerHostingDetails?, + serverDomain: fields[15] as ServerDomain?, + ); + } + + @override + void write(BinaryWriter writer, ServerInstallationWizardData obj) { + writer + ..writeByte(16) + ..writeByte(0) + ..write(obj.hasFinalChecked) + ..writeByte(1) + ..write(obj.isServerStarted) + ..writeByte(2) + ..write(obj.isServerResetedFirstTime) + ..writeByte(3) + ..write(obj.isServerResetedSecondTime) + ..writeByte(4) + ..write(obj.isLoading) + ..writeByte(5) + ..write(obj.isRecoveringServer) + ..writeByte(6) + ..write(obj.rootUser) + ..writeByte(7) + ..write(obj.serverProviderType) + ..writeByte(8) + ..write(obj.serverProviderKey) + ..writeByte(9) + ..write(obj.dnsProviderType) + ..writeByte(10) + ..write(obj.dnsProviderKey) + ..writeByte(11) + ..write(obj.backupsCredential) + ..writeByte(12) + ..write(obj.serverTypeIdentifier) + ..writeByte(13) + ..write(obj.serverLocation) + ..writeByte(14) + ..write(obj.serverDetails) + ..writeByte(15) + ..write(obj.serverDomain); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ServerInstallationWizardDataAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/logic/models/price.dart b/lib/logic/models/price.dart index 494b1511..19b5d59a 100644 --- a/lib/logic/models/price.dart +++ b/lib/logic/models/price.dart @@ -11,7 +11,7 @@ class Price { enum CurrencyType { eur, usd, - unkown, + unknown, } class Currency { diff --git a/lib/ui/pages/dns_details/dns_details.dart b/lib/ui/pages/dns_details/dns_details.dart index 02626f9c..04640732 100644 --- a/lib/ui/pages/dns_details/dns_details.dart +++ b/lib/ui/pages/dns_details/dns_details.dart @@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; import 'package:selfprivacy/ui/components/cards/filled_card.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; @@ -86,7 +87,7 @@ class _DnsDetailsPageState extends State { final bool isReady = context.watch().state is ServerInstallationFinished; final String domain = - getIt().serverDomain?.domainName ?? ''; + getIt().serverDomain?.domainName ?? ''; final DnsRecordsState dnsCubit = context.watch().state; print(dnsCubit.dnsState); diff --git a/lib/ui/pages/more/console/console_page.dart b/lib/ui/pages/more/console/console_page.dart index e69eb2fe..6c549a70 100644 --- a/lib/ui/pages/more/console/console_page.dart +++ b/lib/ui/pages/more/console/console_page.dart @@ -1,7 +1,6 @@ import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/models/console_log.dart'; import 'package:selfprivacy/ui/pages/more/console/console_log_item_widget.dart'; @@ -18,21 +17,15 @@ class ConsolePage extends StatefulWidget { class _ConsolePageState extends State { ConsoleModel get console => getIt(); - /// should freeze logs state to properly read logs - late final Future future; - @override void initState() { super.initState(); - - future = getIt.allReady(); console.addListener(update); } @override void dispose() { console.removeListener(update); - super.dispose(); } @@ -48,66 +41,36 @@ class _ConsolePageState extends State { } @override - Widget build(final BuildContext context) => SafeArea( - child: Scaffold( - appBar: AppBar( - title: Text('console_page.title'.tr()), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.of(context).maybePop(), - ), - actions: [ - IconButton( - icon: Icon( - console.paused - ? Icons.play_arrow_outlined - : Icons.pause_outlined, - ), - onPressed: console.paused ? console.play : console.pause, + Widget build(final BuildContext context) { + final List logs = console.logs; + + return SafeArea( + child: Scaffold( + appBar: AppBar( + title: Text('console_page.title'.tr()), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => Navigator.of(context).maybePop(), + ), + actions: [ + IconButton( + icon: Icon( + console.paused + ? Icons.play_arrow_outlined + : Icons.pause_outlined, ), - ], - ), - body: Scrollbar( - child: FutureBuilder( - future: future, - builder: ( - final BuildContext context, - final AsyncSnapshot snapshot, - ) { - if (snapshot.hasData) { - final List logs = console.logs; - - return logs.isEmpty - ? const _ConsoleViewEmpty() - : _ConsoleViewLoaded(logs: logs); - } - - return const _ConsoleViewLoading(); - }, + onPressed: console.paused ? console.play : console.pause, ), - ), + ], ), - ); -} - -class _ConsoleViewLoading extends StatelessWidget { - const _ConsoleViewLoading(); - - @override - Widget build(final BuildContext context) => Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text('console_page.waiting'.tr()), - const Gap(16), - const Expanded( - child: Center( - child: CircularProgressIndicator.adaptive(), - ), - ), - ], - ); + body: Scrollbar( + child: logs.isEmpty + ? const _ConsoleViewEmpty() + : _ConsoleViewLoaded(logs: logs), + ), + ), + ); + } } class _ConsoleViewEmpty extends StatelessWidget { diff --git a/lib/ui/pages/setup/initializing/initializing.dart b/lib/ui/pages/setup/initializing/initializing.dart index 54b5cbe2..d102e8e6 100644 --- a/lib/ui/pages/setup/initializing/initializing.dart +++ b/lib/ui/pages/setup/initializing/initializing.dart @@ -112,7 +112,7 @@ class InitializingPage extends StatelessWidget { 'Server', 'Installation', ], - activeIndex: cubit.state.porgressBar, + activeIndex: cubit.state.progressBar, ), ), ), diff --git a/lib/utils/app_logger.dart b/lib/utils/app_logger.dart new file mode 100644 index 00000000..863eba9e --- /dev/null +++ b/lib/utils/app_logger.dart @@ -0,0 +1,28 @@ +import 'dart:developer' as developer; + +import 'package:selfprivacy/config/config.dart'; + +class AppLogger { + const AppLogger({required this.name}); + + final String name; + + // TODO: research other possible options, which could support both + // throttling and output formatting + void log( + final String message, { + final Object? error, + final StackTrace? stackTrace, + }) { + if (config.shouldDebugPrint) { + // TODO: could probably add UI logging for console_page + developer.log( + message, + error: error, + stackTrace: stackTrace, + time: DateTime.now(), + name: name, + ); + } + } +} diff --git a/lib/utils/secure_storage.dart b/lib/utils/secure_storage.dart new file mode 100644 index 00000000..a51562c5 --- /dev/null +++ b/lib/utils/secure_storage.dart @@ -0,0 +1,48 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:selfprivacy/utils/app_logger.dart'; + +class SecureStorage { + static final log = const AppLogger(name: 'secure_storage').log; + + static const FlutterSecureStorage secureStorage = FlutterSecureStorage(); + static String keyName = 'key'; + + static Future getKey() async { + try { + final bool hasEncryptionKey = + await secureStorage.containsKey(key: keyName); + if (!hasEncryptionKey) { + return null; + } + final String? string = await secureStorage.read(key: keyName); + log('successfully got encryption key: $string'); + + return base64Url.decode(string!); + } catch (error, stackTrace) { + log( + 'error reading encryption key', + error: error, + stackTrace: stackTrace, + ); + rethrow; + } + } + + static Future setKey(final List key) async { + try { + final value = base64UrlEncode(key); + await secureStorage.write(key: keyName, value: value); + log('successfully saved encryption key: $value'); + } catch (error, stackTrace) { + log( + 'error saving new encryption key', + error: error, + stackTrace: stackTrace, + ); + rethrow; + } + } +} diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index a01f420e..4ff4b899 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { @@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + LocalAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalAuthPlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 258f1683..ae5ef358 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus dynamic_color flutter_secure_storage_windows + local_auth_windows url_launcher_windows )