From bd33b8d679dea439d211105b9ce5225db46fc701 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 28 Nov 2022 22:51:37 +0400 Subject: [PATCH 01/11] feat: Implement distinction for connection errors on initialing page Now it's 'false' when api token is invalid and null response if couldn't connect at all, to show different kinds of errors to the user --- assets/translations/en.json | 1 + assets/translations/ru.json | 1 + lib/logic/api_maps/api_generic_result.dart | 13 +++ .../graphql_maps/server_api/server_api.dart | 83 ++++++++----------- .../digital_ocean/digital_ocean.dart | 35 +++++--- .../server_providers/hetzner/hetzner.dart | 35 +++++--- .../server_providers/server_provider.dart | 5 +- lib/logic/cubit/devices/devices_cubit.dart | 7 +- .../initializing/provider_form_cubit.dart | 11 ++- .../recovery_key/recovery_key_cubit.dart | 4 +- .../server_installation_cubit.dart | 29 +++++-- .../server_installation_repository.dart | 8 +- 12 files changed, 142 insertions(+), 90 deletions(-) create mode 100644 lib/logic/api_maps/api_generic_result.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 18e70c5b..de30d6f0 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -273,6 +273,7 @@ "place_where_data": "A place where your data and SelfPrivacy services will reside:", "how": "How to obtain API token", "provider_bad_key_error": "Provider API key is invalid", + "could_not_connect": "Counldn't connect to the provider, please check your connection.", "choose_location_type": "Choose your server location and type:", "back_to_locations": "Go back to available locations!", "no_locations_found": "No available locations found. Make sure your account is accessible.", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 3dd4e590..b1228fa1 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -272,6 +272,7 @@ "place_where_data": "Здесь будут жить ваши данные и SelfPrivacy-сервисы:", "how": "Как получить API Token", "provider_bad_key_error": "API ключ провайдера неверен", + "could_not_connect": "Не удалось соединиться с провайдером. Пожалуйста, проверьте подключение.", "choose_location_type": "Выберите локацию и тип вашего сервера:", "back_to_locations": "Назад к доступным локациям!", "no_locations_found": "Не найдено локаций. Убедитесь, что ваш аккаунт доступен.", diff --git a/lib/logic/api_maps/api_generic_result.dart b/lib/logic/api_maps/api_generic_result.dart new file mode 100644 index 00000000..b5bacfb6 --- /dev/null +++ b/lib/logic/api_maps/api_generic_result.dart @@ -0,0 +1,13 @@ +class APIGenericResult { + APIGenericResult({ + required this.success, + required this.data, + this.message, + }); + + /// Whether was a response successfully received, + /// doesn't represent success of the request if `data` is `bool` + final bool success; + final String? message; + final T data; +} 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 4370131f..5216ffa8 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 @@ -1,5 +1,6 @@ import 'package:graphql/client.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; @@ -22,27 +23,15 @@ import 'package:selfprivacy/logic/models/service.dart'; import 'package:selfprivacy/logic/models/ssh_settings.dart'; import 'package:selfprivacy/logic/models/system_settings.dart'; +export 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; + part 'jobs_api.dart'; part 'server_actions_api.dart'; part 'services_api.dart'; part 'users_api.dart'; part 'volume_api.dart'; -class GenericResult { - GenericResult({ - required this.success, - required this.data, - this.message, - }); - - /// Whether was a response successfully received, - /// doesn't represent success of the request if `data` is `bool` - final bool success; - final String? message; - final T data; -} - -class GenericMutationResult extends GenericResult { +class GenericMutationResult extends APIGenericResult { GenericMutationResult({ required super.success, required this.code, @@ -206,7 +195,7 @@ class ServerApi extends ApiMap return settings; } - Future> getRecoveryTokenStatus() async { + Future> getRecoveryTokenStatus() async { RecoveryKeyStatus? key; QueryResult response; String? error; @@ -223,18 +212,18 @@ class ServerApi extends ApiMap print(e); } - return GenericResult( + return APIGenericResult( success: error == null, data: key, message: error, ); } - Future> generateRecoveryToken( + Future> generateRecoveryToken( final DateTime? expirationDate, final int? numberOfUses, ) async { - GenericResult key; + APIGenericResult key; QueryResult response; try { @@ -255,19 +244,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - key = GenericResult( + key = APIGenericResult( success: false, data: '', message: response.exception.toString(), ); } - key = GenericResult( + key = APIGenericResult( success: true, data: response.parsedData!.getNewRecoveryApiKey.key!, ); } catch (e) { print(e); - key = GenericResult( + key = APIGenericResult( success: false, data: '', message: e.toString(), @@ -300,8 +289,8 @@ class ServerApi extends ApiMap return records; } - Future>> getApiTokens() async { - GenericResult> tokens; + Future>> getApiTokens() async { + APIGenericResult> tokens; QueryResult response; try { @@ -310,7 +299,7 @@ class ServerApi extends ApiMap if (response.hasException) { final message = response.exception.toString(); print(message); - tokens = GenericResult>( + tokens = APIGenericResult>( success: false, data: [], message: message, @@ -324,13 +313,13 @@ class ServerApi extends ApiMap ApiToken.fromGraphQL(device), ) .toList(); - tokens = GenericResult>( + tokens = APIGenericResult>( success: true, data: parsed, ); } catch (e) { print(e); - tokens = GenericResult>( + tokens = APIGenericResult>( success: false, data: [], message: e.toString(), @@ -340,8 +329,8 @@ class ServerApi extends ApiMap return tokens; } - Future> deleteApiToken(final String name) async { - GenericResult returnable; + Future> deleteApiToken(final String name) async { + APIGenericResult returnable; QueryResult response; try { @@ -358,19 +347,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - returnable = GenericResult( + returnable = APIGenericResult( success: false, data: null, message: response.exception.toString(), ); } - returnable = GenericResult( + returnable = APIGenericResult( success: true, data: null, ); } catch (e) { print(e); - returnable = GenericResult( + returnable = APIGenericResult( success: false, data: null, message: e.toString(), @@ -380,8 +369,8 @@ class ServerApi extends ApiMap return returnable; } - Future> createDeviceToken() async { - GenericResult token; + Future> createDeviceToken() async { + APIGenericResult token; QueryResult response; try { @@ -393,19 +382,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = GenericResult( + token = APIGenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = GenericResult( + token = APIGenericResult( success: true, data: response.parsedData!.getNewDeviceApiKey.key!, ); } catch (e) { print(e); - token = GenericResult( + token = APIGenericResult( success: false, data: '', message: e.toString(), @@ -417,10 +406,10 @@ class ServerApi extends ApiMap Future isHttpServerWorking() async => (await getApiVersion()) != null; - Future> authorizeDevice( + Future> authorizeDevice( final DeviceToken deviceToken, ) async { - GenericResult token; + APIGenericResult token; QueryResult response; try { @@ -442,19 +431,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = GenericResult( + token = APIGenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = GenericResult( + token = APIGenericResult( success: true, data: response.parsedData!.authorizeWithNewDeviceApiKey.token!, ); } catch (e) { print(e); - token = GenericResult( + token = APIGenericResult( success: false, data: '', message: e.toString(), @@ -464,10 +453,10 @@ class ServerApi extends ApiMap return token; } - Future> useRecoveryToken( + Future> useRecoveryToken( final DeviceToken deviceToken, ) async { - GenericResult token; + APIGenericResult token; QueryResult response; try { @@ -489,19 +478,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = GenericResult( + token = APIGenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = GenericResult( + token = APIGenericResult( success: true, data: response.parsedData!.useRecoveryApiKey.token!, ); } catch (e) { print(e); - token = GenericResult( + token = APIGenericResult( success: false, data: '', message: e.toString(), diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart index ba3f0d63..b97018e2 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart @@ -59,35 +59,50 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { String get displayProviderName => 'Digital Ocean'; @override - Future isApiTokenValid(final String token) async { + Future> isApiTokenValid(final String token) async { bool isValid = false; Response? response; + String message = ''; final Dio client = await getClient(); try { response = await client.get( '/account', options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), headers: {'Authorization': 'Bearer $token'}, ), ); } catch (e) { print(e); isValid = false; + message = e.toString(); } finally { close(client); } - if (response != null) { - if (response.statusCode == HttpStatus.ok) { - isValid = true; - } else if (response.statusCode == HttpStatus.unauthorized) { - isValid = false; - } else { - throw Exception('code: ${response.statusCode}'); - } + if (response == null) { + return APIGenericResult( + data: isValid, + success: false, + message: message, + ); } - return isValid; + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return APIGenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); } /// Hardcoded on their documentation and there is no pricing API at all diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index 57df7837..87331d85 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart @@ -60,35 +60,50 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { String get displayProviderName => 'Hetzner'; @override - Future isApiTokenValid(final String token) async { + Future> isApiTokenValid(final String token) async { bool isValid = false; Response? response; + String message = ''; final Dio client = await getClient(); try { response = await client.get( '/servers', options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), headers: {'Authorization': 'Bearer $token'}, ), ); } catch (e) { print(e); isValid = false; + message = e.toString(); } finally { close(client); } - if (response != null) { - if (response.statusCode == HttpStatus.ok) { - isValid = true; - } else if (response.statusCode == HttpStatus.unauthorized) { - isValid = false; - } else { - throw Exception('code: ${response.statusCode}'); - } + if (response == null) { + return APIGenericResult( + data: isValid, + success: false, + message: message, + ); } - return isValid; + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return APIGenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart index a2eb71f3..45e2bd2e 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart @@ -1,3 +1,4 @@ +import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; @@ -8,6 +9,8 @@ import 'package:selfprivacy/logic/models/server_metadata.dart'; import 'package:selfprivacy/logic/models/server_provider_location.dart'; import 'package:selfprivacy/logic/models/server_type.dart'; +export 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; + class ProviderApiTokenValidation { ProviderApiTokenValidation({ required this.length, @@ -39,7 +42,7 @@ abstract class ServerProviderApi extends ApiMap { required final ServerDomain domain, }); - Future isApiTokenValid(final String token); + Future> isApiTokenValid(final String token); ProviderApiTokenValidation getApiTokenValidation(); Future> getMetadata(final int serverId); Future getMetrics( diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index 10ad943d..5e5c145c 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -35,7 +35,7 @@ class ApiDevicesCubit } Future?> _getApiTokens() async { - final GenericResult> response = await api.getApiTokens(); + final APIGenericResult> response = await api.getApiTokens(); if (response.success) { return response.data; } else { @@ -44,7 +44,8 @@ class ApiDevicesCubit } Future deleteDevice(final ApiToken device) async { - final GenericResult response = await api.deleteApiToken(device.name); + final APIGenericResult response = + await api.deleteApiToken(device.name); if (response.success) { emit( ApiDevicesState( @@ -59,7 +60,7 @@ class ApiDevicesCubit } Future getNewDeviceKey() async { - final GenericResult response = await api.createDeviceToken(); + final APIGenericResult response = await api.createDeviceToken(); if (response.success) { return response.data; } else { diff --git a/lib/logic/cubit/forms/setup/initializing/provider_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/provider_form_cubit.dart index d3307762..ebabb5e7 100644 --- a/lib/logic/cubit/forms/setup/initializing/provider_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/provider_form_cubit.dart @@ -29,21 +29,24 @@ class ProviderFormCubit extends FormCubit { @override FutureOr asyncValidation() async { - late bool isKeyValid; + bool? isKeyValid; try { isKeyValid = await serverInstallationCubit .isServerProviderApiTokenValid(apiKey.state.value); } catch (e) { addError(e); - isKeyValid = false; + } + + if (isKeyValid == null) { + apiKey.setError(''); + return false; } if (!isKeyValid) { apiKey.setError('initializing.provider_bad_key_error'.tr()); - return false; } - return true; + return isKeyValid; } } diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart index 76a572d1..56800be3 100644 --- a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart +++ b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart @@ -32,7 +32,7 @@ class RecoveryKeyCubit } Future _getRecoveryKeyStatus() async { - final GenericResult response = + final APIGenericResult response = await api.getRecoveryTokenStatus(); if (response.success) { return response.data; @@ -57,7 +57,7 @@ class RecoveryKeyCubit final DateTime? expirationDate, final int? numberOfUses, }) async { - final GenericResult response = + final APIGenericResult response = await api.generateRecoveryToken(expirationDate, numberOfUses); if (response.success) { refresh(); diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 08852825..dd9dadbd 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -76,16 +76,27 @@ class ServerInstallationCubit extends Cubit { .getDnsProvider() .getApiTokenValidation(); - Future isServerProviderApiTokenValid( + Future isServerProviderApiTokenValid( final String providerToken, - ) async => - ApiController.currentServerProviderApiFactory! - .getServerProvider( - settings: const ServerProviderApiSettings( - isWithToken: false, - ), - ) - .isApiTokenValid(providerToken); + ) async { + final APIGenericResult apiResponse = + await ApiController.currentServerProviderApiFactory! + .getServerProvider( + settings: const ServerProviderApiSettings( + isWithToken: false, + ), + ) + .isApiTokenValid(providerToken); + + if (!apiResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + return null; + } + + return apiResponse.data; + } Future isDnsProviderApiTokenValid( final String providerToken, diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index cc3860e3..8365529a 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -479,7 +479,7 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final GenericResult result = await serverApi.authorizeDevice( + final APIGenericResult result = await serverApi.authorizeDevice( DeviceToken(device: await getDeviceName(), token: newDeviceKey), ); @@ -516,7 +516,7 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final GenericResult result = await serverApi.useRecoveryToken( + final APIGenericResult result = await serverApi.useRecoveryToken( DeviceToken(device: await getDeviceName(), token: recoveryKey), ); @@ -577,9 +577,9 @@ class ServerInstallationRepository { ); } } - final GenericResult deviceAuthKey = + final APIGenericResult deviceAuthKey = await serverApi.createDeviceToken(); - final GenericResult result = await serverApi.authorizeDevice( + final APIGenericResult result = await serverApi.authorizeDevice( DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data), ); From 1df5f6594d21f2f82104e2ae334851a9322d8e29 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 28 Nov 2022 23:11:08 +0400 Subject: [PATCH 02/11] feat: Implement distinction for connection errors on server type page Now user gets notified when connection error occurs --- .../digital_ocean/digital_ocean.dart | 19 ++++++++++++---- .../server_providers/hetzner/hetzner.dart | 19 ++++++++++++---- .../server_providers/server_provider.dart | 5 +++-- .../server_installation_cubit.dart | 22 +++++++++++++++++-- 4 files changed, 53 insertions(+), 12 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart index b97018e2..55534b1d 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart @@ -709,7 +709,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future> getAvailableLocations() async { + Future>> + getAvailableLocations() async { List locations = []; final Dio client = await getClient(); @@ -730,15 +731,20 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { .toList(); } catch (e) { print(e); + return APIGenericResult( + data: [], + success: false, + message: e.toString(), + ); } finally { close(client); } - return locations; + return APIGenericResult(data: locations, success: true); } @override - Future> getServerTypesByLocation({ + Future>> getServerTypesByLocation({ required final ServerProviderLocation location, }) async { final List types = []; @@ -771,11 +777,16 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } } catch (e) { print(e); + return APIGenericResult( + data: [], + success: false, + message: e.toString(), + ); } finally { close(client); } - return types; + return APIGenericResult(data: types, success: true); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index 87331d85..0f735217 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart @@ -707,7 +707,8 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } @override - Future> getAvailableLocations() async { + Future>> + getAvailableLocations() async { List locations = []; final Dio client = await getClient(); @@ -728,15 +729,20 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { .toList(); } catch (e) { print(e); + return APIGenericResult( + success: false, + data: [], + message: e.toString(), + ); } finally { close(client); } - return locations; + return APIGenericResult(success: true, data: locations); } @override - Future> getServerTypesByLocation({ + Future>> getServerTypesByLocation({ required final ServerProviderLocation location, }) async { final List types = []; @@ -769,11 +775,16 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } } catch (e) { print(e); + return APIGenericResult( + data: [], + success: false, + message: e.toString(), + ); } finally { close(client); } - return types; + return APIGenericResult(data: types, success: true); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart index 45e2bd2e..1b9a320a 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart @@ -22,8 +22,9 @@ class ProviderApiTokenValidation { abstract class ServerProviderApi extends ApiMap { Future> getServers(); - Future> getAvailableLocations(); - Future> getServerTypesByLocation({ + Future>> + getAvailableLocations(); + Future>> getServerTypesByLocation({ required final ServerProviderLocation location, }); diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index dd9dadbd..ef23d18a 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -123,9 +123,18 @@ class ServerInstallationCubit extends Cubit { return []; } - return ApiController.currentServerProviderApiFactory! + final APIGenericResult apiResult = await ApiController + .currentServerProviderApiFactory! .getServerProvider() .getAvailableLocations(); + + if (!apiResult.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + } + + return apiResult.data; } Future> fetchAvailableTypesByLocation( @@ -135,9 +144,18 @@ class ServerInstallationCubit extends Cubit { return []; } - return ApiController.currentServerProviderApiFactory! + final APIGenericResult apiResult = await ApiController + .currentServerProviderApiFactory! .getServerProvider() .getServerTypesByLocation(location: location); + + if (!apiResult.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + } + + return apiResult.data; } void setServerProviderKey(final String serverProviderKey) async { From e62e8bf916c4757330c25c1fa33074126b1e3891 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 28 Nov 2022 23:55:37 +0400 Subject: [PATCH 03/11] feat: Implement distinction for connection errors on dns provider page Now user gets notified when connection error occurs --- .../dns_providers/cloudflare/cloudflare.dart | 39 +++++++++++++------ .../rest_maps/dns_providers/dns_provider.dart | 5 ++- .../initializing/dns_provider_form_cubit.dart | 11 ++++-- .../server_installation_cubit.dart | 22 ++++++++--- 4 files changed, 55 insertions(+), 22 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart index c79f0987..f4d786ca 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart @@ -46,33 +46,50 @@ class CloudflareApi extends DnsProviderApi { String rootAddress = 'https://api.cloudflare.com/client/v4'; @override - Future isApiTokenValid(final String token) async { + Future> isApiTokenValid(final String token) async { bool isValid = false; Response? response; + String message = ''; final Dio client = await getClient(); try { response = await client.get( '/user/tokens/verify', - options: Options(headers: {'Authorization': 'Bearer $token'}), + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Bearer $token'}, + ), ); } catch (e) { print(e); isValid = false; + message = e.toString(); } finally { close(client); } - if (response != null) { - if (response.statusCode == HttpStatus.ok) { - isValid = true; - } else if (response.statusCode == HttpStatus.unauthorized) { - isValid = false; - } else { - throw Exception('code: ${response.statusCode}'); - } + if (response == null) { + return APIGenericResult( + data: isValid, + success: false, + message: message, + ); } - return isValid; + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return APIGenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); } @override diff --git a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart index 3ff6222e..2c538251 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart @@ -1,7 +1,10 @@ +import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; +export 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; + class DomainNotFoundException implements Exception { DomainNotFoundException(this.message); final String message; @@ -26,6 +29,6 @@ abstract class DnsProviderApi extends ApiMap { Future getZoneId(final String domain); Future> domainList(); - Future isApiTokenValid(final String token); + Future> isApiTokenValid(final String token); RegExp getApiTokenValidation(); } diff --git a/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart index e50d7db3..553c3492 100644 --- a/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart @@ -28,21 +28,24 @@ class DnsProviderFormCubit extends FormCubit { @override FutureOr asyncValidation() async { - late bool isKeyValid; + bool? isKeyValid; try { isKeyValid = await initializingCubit .isDnsProviderApiTokenValid(apiKey.state.value); } catch (e) { addError(e); - isKeyValid = false; + } + + if (isKeyValid == null) { + apiKey.setError(''); + return false; } if (!isKeyValid) { apiKey.setError('initializing.cloudflare_bad_key_error'.tr()); - return false; } - return true; + return isKeyValid; } } diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index ef23d18a..c63154c0 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -98,7 +98,7 @@ class ServerInstallationCubit extends Cubit { return apiResponse.data; } - Future isDnsProviderApiTokenValid( + Future isDnsProviderApiTokenValid( final String providerToken, ) async { if (ApiController.currentDnsProviderApiFactory == null) { @@ -111,11 +111,21 @@ class ServerInstallationCubit extends Cubit { ); } - return ApiController.currentDnsProviderApiFactory! - .getDnsProvider( - settings: const DnsProviderApiSettings(isWithToken: false), - ) - .isApiTokenValid(providerToken); + final APIGenericResult apiResponse = + await ApiController.currentDnsProviderApiFactory! + .getDnsProvider( + settings: const DnsProviderApiSettings(isWithToken: false), + ) + .isApiTokenValid(providerToken); + + if (!apiResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + return null; + } + + return apiResponse.data; } Future> fetchAvailableLocations() async { From 6b5a4f78757a00f4e3c20866ac9af2d3f0b5bff4 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 29 Nov 2022 15:27:19 +0400 Subject: [PATCH 04/11] chore: Make assets for connectior eroor shorter To fit on screen --- assets/translations/en.json | 2 +- assets/translations/ru.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index de30d6f0..d490583e 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -273,7 +273,7 @@ "place_where_data": "A place where your data and SelfPrivacy services will reside:", "how": "How to obtain API token", "provider_bad_key_error": "Provider API key is invalid", - "could_not_connect": "Counldn't connect to the provider, please check your connection.", + "could_not_connect": "Counldn't connect to the provider.", "choose_location_type": "Choose your server location and type:", "back_to_locations": "Go back to available locations!", "no_locations_found": "No available locations found. Make sure your account is accessible.", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index b1228fa1..17cdbcad 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -272,7 +272,7 @@ "place_where_data": "Здесь будут жить ваши данные и SelfPrivacy-сервисы:", "how": "Как получить API Token", "provider_bad_key_error": "API ключ провайдера неверен", - "could_not_connect": "Не удалось соединиться с провайдером. Пожалуйста, проверьте подключение.", + "could_not_connect": "Не удалось соединиться с провайдером.", "choose_location_type": "Выберите локацию и тип вашего сервера:", "back_to_locations": "Назад к доступным локациям!", "no_locations_found": "Не найдено локаций. Убедитесь, что ваш аккаунт доступен.", From 1dfd2180d2f08626577f94b7fa8f2b8eddefb8db Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 29 Nov 2022 15:28:09 +0400 Subject: [PATCH 05/11] feat: Implement distinction for connection errors on storage page Now user gets notified when connection error occurs --- lib/logic/api_maps/rest_maps/backblaze.dart | 35 +++++++++++++++---- .../initializing/backblaze_form_cubit.dart | 29 ++++++++++----- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/backblaze.dart b/lib/logic/api_maps/rest_maps/backblaze.dart index 5140311d..95ced573 100644 --- a/lib/logic/api_maps/rest_maps/backblaze.dart +++ b/lib/logic/api_maps/rest_maps/backblaze.dart @@ -2,9 +2,12 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; +export 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; + class BackblazeApiAuth { BackblazeApiAuth({required this.authorizationToken, required this.apiUrl}); @@ -71,28 +74,46 @@ class BackblazeApi extends ApiMap { ); } - Future isValid(final String encodedApiKey) async { + Future> isApiTokenValid( + final String encodedApiKey, + ) async { final Dio client = await getClient(); + bool isTokenValid = false; try { final Response response = await client.get( 'b2_authorize_account', - options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}), + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Basic $encodedApiKey'}, + ), ); if (response.statusCode == HttpStatus.ok) { if (response.data['allowed']['capabilities'].contains('listBuckets')) { - return true; + isTokenValid = true; } - return false; + isTokenValid = false; } else if (response.statusCode == HttpStatus.unauthorized) { - return false; + isTokenValid = false; } else { throw Exception('code: ${response.statusCode}'); } - } on DioError { - return false; + } on DioError catch (e) { + print(e); + return APIGenericResult( + data: false, + success: false, + message: e.toString(), + ); } finally { close(client); } + + return APIGenericResult( + data: isTokenValid, + success: true, + ); } // Create bucket diff --git a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart index 2a9f4662..7390eee1 100644 --- a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart @@ -1,13 +1,14 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; +import 'package:selfprivacy/logic/get_it/navigation.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:easy_localization/easy_localization.dart'; class BackblazeFormCubit extends FormCubit { BackblazeFormCubit(this.serverInstallationCubit) { - //var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]"); keyId = FieldCubit( initalValue: '', validations: [ @@ -40,7 +41,7 @@ class BackblazeFormCubit extends FormCubit { @override FutureOr asyncValidation() async { - late bool isKeyValid; + late APIGenericResult backblazeResponse; final BackblazeApi apiClient = BackblazeApi(isWithToken: false); try { @@ -48,18 +49,30 @@ class BackblazeFormCubit extends FormCubit { keyId.state.value, applicationKey.state.value, ); - isKeyValid = await apiClient.isValid(encodedApiKey); + backblazeResponse = await apiClient.isApiTokenValid(encodedApiKey); } catch (e) { addError(e); - isKeyValid = false; + backblazeResponse = APIGenericResult( + success: false, + data: false, + message: e.toString(), + ); } - if (!isKeyValid) { - keyId.setError('initializing.backblaze_bad_key_error'.tr()); - applicationKey.setError('initializing.backblaze_bad_key_error'.tr()); + if (!backblazeResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + keyId.setError(''); + applicationKey.setError(''); return false; } - return true; + if (!backblazeResponse.data) { + keyId.setError('initializing.backblaze_bad_key_error'.tr()); + applicationKey.setError('initializing.backblaze_bad_key_error'.tr()); + } + + return backblazeResponse.data; } } From 65f5d987e2f0747e17552d0d88da49d021145ba7 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 29 Nov 2022 19:21:36 +0400 Subject: [PATCH 06/11] feat: Implement error handling for server installation Now user gets notified when connection error occurs --- .../dns_providers/cloudflare/cloudflare.dart | 20 ++++++- .../rest_maps/dns_providers/dns_provider.dart | 4 +- .../digital_ocean/digital_ocean.dart | 4 +- .../server_providers/hetzner/hetzner.dart | 9 +++- .../server_providers/server_provider.dart | 2 +- .../server_installation_repository.dart | 54 +++++++++++++------ 6 files changed, 71 insertions(+), 22 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart index f4d786ca..14cf5c96 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart @@ -113,7 +113,7 @@ class CloudflareApi extends DnsProviderApi { } @override - Future removeSimilarRecords({ + Future> removeSimilarRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -139,9 +139,16 @@ class CloudflareApi extends DnsProviderApi { await Future.wait(allDeleteFutures); } catch (e) { print(e); + return APIGenericResult( + success: false, + data: null, + message: e.toString(), + ); } finally { close(client); } + + return APIGenericResult(success: true, data: null); } @override @@ -183,7 +190,7 @@ class CloudflareApi extends DnsProviderApi { } @override - Future createMultipleDnsRecords({ + Future> createMultipleDnsRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -206,9 +213,18 @@ class CloudflareApi extends DnsProviderApi { } on DioError catch (e) { print(e.message); rethrow; + } catch (e) { + print(e); + return APIGenericResult( + success: false, + data: null, + message: e.toString(), + ); } finally { close(client); } + + return APIGenericResult(success: true, data: null); } List projectDnsRecords( diff --git a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart index 2c538251..106d185c 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart @@ -14,11 +14,11 @@ abstract class DnsProviderApi extends ApiMap { Future> getDnsRecords({ required final ServerDomain domain, }); - Future removeSimilarRecords({ + Future> removeSimilarRecords({ required final ServerDomain domain, final String? ip4, }); - Future createMultipleDnsRecords({ + Future> createMultipleDnsRecords({ required final ServerDomain domain, final String? ip4, }); diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart index 55534b1d..86cda482 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart @@ -790,11 +790,13 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future createReverseDns({ + Future> createReverseDns({ required final ServerHostingDetails serverDetails, required final ServerDomain domain, }) async { /// TODO remove from provider interface + const bool success = true; + return APIGenericResult(success: success, data: null); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index 0f735217..4b51b27c 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart @@ -788,7 +788,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } @override - Future createReverseDns({ + Future> createReverseDns({ required final ServerHostingDetails serverDetails, required final ServerDomain domain, }) async { @@ -803,8 +803,15 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } catch (e) { print(e); + return APIGenericResult( + success: false, + data: null, + message: e.toString(), + ); } finally { close(client); } + + return APIGenericResult(success: true, data: null); } } diff --git a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart index 1b9a320a..21f6f376 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart @@ -38,7 +38,7 @@ abstract class ServerProviderApi extends ApiMap { required final String domainName, required final String serverType, }); - Future createReverseDns({ + Future> createReverseDns({ required final ServerHostingDetails serverDetails, required final ServerDomain domain, }); diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 8365529a..fb4d9d15 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -334,21 +334,9 @@ class ServerInstallationRepository { final ServerProviderApi serverApi = ApiController.currentServerProviderApiFactory!.getServerProvider(); - await dnsProviderApi.removeSimilarRecords( - ip4: serverDetails.ip4, - domain: domain, - ); - - try { - await dnsProviderApi.createMultipleDnsRecords( - ip4: serverDetails.ip4, - domain: domain, - ); - } on DioError catch (e) { + void showDomainErrorPopUp(final String error) { showPopUpAlert( - alertTitle: e.response!.data['errors'][0]['code'] == 1038 - ? 'modals.you_cant_use_this_api'.tr() - : 'domain.error'.tr(), + alertTitle: error, description: 'modals.delete_server_volume'.tr(), cancelButtonOnPressed: onCancel, actionButtonTitle: 'basis.delete'.tr(), @@ -359,14 +347,50 @@ class ServerInstallationRepository { onCancel(); }, ); + } + + final APIGenericResult removingResult = + await dnsProviderApi.removeSimilarRecords( + ip4: serverDetails.ip4, + domain: domain, + ); + + if (!removingResult.success) { + showDomainErrorPopUp('domain.error'.tr()); return false; } - await serverApi.createReverseDns( + bool createdSuccessfully = false; + String errorMessage = 'domain.error'.tr(); + try { + final APIGenericResult createResult = + await dnsProviderApi.createMultipleDnsRecords( + ip4: serverDetails.ip4, + domain: domain, + ); + createdSuccessfully = createResult.success; + } on DioError catch (e) { + if (e.response!.data['errors'][0]['code'] == 1038) { + errorMessage = 'modals.you_cant_use_this_api'.tr(); + } + } + + if (!createdSuccessfully) { + showDomainErrorPopUp(errorMessage); + return false; + } + + final APIGenericResult createReverseResult = + await serverApi.createReverseDns( serverDetails: serverDetails, domain: domain, ); + if (!createReverseResult.success) { + showDomainErrorPopUp(errorMessage); + return false; + } + return true; } From 54d8b044394497ac0874f4d54167c83398fdbe94 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 30 Nov 2022 19:02:30 +0400 Subject: [PATCH 07/11] fix: Fix backblaze token validation True if correct, false if incorrect, null if no connection --- lib/logic/api_maps/rest_maps/backblaze.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/backblaze.dart b/lib/logic/api_maps/rest_maps/backblaze.dart index 95ced573..edc283b5 100644 --- a/lib/logic/api_maps/rest_maps/backblaze.dart +++ b/lib/logic/api_maps/rest_maps/backblaze.dart @@ -90,10 +90,7 @@ class BackblazeApi extends ApiMap { ), ); if (response.statusCode == HttpStatus.ok) { - if (response.data['allowed']['capabilities'].contains('listBuckets')) { - isTokenValid = true; - } - isTokenValid = false; + isTokenValid = response.data['allowed']['capabilities'].contains('listBuckets'); } else if (response.statusCode == HttpStatus.unauthorized) { isTokenValid = false; } else { From 899c84c54fa4e2844bc2b3d4fcc0ac20d43785a1 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 30 Nov 2022 19:04:04 +0400 Subject: [PATCH 08/11] chore: Rename Check step to Installation It just feels more convenient tbh... --- lib/ui/pages/setup/initializing/initializing.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/pages/setup/initializing/initializing.dart b/lib/ui/pages/setup/initializing/initializing.dart index 29fcd6f0..1e3e7982 100644 --- a/lib/ui/pages/setup/initializing/initializing.dart +++ b/lib/ui/pages/setup/initializing/initializing.dart @@ -77,7 +77,7 @@ class InitializingPage extends StatelessWidget { 'Domain', 'User', 'Server', - 'Check', + 'Installation', ], activeIndex: cubit.state.porgressBar, ), From 29b0bf23976111fad52fbc77930305b1936206cc Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 30 Nov 2022 19:05:21 +0400 Subject: [PATCH 09/11] fix: Fix some initializing errors Correct progress index calculation and hardcore cloudflare loading until new providers for DNS are supported --- .../server_installation_repository.dart | 13 ++++++------- .../server_installation_state.dart | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index fb4d9d15..80c3ee0d 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -75,13 +75,12 @@ class ServerInstallationRepository { ); } - if (serverDomain != null && serverDomain.provider != DnsProvider.unknown) { - ApiController.initDnsProviderApiFactory( - DnsProviderApiFactorySettings( - provider: serverDomain.provider, - ), - ); - } + // No other DNS provider is supported for now, so it's fine. + ApiController.initDnsProviderApiFactory( + DnsProviderApiFactorySettings( + provider: DnsProvider.cloudflare, + ), + ); if (box.get(BNames.hasFinalChecked, defaultValue: false)) { return ServerInstallationFinished( diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index 331c3e2a..69934bbd 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -48,7 +48,7 @@ abstract class ServerInstallationState extends Equatable { bool get isFullyInitilized => _fulfilementList.every((final el) => el!); ServerSetupProgress get progress => ServerSetupProgress - .values[_fulfilementList.where((final el) => el!).length]; + .values[_fulfilementList.where((final el) => el!).length + 1]; int get porgressBar { if (progress.index < 6) { From e0b32404bedb60f913cd16313d8297882d79ec94 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 2 Dec 2022 22:40:08 +0400 Subject: [PATCH 10/11] refactor: Implement better error handling on create server stage Replace try-catch hell with APIGenericResult chain --- lib/logic/api_maps/api_generic_result.dart | 2 + .../graphql_maps/server_api/jobs_api.dart | 6 +- .../graphql_maps/server_api/server_api.dart | 10 --- .../graphql_maps/server_api/services_api.dart | 36 ++++---- .../graphql_maps/server_api/users_api.dart | 30 +++---- .../graphql_maps/server_api/volume_api.dart | 8 +- .../digital_ocean/digital_ocean.dart | 54 +++++++++--- .../server_providers/hetzner/hetzner.dart | 68 ++++++++++----- .../server_providers/server_provider.dart | 2 +- .../server_providers/volume_provider.dart | 10 ++- .../initializing/backblaze_form_cubit.dart | 1 - .../provider_volume_cubit.dart | 9 +- .../server_installation_repository.dart | 82 +++++++++++-------- lib/logic/cubit/users/users_cubit.dart | 10 +-- 14 files changed, 197 insertions(+), 131 deletions(-) diff --git a/lib/logic/api_maps/api_generic_result.dart b/lib/logic/api_maps/api_generic_result.dart index b5bacfb6..81e1760a 100644 --- a/lib/logic/api_maps/api_generic_result.dart +++ b/lib/logic/api_maps/api_generic_result.dart @@ -3,6 +3,7 @@ class APIGenericResult { required this.success, required this.data, this.message, + this.code, }); /// Whether was a response successfully received, @@ -10,4 +11,5 @@ class APIGenericResult { final bool success; final String? message; final T data; + final int? code; } diff --git a/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart b/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart index 84acff43..c14aa98d 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart @@ -22,13 +22,13 @@ mixin JobsApi on ApiMap { return jobsList; } - Future> removeApiJob(final String uid) async { + Future> removeApiJob(final String uid) async { try { final GraphQLClient client = await getClient(); final variables = Variables$Mutation$RemoveJob(jobId: uid); final mutation = Options$Mutation$RemoveJob(variables: variables); final response = await client.mutate$RemoveJob(mutation); - return GenericMutationResult( + return APIGenericResult( data: response.parsedData?.removeJob.success ?? false, success: true, code: response.parsedData?.removeJob.code ?? 0, @@ -36,7 +36,7 @@ mixin JobsApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: false, success: false, code: 0, 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 5216ffa8..bb703103 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 @@ -31,16 +31,6 @@ part 'services_api.dart'; part 'users_api.dart'; part 'volume_api.dart'; -class GenericMutationResult extends APIGenericResult { - GenericMutationResult({ - required super.success, - required this.code, - required super.data, - super.message, - }); - final int code; -} - class ServerApi extends ApiMap with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi { ServerApi({ diff --git a/lib/logic/api_maps/graphql_maps/server_api/services_api.dart b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart index a2e85914..adfe806f 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/services_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart @@ -20,7 +20,7 @@ mixin ServicesApi on ApiMap { return services; } - Future> enableService( + Future> enableService( final String serviceId, ) async { try { @@ -28,7 +28,7 @@ mixin ServicesApi on ApiMap { final variables = Variables$Mutation$EnableService(serviceId: serviceId); final mutation = Options$Mutation$EnableService(variables: variables); final response = await client.mutate$EnableService(mutation); - return GenericMutationResult( + return APIGenericResult( data: response.parsedData?.enableService.success ?? false, success: true, code: response.parsedData?.enableService.code ?? 0, @@ -36,7 +36,7 @@ mixin ServicesApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: false, success: false, code: 0, @@ -45,7 +45,7 @@ mixin ServicesApi on ApiMap { } } - Future> disableService( + Future> disableService( final String serviceId, ) async { try { @@ -53,7 +53,7 @@ mixin ServicesApi on ApiMap { final variables = Variables$Mutation$DisableService(serviceId: serviceId); final mutation = Options$Mutation$DisableService(variables: variables); final response = await client.mutate$DisableService(mutation); - return GenericMutationResult( + return APIGenericResult( data: null, success: response.parsedData?.disableService.success ?? false, code: response.parsedData?.disableService.code ?? 0, @@ -61,7 +61,7 @@ mixin ServicesApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: null, success: false, code: 0, @@ -70,7 +70,7 @@ mixin ServicesApi on ApiMap { } } - Future> stopService( + Future> stopService( final String serviceId, ) async { try { @@ -78,7 +78,7 @@ mixin ServicesApi on ApiMap { final variables = Variables$Mutation$StopService(serviceId: serviceId); final mutation = Options$Mutation$StopService(variables: variables); final response = await client.mutate$StopService(mutation); - return GenericMutationResult( + return APIGenericResult( data: response.parsedData?.stopService.success ?? false, success: true, code: response.parsedData?.stopService.code ?? 0, @@ -86,7 +86,7 @@ mixin ServicesApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: false, success: false, code: 0, @@ -95,13 +95,13 @@ mixin ServicesApi on ApiMap { } } - Future startService(final String serviceId) async { + Future startService(final String serviceId) async { try { final GraphQLClient client = await getClient(); final variables = Variables$Mutation$StartService(serviceId: serviceId); final mutation = Options$Mutation$StartService(variables: variables); final response = await client.mutate$StartService(mutation); - return GenericMutationResult( + return APIGenericResult( data: null, success: response.parsedData?.startService.success ?? false, code: response.parsedData?.startService.code ?? 0, @@ -109,7 +109,7 @@ mixin ServicesApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: null, success: false, code: 0, @@ -118,7 +118,7 @@ mixin ServicesApi on ApiMap { } } - Future> restartService( + Future> restartService( final String serviceId, ) async { try { @@ -126,7 +126,7 @@ mixin ServicesApi on ApiMap { final variables = Variables$Mutation$RestartService(serviceId: serviceId); final mutation = Options$Mutation$RestartService(variables: variables); final response = await client.mutate$RestartService(mutation); - return GenericMutationResult( + return APIGenericResult( data: response.parsedData?.restartService.success ?? false, success: true, code: response.parsedData?.restartService.code ?? 0, @@ -134,7 +134,7 @@ mixin ServicesApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: false, success: false, code: 0, @@ -143,7 +143,7 @@ mixin ServicesApi on ApiMap { } } - Future> moveService( + Future> moveService( final String serviceId, final String destination, ) async { @@ -158,7 +158,7 @@ mixin ServicesApi on ApiMap { final mutation = Options$Mutation$MoveService(variables: variables); final response = await client.mutate$MoveService(mutation); final jobJson = response.parsedData?.moveService.job?.toJson(); - return GenericMutationResult( + return APIGenericResult( success: true, code: response.parsedData?.moveService.code ?? 0, message: response.parsedData?.moveService.message, @@ -166,7 +166,7 @@ mixin ServicesApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( success: false, code: 0, message: e.toString(), diff --git a/lib/logic/api_maps/graphql_maps/server_api/users_api.dart b/lib/logic/api_maps/graphql_maps/server_api/users_api.dart index c11f6a0e..f1851353 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/users_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/users_api.dart @@ -45,7 +45,7 @@ mixin UsersApi on ApiMap { return user; } - Future> createUser( + Future> createUser( final String username, final String password, ) async { @@ -56,7 +56,7 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$CreateUser(variables: variables); final response = await client.mutate$CreateUser(mutation); - return GenericMutationResult( + return APIGenericResult( success: true, code: response.parsedData?.createUser.code ?? 500, message: response.parsedData?.createUser.message, @@ -66,7 +66,7 @@ mixin UsersApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( success: false, code: 0, message: e.toString(), @@ -75,7 +75,7 @@ mixin UsersApi on ApiMap { } } - Future> deleteUser( + Future> deleteUser( final String username, ) async { try { @@ -83,7 +83,7 @@ mixin UsersApi on ApiMap { final variables = Variables$Mutation$DeleteUser(username: username); final mutation = Options$Mutation$DeleteUser(variables: variables); final response = await client.mutate$DeleteUser(mutation); - return GenericMutationResult( + return APIGenericResult( data: response.parsedData?.deleteUser.success ?? false, success: true, code: response.parsedData?.deleteUser.code ?? 500, @@ -91,7 +91,7 @@ mixin UsersApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: false, success: false, code: 500, @@ -100,7 +100,7 @@ mixin UsersApi on ApiMap { } } - Future> updateUser( + Future> updateUser( final String username, final String password, ) async { @@ -111,7 +111,7 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$UpdateUser(variables: variables); final response = await client.mutate$UpdateUser(mutation); - return GenericMutationResult( + return APIGenericResult( success: true, code: response.parsedData?.updateUser.code ?? 500, message: response.parsedData?.updateUser.message, @@ -121,7 +121,7 @@ mixin UsersApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: null, success: false, code: 0, @@ -130,7 +130,7 @@ mixin UsersApi on ApiMap { } } - Future> addSshKey( + Future> addSshKey( final String username, final String sshKey, ) async { @@ -144,7 +144,7 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$AddSshKey(variables: variables); final response = await client.mutate$AddSshKey(mutation); - return GenericMutationResult( + return APIGenericResult( success: true, code: response.parsedData?.addSshKey.code ?? 500, message: response.parsedData?.addSshKey.message, @@ -154,7 +154,7 @@ mixin UsersApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: null, success: false, code: 0, @@ -163,7 +163,7 @@ mixin UsersApi on ApiMap { } } - Future> removeSshKey( + Future> removeSshKey( final String username, final String sshKey, ) async { @@ -177,7 +177,7 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$RemoveSshKey(variables: variables); final response = await client.mutate$RemoveSshKey(mutation); - return GenericMutationResult( + return APIGenericResult( success: response.parsedData?.removeSshKey.success ?? false, code: response.parsedData?.removeSshKey.code ?? 500, message: response.parsedData?.removeSshKey.message, @@ -187,7 +187,7 @@ mixin UsersApi on ApiMap { ); } catch (e) { print(e); - return GenericMutationResult( + return APIGenericResult( data: null, success: false, code: 0, diff --git a/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart b/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart index 360dd491..e830cabd 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart @@ -57,10 +57,10 @@ mixin VolumeApi on ApiMap { } } - Future> migrateToBinds( + Future> migrateToBinds( final Map serviceToDisk, ) async { - GenericMutationResult? mutation; + APIGenericResult? mutation; try { final GraphQLClient client = await getClient(); @@ -78,7 +78,7 @@ mixin VolumeApi on ApiMap { await client.mutate$MigrateToBinds( migrateMutation, ); - mutation = mutation = GenericMutationResult( + mutation = mutation = APIGenericResult( success: true, code: result.parsedData!.migrateToBinds.code, message: result.parsedData!.migrateToBinds.message, @@ -86,7 +86,7 @@ mixin VolumeApi on ApiMap { ); } catch (e) { print(e); - mutation = GenericMutationResult( + mutation = APIGenericResult( success: false, code: 0, message: e.toString(), diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart index 86cda482..1ab5795c 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart @@ -114,10 +114,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { ); @override - Future createVolume() async { + Future> createVolume() async { ServerVolume? volume; - final Response createVolumeResponse; + Response? createVolumeResponse; final Dio client = await getClient(); try { final List volumes = await getVolumes(); @@ -146,11 +146,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { ); } catch (e) { print(e); + return APIGenericResult( + data: null, + success: false, + message: e.toString(), + ); } finally { client.close(); } - return volume; + return APIGenericResult( + data: volume, + success: true, + code: createVolumeResponse.statusCode, + message: createVolumeResponse.statusMessage, + ); } @override @@ -219,13 +229,13 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future attachVolume( + Future> attachVolume( final ServerVolume volume, final int serverId, ) async { bool success = false; - final Response attachVolumeResponse; + Response? attachVolumeResponse; final Dio client = await getClient(); try { attachVolumeResponse = await client.post( @@ -241,11 +251,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { attachVolumeResponse.data['action']['status'].toString() != 'error'; } catch (e) { print(e); + return APIGenericResult( + data: false, + success: false, + message: e.toString(), + ); } finally { close(client); } - return success; + return APIGenericResult( + data: success, + success: true, + code: attachVolumeResponse.statusCode, + message: attachVolumeResponse.statusMessage, + ); } @override @@ -323,7 +343,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future createServer({ + Future> createServer({ required final String dnsApiToken, required final User rootUser, required final String domainName, @@ -345,6 +365,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$formattedHostname bash 2>&1 | tee /tmp/infect.log"; print(userdataString); + Response? serverCreateResponse; final Dio client = await getClient(); try { final Map data = { @@ -356,14 +377,15 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { }; print('Decoded data: $data'); - final Response serverCreateResponse = await client.post( + serverCreateResponse = await client.post( '/droplets', data: data, ); final int serverId = serverCreateResponse.data['droplet']['id']; - final ServerVolume? newVolume = await createVolume(); - final bool attachedVolume = await attachVolume(newVolume!, serverId); + final ServerVolume? newVolume = (await createVolume()).data; + final bool attachedVolume = + (await attachVolume(newVolume!, serverId)).data; String? ipv4; int attempts = 0; @@ -391,11 +413,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } } catch (e) { print(e); + return APIGenericResult( + success: false, + data: null, + message: e.toString(), + ); } finally { close(client); } - return serverDetails; + return APIGenericResult( + data: serverDetails, + success: true, + code: serverCreateResponse.statusCode, + message: serverCreateResponse.statusMessage, + ); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index 4b51b27c..6e2049e9 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart @@ -140,10 +140,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } @override - Future createVolume() async { + Future> createVolume() async { ServerVolume? volume; - final Response createVolumeResponse; + Response? createVolumeResponse; final Dio client = await getClient(); try { createVolumeResponse = await client.post( @@ -171,11 +171,21 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } catch (e) { print(e); + return APIGenericResult( + data: null, + success: false, + message: e.toString(), + ); } finally { client.close(); } - return volume; + return APIGenericResult( + data: volume, + success: true, + code: createVolumeResponse.statusCode, + message: createVolumeResponse.statusMessage, + ); } @override @@ -259,13 +269,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } @override - Future attachVolume( + Future> attachVolume( final ServerVolume volume, final int serverId, ) async { bool success = false; - final Response attachVolumeResponse; + Response? attachVolumeResponse; final Dio client = await getClient(); try { attachVolumeResponse = await client.post( @@ -283,7 +293,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { client.close(); } - return success; + return APIGenericResult( + data: success, + success: true, + code: attachVolumeResponse?.statusCode, + message: attachVolumeResponse?.statusMessage, + ); } @override @@ -335,31 +350,33 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } @override - Future createServer({ + Future> createServer({ required final String dnsApiToken, required final User rootUser, required final String domainName, required final String serverType, }) async { - ServerHostingDetails? details; + final APIGenericResult newVolumeResponse = + await createVolume(); - final ServerVolume? newVolume = await createVolume(); - if (newVolume == null) { - return details; + if (!newVolumeResponse.success || newVolumeResponse.data == null) { + return APIGenericResult( + data: null, + success: false, + message: newVolumeResponse.message, + code: newVolumeResponse.code, + ); } - - details = await createServerWithVolume( + return createServerWithVolume( dnsApiToken: dnsApiToken, rootUser: rootUser, domainName: domainName, - volume: newVolume, + volume: newVolumeResponse.data!, serverType: serverType, ); - - return details; } - Future createServerWithVolume({ + Future> createServerWithVolume({ required final String dnsApiToken, required final User rootUser, required final String domainName, @@ -381,6 +398,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { final String userdataString = "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log"; + Response? serverCreateResponse; ServerHostingDetails? serverDetails; DioError? hetznerError; bool success = false; @@ -400,7 +418,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { }; print('Decoded data: $data'); - final Response serverCreateResponse = await client.post( + serverCreateResponse = await client.post( '/servers', data: data, ); @@ -428,11 +446,19 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { await deleteVolume(volume); } - if (hetznerError != null) { - throw hetznerError; + String? apiResultMessage = serverCreateResponse?.statusMessage; + if (hetznerError != null && + hetznerError.response!.data['error']['code'] == 'uniqueness_error') { + apiResultMessage = 'uniqueness_error'; } - return serverDetails; + return APIGenericResult( + data: serverDetails, + success: success && hetznerError == null, + code: serverCreateResponse?.statusCode ?? + hetznerError?.response?.statusCode, + message: apiResultMessage, + ); } static String getHostnameFromDomain(final String domain) { diff --git a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart index 21f6f376..05fb5e61 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart @@ -32,7 +32,7 @@ abstract class ServerProviderApi extends ApiMap { Future powerOn(); Future deleteServer({required final String domainName}); - Future createServer({ + Future> createServer({ required final String dnsApiToken, required final User rootUser, required final String domainName, diff --git a/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart index d3ae6f2a..5e01d268 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart @@ -1,12 +1,18 @@ +import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/price.dart'; +export 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; + mixin VolumeProviderApi on ApiMap { - Future createVolume(); + Future> createVolume(); Future> getVolumes({final String? status}); - Future attachVolume(final ServerVolume volume, final int serverId); + Future> attachVolume( + final ServerVolume volume, + final int serverId, + ); Future detachVolume(final ServerVolume volume); Future resizeVolume(final ServerVolume volume, final DiskSize size); Future deleteVolume(final ServerVolume volume); diff --git a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart index 7390eee1..21d17a84 100644 --- a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart @@ -3,7 +3,6 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/get_it/navigation.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:easy_localization/easy_localization.dart'; diff --git a/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart b/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart index 11e180d0..ff8eb797 100644 --- a/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart +++ b/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart @@ -113,10 +113,11 @@ class ApiProviderVolumeCubit } Future createVolume() async { - final ServerVolume? volume = await ApiController - .currentVolumeProviderApiFactory! - .getVolumeProvider() - .createVolume(); + final ServerVolume? volume = (await ApiController + .currentVolumeProviderApiFactory! + .getVolumeProvider() + .createVolume()) + .data; final diskVolume = DiskVolume(providerVolume: volume); await attachVolume(diskVolume); diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 80c3ee0d..b495ec0d 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -246,22 +246,52 @@ class ServerInstallationRepository { }) async { final ServerProviderApi api = ApiController.currentServerProviderApiFactory!.getServerProvider(); + + void showInstallationErrorPopUp() { + showPopUpAlert( + alertTitle: 'modals.unexpected_error'.tr(), + description: 'modals.try_again'.tr(), + actionButtonTitle: 'modals.yes'.tr(), + actionButtonOnPressed: () async { + ServerHostingDetails? serverDetails; + try { + final APIGenericResult createResult = await api.createServer( + dnsApiToken: cloudFlareKey, + rootUser: rootUser, + domainName: domainName, + serverType: getIt().serverType!, + ); + serverDetails = createResult.data; + } catch (e) { + print(e); + } + + if (serverDetails == null) { + print('Server is not initialized!'); + return; + } + await saveServerDetails(serverDetails); + onSuccess(serverDetails); + }, + cancelButtonOnPressed: onCancel, + ); + } + try { - final ServerHostingDetails? serverDetails = await api.createServer( + final APIGenericResult createServerResult = + await api.createServer( dnsApiToken: cloudFlareKey, rootUser: rootUser, domainName: domainName, serverType: getIt().serverType!, ); - if (serverDetails == null) { - print('Server is not initialized!'); - return; + if (createServerResult.data == null) { + const String e = 'Server is not initialized!'; + print(e); } - saveServerDetails(serverDetails); - onSuccess(serverDetails); - } on DioError catch (e) { - if (e.response!.data['error']['code'] == 'uniqueness_error') { + + if (createServerResult.message == 'uniqueness_error') { showPopUpAlert( alertTitle: 'modals.already_exists'.tr(), description: 'modals.destroy_server'.tr(), @@ -273,39 +303,13 @@ class ServerInstallationRepository { ServerHostingDetails? serverDetails; try { - serverDetails = await api.createServer( - dnsApiToken: cloudFlareKey, - rootUser: rootUser, - domainName: domainName, - serverType: getIt().serverType!, - ); - } catch (e) { - print(e); - } - - if (serverDetails == null) { - print('Server is not initialized!'); - return; - } - await saveServerDetails(serverDetails); - onSuccess(serverDetails); - }, - cancelButtonOnPressed: onCancel, - ); - } else { - showPopUpAlert( - alertTitle: 'modals.unexpected_error'.tr(), - description: 'modals.try_again'.tr(), - actionButtonTitle: 'modals.yes'.tr(), - actionButtonOnPressed: () async { - ServerHostingDetails? serverDetails; - try { - serverDetails = await api.createServer( + final APIGenericResult createResult = await api.createServer( dnsApiToken: cloudFlareKey, rootUser: rootUser, domainName: domainName, serverType: getIt().serverType!, ); + serverDetails = createResult.data; } catch (e) { print(e); } @@ -320,6 +324,12 @@ class ServerInstallationRepository { cancelButtonOnPressed: onCancel, ); } + + saveServerDetails(createServerResult.data!); + onSuccess(createServerResult.data!); + } catch (e) { + print(e); + showInstallationErrorPopUp(); } } diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 070fce2c..001ce8d0 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -78,7 +78,7 @@ class UsersCubit extends ServerInstallationDependendCubit { return; } // If API returned error, do nothing - final GenericMutationResult result = + final APIGenericResult result = await api.createUser(user.login, password); if (result.data == null) { getIt() @@ -101,7 +101,7 @@ class UsersCubit extends ServerInstallationDependendCubit { return; } final List loadedUsers = List.from(state.users); - final GenericMutationResult result = await api.deleteUser(user.login); + final APIGenericResult result = await api.deleteUser(user.login); if (result.success && result.data) { loadedUsers.removeWhere((final User u) => u.login == user.login); await box.clear(); @@ -128,7 +128,7 @@ class UsersCubit extends ServerInstallationDependendCubit { .showSnackBar('users.could_not_change_password'.tr()); return; } - final GenericMutationResult result = + final APIGenericResult result = await api.updateUser(user.login, newPassword); if (result.data == null) { getIt().showSnackBar( @@ -138,7 +138,7 @@ class UsersCubit extends ServerInstallationDependendCubit { } Future addSshKey(final User user, final String publicKey) async { - final GenericMutationResult result = + final APIGenericResult result = await api.addSshKey(user.login, publicKey); if (result.data != null) { final User updatedUser = result.data!; @@ -157,7 +157,7 @@ class UsersCubit extends ServerInstallationDependendCubit { } Future deleteSshKey(final User user, final String publicKey) async { - final GenericMutationResult result = + final APIGenericResult result = await api.removeSshKey(user.login, publicKey); if (result.data != null) { final User updatedUser = result.data!; From b5133aa2a6206cee969b82a532e569a66e31c193 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 2 Dec 2022 23:06:57 +0400 Subject: [PATCH 11/11] fix: Remove breaking installation changes --- .../server_installation/server_installation_repository.dart | 1 + .../cubit/server_installation/server_installation_state.dart | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index b495ec0d..05863b83 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -323,6 +323,7 @@ class ServerInstallationRepository { }, cancelButtonOnPressed: onCancel, ); + return; } saveServerDetails(createServerResult.data!); diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index 69934bbd..331c3e2a 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -48,7 +48,7 @@ abstract class ServerInstallationState extends Equatable { bool get isFullyInitilized => _fulfilementList.every((final el) => el!); ServerSetupProgress get progress => ServerSetupProgress - .values[_fulfilementList.where((final el) => el!).length + 1]; + .values[_fulfilementList.where((final el) => el!).length]; int get porgressBar { if (progress.index < 6) {