diff --git a/assets/translations/en.json b/assets/translations/en.json index 18e70c5b..d490583e 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.", "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..17cdbcad 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..81e1760a --- /dev/null +++ b/lib/logic/api_maps/api_generic_result.dart @@ -0,0 +1,15 @@ +class APIGenericResult { + APIGenericResult({ + required this.success, + required this.data, + this.message, + this.code, + }); + + /// 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; + 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 4370131f..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 @@ -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,36 +23,14 @@ 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 { - 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({ @@ -206,7 +185,7 @@ class ServerApi extends ApiMap return settings; } - Future> getRecoveryTokenStatus() async { + Future> getRecoveryTokenStatus() async { RecoveryKeyStatus? key; QueryResult response; String? error; @@ -223,18 +202,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 +234,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 +279,8 @@ class ServerApi extends ApiMap return records; } - Future>> getApiTokens() async { - GenericResult> tokens; + Future>> getApiTokens() async { + APIGenericResult> tokens; QueryResult response; try { @@ -310,7 +289,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 +303,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 +319,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 +337,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 +359,8 @@ class ServerApi extends ApiMap return returnable; } - Future> createDeviceToken() async { - GenericResult token; + Future> createDeviceToken() async { + APIGenericResult token; QueryResult response; try { @@ -393,19 +372,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 +396,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 +421,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 +443,10 @@ class ServerApi extends ApiMap return token; } - Future> useRecoveryToken( + Future> useRecoveryToken( final DeviceToken deviceToken, ) async { - GenericResult token; + APIGenericResult token; QueryResult response; try { @@ -489,19 +468,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/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/backblaze.dart b/lib/logic/api_maps/rest_maps/backblaze.dart index 5140311d..edc283b5 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,43 @@ 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; - } - return false; + isTokenValid = response.data['allowed']['capabilities'].contains('listBuckets'); } 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/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart index c79f0987..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 @@ -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 @@ -96,7 +113,7 @@ class CloudflareApi extends DnsProviderApi { } @override - Future removeSimilarRecords({ + Future> removeSimilarRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -122,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 @@ -166,7 +190,7 @@ class CloudflareApi extends DnsProviderApi { } @override - Future createMultipleDnsRecords({ + Future> createMultipleDnsRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -189,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 3ff6222e..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 @@ -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; @@ -11,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, }); @@ -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/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..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 @@ -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 @@ -99,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(); @@ -131,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 @@ -204,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( @@ -226,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 @@ -308,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, @@ -330,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 = { @@ -341,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; @@ -376,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 @@ -694,7 +741,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future> getAvailableLocations() async { + Future>> + getAvailableLocations() async { List locations = []; final Dio client = await getClient(); @@ -715,15 +763,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 = []; @@ -756,19 +809,26 @@ 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 - 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 57df7837..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 @@ -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 @@ -125,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( @@ -156,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 @@ -244,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( @@ -268,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 @@ -320,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, @@ -366,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; @@ -385,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, ); @@ -413,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) { @@ -692,7 +733,8 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } @override - Future> getAvailableLocations() async { + Future>> + getAvailableLocations() async { List locations = []; final Dio client = await getClient(); @@ -713,15 +755,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 = []; @@ -754,15 +801,20 @@ 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 - Future createReverseDns({ + Future> createReverseDns({ required final ServerHostingDetails serverDetails, required final ServerDomain domain, }) async { @@ -777,8 +829,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 a2eb71f3..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 @@ -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, @@ -19,8 +22,9 @@ class ProviderApiTokenValidation { abstract class ServerProviderApi extends ApiMap { Future> getServers(); - Future> getAvailableLocations(); - Future> getServerTypesByLocation({ + Future>> + getAvailableLocations(); + Future>> getServerTypesByLocation({ required final ServerProviderLocation location, }); @@ -28,18 +32,18 @@ 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, required final String serverType, }); - Future createReverseDns({ + Future> createReverseDns({ required final ServerHostingDetails serverDetails, 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/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/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/backblaze_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart index 2a9f4662..21d17a84 100644 --- a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart @@ -1,5 +1,6 @@ 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/models/hive/backblaze_credential.dart'; @@ -7,7 +8,6 @@ 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 +40,7 @@ class BackblazeFormCubit extends FormCubit { @override FutureOr asyncValidation() async { - late bool isKeyValid; + late APIGenericResult backblazeResponse; final BackblazeApi apiClient = BackblazeApi(isWithToken: false); try { @@ -48,18 +48,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; } } 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/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/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/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..c63154c0 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -76,18 +76,29 @@ 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); - Future isDnsProviderApiTokenValid( + if (!apiResponse.success) { + getIt().showSnackBar( + 'initializing.could_not_connect'.tr(), + ); + return null; + } + + return apiResponse.data; + } + + Future isDnsProviderApiTokenValid( final String providerToken, ) async { if (ApiController.currentDnsProviderApiFactory == null) { @@ -100,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 { @@ -112,9 +133,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( @@ -124,9 +154,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 { diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index cc3860e3..05863b83 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( @@ -247,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(), @@ -274,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,7 +323,14 @@ class ServerInstallationRepository { }, cancelButtonOnPressed: onCancel, ); + return; } + + saveServerDetails(createServerResult.data!); + onSuccess(createServerResult.data!); + } catch (e) { + print(e); + showInstallationErrorPopUp(); } } @@ -334,21 +344,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 +357,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; } @@ -479,7 +513,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 +550,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 +611,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), ); 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!; 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, ),