From bad692656797cc962385d2ed815054835555a98d Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 13 Feb 2023 18:13:32 +0400 Subject: [PATCH] chore: Continue refactoring - Rename APIGenericResult to GenericResult - Wrap all provider functions results with GenericResult - Move basic server commands and getters to business logic layer from API on Hetzner --- ...eneric_result.dart => generic_result.dart} | 4 +- .../graphql_maps/server_api/jobs_api.dart | 6 +- .../graphql_maps/server_api/server_api.dart | 68 +-- .../graphql_maps/server_api/services_api.dart | 36 +- .../graphql_maps/server_api/users_api.dart | 30 +- .../graphql_maps/server_api/volume_api.dart | 8 +- lib/logic/api_maps/rest_maps/backblaze.dart | 10 +- .../dns_providers/cloudflare/cloudflare.dart | 18 +- .../digital_ocean_dns/digital_ocean_dns.dart | 18 +- .../rest_maps/dns_providers/dns_provider.dart | 10 +- .../digital_ocean/digital_ocean_api.dart | 50 +-- .../server_providers/hetzner/hetzner_api.dart | 315 ++++---------- .../server_providers/server_provider.dart | 45 +- .../server_providers/volume_provider.dart | 19 +- lib/logic/cubit/devices/devices_cubit.dart | 7 +- .../initializing/backblaze_form_cubit.dart | 4 +- .../recovery_key/recovery_key_cubit.dart | 4 +- .../server_installation_cubit.dart | 27 +- .../server_installation_repository.dart | 25 +- lib/logic/cubit/users/users_cubit.dart | 10 +- lib/logic/models/server_metadata.dart | 4 +- lib/logic/providers/server_provider.dart | 14 +- .../providers/server_providers/hetzner.dart | 389 +++++++++++++++++- lib/ui/pages/server_details/text_details.dart | 2 +- 24 files changed, 634 insertions(+), 489 deletions(-) rename lib/logic/api_maps/{api_generic_result.dart => generic_result.dart} (85%) diff --git a/lib/logic/api_maps/api_generic_result.dart b/lib/logic/api_maps/generic_result.dart similarity index 85% rename from lib/logic/api_maps/api_generic_result.dart rename to lib/logic/api_maps/generic_result.dart index 81e1760a..5ce31561 100644 --- a/lib/logic/api_maps/api_generic_result.dart +++ b/lib/logic/api_maps/generic_result.dart @@ -1,5 +1,5 @@ -class APIGenericResult { - APIGenericResult({ +class GenericResult { + GenericResult({ required this.success, required this.data, this.message, 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 c14aa98d..03bfd1b3 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 9f863b1f..966b17bd 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,6 +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/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'; @@ -24,7 +24,7 @@ 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'; +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; part 'jobs_api.dart'; part 'server_actions_api.dart'; @@ -205,7 +205,7 @@ class ServerApi extends ApiMap return settings; } - Future> getRecoveryTokenStatus() async { + Future> getRecoveryTokenStatus() async { RecoveryKeyStatus? key; QueryResult response; String? error; @@ -222,18 +222,18 @@ class ServerApi extends ApiMap print(e); } - return APIGenericResult( + return GenericResult( success: error == null, data: key, message: error, ); } - Future> generateRecoveryToken( + Future> generateRecoveryToken( final DateTime? expirationDate, final int? numberOfUses, ) async { - APIGenericResult key; + GenericResult key; QueryResult response; try { @@ -254,19 +254,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - key = APIGenericResult( + key = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - key = APIGenericResult( + key = GenericResult( success: true, data: response.parsedData!.getNewRecoveryApiKey.key!, ); } catch (e) { print(e); - key = APIGenericResult( + key = GenericResult( success: false, data: '', message: e.toString(), @@ -299,8 +299,8 @@ class ServerApi extends ApiMap return records; } - Future>> getApiTokens() async { - APIGenericResult> tokens; + Future>> getApiTokens() async { + GenericResult> tokens; QueryResult response; try { @@ -309,7 +309,7 @@ class ServerApi extends ApiMap if (response.hasException) { final message = response.exception.toString(); print(message); - tokens = APIGenericResult>( + tokens = GenericResult>( success: false, data: [], message: message, @@ -323,13 +323,13 @@ class ServerApi extends ApiMap ApiToken.fromGraphQL(device), ) .toList(); - tokens = APIGenericResult>( + tokens = GenericResult>( success: true, data: parsed, ); } catch (e) { print(e); - tokens = APIGenericResult>( + tokens = GenericResult>( success: false, data: [], message: e.toString(), @@ -339,8 +339,8 @@ class ServerApi extends ApiMap return tokens; } - Future> deleteApiToken(final String name) async { - APIGenericResult returnable; + Future> deleteApiToken(final String name) async { + GenericResult returnable; QueryResult response; try { @@ -357,19 +357,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - returnable = APIGenericResult( + returnable = GenericResult( success: false, data: null, message: response.exception.toString(), ); } - returnable = APIGenericResult( + returnable = GenericResult( success: true, data: null, ); } catch (e) { print(e); - returnable = APIGenericResult( + returnable = GenericResult( success: false, data: null, message: e.toString(), @@ -379,8 +379,8 @@ class ServerApi extends ApiMap return returnable; } - Future> createDeviceToken() async { - APIGenericResult token; + Future> createDeviceToken() async { + GenericResult token; QueryResult response; try { @@ -392,19 +392,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = APIGenericResult( + token = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = APIGenericResult( + token = GenericResult( success: true, data: response.parsedData!.getNewDeviceApiKey.key!, ); } catch (e) { print(e); - token = APIGenericResult( + token = GenericResult( success: false, data: '', message: e.toString(), @@ -416,10 +416,10 @@ class ServerApi extends ApiMap Future isHttpServerWorking() async => (await getApiVersion()) != null; - Future> authorizeDevice( + Future> authorizeDevice( final DeviceToken deviceToken, ) async { - APIGenericResult token; + GenericResult token; QueryResult response; try { @@ -441,19 +441,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = APIGenericResult( + token = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = APIGenericResult( + token = GenericResult( success: true, data: response.parsedData!.authorizeWithNewDeviceApiKey.token!, ); } catch (e) { print(e); - token = APIGenericResult( + token = GenericResult( success: false, data: '', message: e.toString(), @@ -463,10 +463,10 @@ class ServerApi extends ApiMap return token; } - Future> useRecoveryToken( + Future> useRecoveryToken( final DeviceToken deviceToken, ) async { - APIGenericResult token; + GenericResult token; QueryResult response; try { @@ -488,19 +488,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = APIGenericResult( + token = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = APIGenericResult( + token = GenericResult( success: true, data: response.parsedData!.useRecoveryApiKey.token!, ); } catch (e) { print(e); - token = APIGenericResult( + token = GenericResult( 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 adfe806f..9d39f137 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 f1851353..cca78798 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 APIGenericResult( + return GenericResult( 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 e830cabd..3459e6f9 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 { - APIGenericResult? mutation; + GenericResult? mutation; try { final GraphQLClient client = await getClient(); @@ -78,7 +78,7 @@ mixin VolumeApi on ApiMap { await client.mutate$MigrateToBinds( migrateMutation, ); - mutation = mutation = APIGenericResult( + mutation = mutation = GenericResult( 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 = APIGenericResult( + mutation = GenericResult( 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 8ea94803..6fe5cd8e 100644 --- a/lib/logic/api_maps/rest_maps/backblaze.dart +++ b/lib/logic/api_maps/rest_maps/backblaze.dart @@ -2,11 +2,11 @@ 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/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'; +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; class BackblazeApiAuth { BackblazeApiAuth({required this.authorizationToken, required this.apiUrl}); @@ -74,7 +74,7 @@ class BackblazeApi extends ApiMap { ); } - Future> isApiTokenValid( + Future> isApiTokenValid( final String encodedApiKey, ) async { final Dio client = await getClient(); @@ -99,7 +99,7 @@ class BackblazeApi extends ApiMap { } } on DioError catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: false, success: false, message: e.toString(), @@ -108,7 +108,7 @@ class BackblazeApi extends ApiMap { close(client); } - return APIGenericResult( + return GenericResult( data: isTokenValid, success: true, ); 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 1da08c40..d9ac5c6c 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,7 +46,7 @@ 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 = ''; @@ -70,7 +70,7 @@ class CloudflareApi extends DnsProviderApi { } if (response == null) { - return APIGenericResult( + return GenericResult( data: isValid, success: false, message: message, @@ -85,7 +85,7 @@ class CloudflareApi extends DnsProviderApi { throw Exception('code: ${response.statusCode}'); } - return APIGenericResult( + return GenericResult( data: isValid, success: true, message: response.statusMessage, @@ -113,7 +113,7 @@ class CloudflareApi extends DnsProviderApi { } @override - Future> removeSimilarRecords({ + Future> removeSimilarRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -139,7 +139,7 @@ class CloudflareApi extends DnsProviderApi { await Future.wait(allDeleteFutures); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: null, message: e.toString(), @@ -148,7 +148,7 @@ class CloudflareApi extends DnsProviderApi { close(client); } - return APIGenericResult(success: true, data: null); + return GenericResult(success: true, data: null); } @override @@ -272,7 +272,7 @@ class CloudflareApi extends DnsProviderApi { } @override - Future> createMultipleDnsRecords({ + Future> createMultipleDnsRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -298,7 +298,7 @@ class CloudflareApi extends DnsProviderApi { rethrow; } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: null, message: e.toString(), @@ -307,7 +307,7 @@ class CloudflareApi extends DnsProviderApi { close(client); } - return APIGenericResult(success: true, data: null); + return GenericResult(success: true, data: null); } @override diff --git a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart index b5b33f98..fd2b2e02 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns.dart @@ -46,7 +46,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { String rootAddress = 'https://api.digitalocean.com/v2'; @override - Future> isApiTokenValid(final String token) async { + Future> isApiTokenValid(final String token) async { bool isValid = false; Response? response; String message = ''; @@ -70,7 +70,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { } if (response == null) { - return APIGenericResult( + return GenericResult( data: isValid, success: false, message: message, @@ -85,7 +85,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { throw Exception('code: ${response.statusCode}'); } - return APIGenericResult( + return GenericResult( data: isValid, success: true, message: response.statusMessage, @@ -97,7 +97,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { Future getZoneId(final String domain) async => domain; @override - Future> removeSimilarRecords({ + Future> removeSimilarRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -118,7 +118,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { await Future.wait(allDeleteFutures); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: null, message: e.toString(), @@ -127,7 +127,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { close(client); } - return APIGenericResult(success: true, data: null); + return GenericResult(success: true, data: null); } @override @@ -262,7 +262,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { } @override - Future> createMultipleDnsRecords({ + Future> createMultipleDnsRecords({ required final ServerDomain domain, final String? ip4, }) async { @@ -292,7 +292,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { rethrow; } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: null, message: e.toString(), @@ -301,7 +301,7 @@ class DigitalOceanDnsApi extends DnsProviderApi { close(client); } - return APIGenericResult(success: true, data: null); + return GenericResult(success: true, data: null); } @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 0d010242..299928b0 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,10 +1,10 @@ -import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.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'; +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; export 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; class DomainNotFoundException implements Exception { @@ -21,11 +21,11 @@ abstract class DnsProviderApi extends ApiMap { final String? ipAddress, final String? dkimPublicKey, }); - Future> removeSimilarRecords({ + Future> removeSimilarRecords({ required final ServerDomain domain, final String? ip4, }); - Future> createMultipleDnsRecords({ + Future> createMultipleDnsRecords({ required final ServerDomain domain, final String? ip4, }); @@ -36,7 +36,7 @@ abstract class DnsProviderApi extends ApiMap { Future getZoneId(final String domain); Future> domainList(); - Future> isApiTokenValid(final String token); + Future> isApiTokenValid(final String token); RegExp getApiTokenValidation(); List getProjectDnsRecords( diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart index 540b39c5..3d05d4db 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart @@ -60,7 +60,7 @@ 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 = ''; @@ -84,7 +84,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } if (response == null) { - return APIGenericResult( + return GenericResult( data: isValid, success: false, message: message, @@ -99,7 +99,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { throw Exception('code: ${response.statusCode}'); } - return APIGenericResult( + return GenericResult( data: isValid, success: true, message: response.statusMessage, @@ -115,7 +115,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { ); @override - Future> createVolume() async { + Future> createVolume() async { ServerVolume? volume; Response? createVolumeResponse; @@ -147,7 +147,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { ); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: null, success: false, message: e.toString(), @@ -156,7 +156,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { client.close(); } - return APIGenericResult( + return GenericResult( data: volume, success: true, code: createVolumeResponse.statusCode, @@ -230,7 +230,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future> attachVolume( + Future> attachVolume( final ServerVolume volume, final int serverId, ) async { @@ -252,7 +252,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { attachVolumeResponse.data['action']['status'].toString() != 'error'; } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: false, success: false, message: e.toString(), @@ -261,7 +261,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult( + return GenericResult( data: success, success: true, code: attachVolumeResponse.statusCode, @@ -327,7 +327,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future> createServer({ + Future> createServer({ required final String dnsApiToken, required final User rootUser, required final String domainName, @@ -399,7 +399,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: null, message: e.toString(), @@ -408,7 +408,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult( + return GenericResult( data: serverDetails, success: true, code: serverCreateResponse.statusCode, @@ -417,7 +417,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future> deleteServer({ + Future> deleteServer({ required final String domainName, }) async { final Dio client = await getClient(); @@ -431,7 +431,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { ); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: false, success: false, message: e.toString(), @@ -446,7 +446,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { ); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: false, success: false, message: e.toString(), @@ -464,7 +464,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { await Future.wait(laterFutures); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: false, message: e.toString(), @@ -473,7 +473,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult( + return GenericResult( success: true, data: true, ); @@ -762,7 +762,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future>> + Future>> getAvailableLocations() async { List locations = []; @@ -784,7 +784,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { .toList(); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: [], success: false, message: e.toString(), @@ -793,11 +793,11 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult(data: locations, success: true); + return GenericResult(data: locations, success: true); } @override - Future>> getServerTypesByLocation({ + Future>> getAvailableServerTypes({ required final ServerProviderLocation location, }) async { final List types = []; @@ -830,7 +830,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: [], success: false, message: e.toString(), @@ -839,17 +839,17 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult(data: types, success: true); + return GenericResult(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); + return GenericResult(success: success, data: null); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart index cfd9eb40..f43fc050 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart @@ -17,7 +17,6 @@ import 'package:selfprivacy/logic/models/price.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart'; 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'; import 'package:selfprivacy/utils/extensions/string_extensions.dart'; import 'package:selfprivacy/utils/network_utils.dart'; import 'package:selfprivacy/utils/password_generator.dart'; @@ -60,8 +59,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { @override String get displayProviderName => 'Hetzner'; - @override - Future> isApiTokenValid(final String token) async { + Future> isApiTokenValid(final String token) async { bool isValid = false; Response? response; String message = ''; @@ -85,7 +83,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } if (response == null) { - return APIGenericResult( + return GenericResult( data: isValid, success: false, message: message, @@ -100,21 +98,19 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { throw Exception('code: ${response.statusCode}'); } - return APIGenericResult( + return GenericResult( data: isValid, success: true, message: response.statusMessage, ); } - @override ProviderApiTokenValidation getApiTokenValidation() => ProviderApiTokenValidation( regexp: RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'), length: 64, ); - @override Future getPricePerGb() async { double? price; @@ -140,8 +136,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } - @override - Future> createVolume() async { + Future> createVolume() async { ServerVolume? volume; Response? createVolumeResponse; @@ -172,7 +167,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: null, success: false, message: e.toString(), @@ -181,7 +176,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { client.close(); } - return APIGenericResult( + return GenericResult( data: volume, success: true, code: createVolumeResponse.statusCode, @@ -189,7 +184,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } - @override Future> getVolumes({final String? status}) async { final List volumes = []; @@ -257,7 +251,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { return volume; } - @override Future deleteVolume(final ServerVolume volume) async { final Dio client = await getClient(); try { @@ -269,8 +262,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } } - @override - Future> attachVolume( + Future> attachVolume( final ServerVolume volume, final int serverId, ) async { @@ -294,7 +286,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { client.close(); } - return APIGenericResult( + return GenericResult( data: success, success: true, code: attachVolumeResponse?.statusCode, @@ -302,7 +294,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } - @override Future detachVolume(final ServerVolume volume) async { bool success = false; @@ -323,7 +314,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { return success; } - @override Future resizeVolume( final ServerVolume volume, final DiskSize size, @@ -350,19 +340,17 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { return success; } - @override - Future> createServer({ + Future> createServer({ required final String dnsApiToken, required final User rootUser, required final String domainName, required final String serverType, required final DnsProviderType dnsProvider, }) async { - final APIGenericResult newVolumeResponse = - await createVolume(); + final GenericResult newVolumeResponse = await createVolume(); if (!newVolumeResponse.success || newVolumeResponse.data == null) { - return APIGenericResult( + return GenericResult( data: null, success: false, message: newVolumeResponse.message, @@ -379,7 +367,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } - Future> createServerWithVolume({ + Future> createServerWithVolume({ required final String dnsApiToken, required final User rootUser, required final String domainName, @@ -457,7 +445,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { apiResultMessage = 'uniqueness_error'; } - return APIGenericResult( + return GenericResult( data: serverDetails, success: success && hetznerError == null, code: serverCreateResponse?.statusCode ?? @@ -466,8 +454,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { ); } - @override - Future> deleteServer({ + Future> deleteServer({ required final String domainName, }) async { final Dio client = await getClient(); @@ -494,7 +481,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { await Future.wait(laterFutures); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: false, message: e.toString(), @@ -503,45 +490,55 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult( + return GenericResult( success: true, data: true, ); } - @override - Future restart() async { - final ServerHostingDetails server = getIt().serverDetails!; - + Future> restart(final int serverId) async { final Dio client = await getClient(); try { - await client.post('/servers/${server.id}/actions/reset'); + await client.post('/servers/$serverId/actions/reset'); } catch (e) { print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); } finally { close(client); } - return server.copyWith(startTime: DateTime.now()); + return GenericResult( + success: true, + data: null, + ); } - @override - Future powerOn() async { - final ServerHostingDetails server = getIt().serverDetails!; - + Future> powerOn(final int serverId) async { final Dio client = await getClient(); try { - await client.post('/servers/${server.id}/actions/poweron'); + await client.post('/servers/$serverId/actions/poweron'); } catch (e) { print(e); + return GenericResult( + success: false, + data: null, + message: e.toString(), + ); } finally { close(client); } - return server.copyWith(startTime: DateTime.now()); + return GenericResult( + success: true, + data: null, + ); } - Future> requestRawMetrics( + Future>> getMetrics( final int serverId, final DateTime start, final DateTime end, @@ -562,127 +559,20 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { metrics = res.data['metrics']; } catch (e) { print(e); + return GenericResult( + success: false, + data: {}, + message: e.toString(), + ); } finally { close(client); } - return metrics; + return GenericResult(data: metrics, success: true); } - List serializeTimeSeries( - final Map json, - final String type, - ) { - final List list = json['time_series'][type]['values']; - return list - .map((final el) => TimeSeriesData(el[0], double.parse(el[1]))) - .toList(); - } - - @override - Future getMetrics( - final int serverId, - final DateTime start, - final DateTime end, - ) async { - ServerMetrics? metrics; - - final Map rawCpuMetrics = await requestRawMetrics( - serverId, - start, - end, - 'cpu', - ); - final Map rawNetworkMetrics = await requestRawMetrics( - serverId, - start, - end, - 'network', - ); - - if (rawNetworkMetrics.isEmpty || rawCpuMetrics.isEmpty) { - return metrics; - } - - metrics = ServerMetrics( - cpu: serializeTimeSeries( - rawCpuMetrics, - 'cpu', - ), - bandwidthIn: serializeTimeSeries( - rawNetworkMetrics, - 'network.0.bandwidth.in', - ), - bandwidthOut: serializeTimeSeries( - rawNetworkMetrics, - 'network.0.bandwidth.out', - ), - end: end, - start: start, - stepsInSecond: rawCpuMetrics['step'], - ); - - return metrics; - } - - @override - Future> getMetadata(final int serverId) async { - List metadata = []; - - final Dio client = await getClient(); - try { - final Response response = await client.get('/servers/$serverId'); - final hetznerInfo = HetznerServerInfo.fromJson(response.data!['server']); - metadata = [ - ServerMetadataEntity( - type: MetadataType.id, - name: 'server.server_id'.tr(), - value: hetznerInfo.id.toString(), - ), - ServerMetadataEntity( - type: MetadataType.status, - name: 'server.status'.tr(), - value: hetznerInfo.status.toString().split('.')[1].capitalize(), - ), - ServerMetadataEntity( - type: MetadataType.cpu, - name: 'server.cpu'.tr(), - value: 'server.core_count'.plural(hetznerInfo.serverType.cores), - ), - ServerMetadataEntity( - type: MetadataType.ram, - name: 'server.ram'.tr(), - value: '${hetznerInfo.serverType.memory.toString()} GB', - ), - ServerMetadataEntity( - type: MetadataType.cost, - name: 'server.monthly_cost'.tr(), - value: hetznerInfo.serverType.prices[1].monthly.toStringAsFixed(2), - ), - ServerMetadataEntity( - type: MetadataType.location, - name: 'server.location'.tr(), - value: - '${hetznerInfo.location.city}, ${hetznerInfo.location.country}', - ), - ServerMetadataEntity( - type: MetadataType.other, - name: 'server.provider'.tr(), - value: displayProviderName, - ), - ]; - } catch (e) { - print(e); - } finally { - close(client); - } - - return metadata; - } - - @override - Future> getServers() async { - List servers = []; + Future>> getServers() async { + List servers = []; final Dio client = await getClient(); try { @@ -691,53 +581,22 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { .map( (final e) => HetznerServerInfo.fromJson(e), ) - .toList() - .where( - (final server) => server.publicNet.ipv4 != null, - ) - .map( - (final server) => ServerBasicInfo( - id: server.id, - name: server.name, - ip: server.publicNet.ipv4.ip, - reverseDns: server.publicNet.ipv4.reverseDns, - created: server.created, - ), - ) .toList(); } catch (e) { print(e); + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); } finally { close(client); } - print(servers); - return servers; + return GenericResult(data: servers, success: true); } - String? getEmojiFlag(final String query) { - String? emoji; - - switch (query.toLowerCase()) { - case 'de': - emoji = '🇩🇪'; - break; - - case 'fi': - emoji = '🇫🇮'; - break; - - case 'us': - emoji = '🇺🇸'; - break; - } - - return emoji; - } - - @override - Future>> - getAvailableLocations() async { + Future> getAvailableLocations() async { List locations = []; final Dio client = await getClient(); @@ -746,19 +605,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { '/locations', ); - locations = response.data!['locations'] - .map( - (final location) => ServerProviderLocation( - title: location['city'], - description: location['description'], - flag: getEmojiFlag(location['country']), - identifier: location['name'], - ), - ) - .toList(); + locations = response.data!['locations']; } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: [], message: e.toString(), @@ -767,44 +617,21 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult(success: true, data: locations); + return GenericResult(success: true, data: locations); } - @override - Future>> getServerTypesByLocation({ - required final ServerProviderLocation location, - }) async { - final List types = []; + Future> getAvailableServerTypes() async { + List types = []; final Dio client = await getClient(); try { final Response response = await client.get( '/server_types', ); - final rawTypes = response.data!['server_types']; - for (final rawType in rawTypes) { - for (final rawPrice in rawType['prices']) { - if (rawPrice['location'].toString() == location.identifier) { - types.add( - ServerType( - title: rawType['description'], - identifier: rawType['name'], - ram: rawType['memory'], - cores: rawType['cores'], - disk: DiskSize(byte: rawType['disk'] * 1024 * 1024 * 1024), - price: Price( - value: double.parse(rawPrice['price_monthly']['gross']), - currency: 'EUR', - ), - location: location, - ), - ); - } - } - } + types = response.data!['server_types']; } catch (e) { print(e); - return APIGenericResult( + return GenericResult( data: [], success: false, message: e.toString(), @@ -813,26 +640,26 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult(data: types, success: true); + return GenericResult(data: types, success: true); } - @override - Future> createReverseDns({ - required final ServerHostingDetails serverDetails, - required final ServerDomain domain, + Future> createReverseDns({ + required final int serverId, + required final String ip4, + required final String dnsPtr, }) async { final Dio client = await getClient(); try { await client.post( - '/servers/${serverDetails.id}/actions/change_dns_ptr', + '/servers/$serverId/actions/change_dns_ptr', data: { - 'ip': serverDetails.ip4, - 'dns_ptr': domain.domainName, + 'ip': ip4, + 'dns_ptr': dnsPtr, }, ); } catch (e) { print(e); - return APIGenericResult( + return GenericResult( success: false, data: null, message: e.toString(), @@ -841,6 +668,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { close(client); } - return APIGenericResult(success: true, data: null); + return GenericResult(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 99516535..99c6b1ac 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,15 +1,6 @@ -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'; -import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/logic/models/metrics.dart'; -import 'package:selfprivacy/logic/models/server_basic_info.dart'; -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'; +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; class ProviderApiTokenValidation { ProviderApiTokenValidation({ @@ -21,40 +12,6 @@ class ProviderApiTokenValidation { } abstract class ServerProviderApi extends ApiMap { - Future> getServers(); - Future>> - getAvailableLocations(); - Future>> getServerTypesByLocation({ - required final ServerProviderLocation location, - }); - - Future restart(); - Future powerOn(); - - Future> deleteServer({ - required final String domainName, - }); - Future> createServer({ - required final String dnsApiToken, - required final User rootUser, - required final String domainName, - required final String serverType, - required final DnsProviderType dnsProvider, - }); - Future> createReverseDns({ - required final ServerHostingDetails serverDetails, - required final ServerDomain domain, - }); - - Future> isApiTokenValid(final String token); - ProviderApiTokenValidation getApiTokenValidation(); - Future> getMetadata(final int serverId); - Future getMetrics( - final int serverId, - final DateTime start, - final DateTime end, - ); - String dnsProviderToInfectName(final DnsProviderType dnsProvider) { String dnsProviderType; switch (dnsProvider) { 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 5e01d268..5ddacd6d 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,20 +1,5 @@ -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'; +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; -mixin VolumeProviderApi on ApiMap { - Future> createVolume(); - Future> getVolumes({final String? status}); - 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); - Future getPricePerGb(); -} +mixin VolumeProviderApi on ApiMap {} diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index 5e5c145c..10ad943d 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 APIGenericResult> response = await api.getApiTokens(); + final GenericResult> response = await api.getApiTokens(); if (response.success) { return response.data; } else { @@ -44,8 +44,7 @@ class ApiDevicesCubit } Future deleteDevice(final ApiToken device) async { - final APIGenericResult response = - await api.deleteApiToken(device.name); + final GenericResult response = await api.deleteApiToken(device.name); if (response.success) { emit( ApiDevicesState( @@ -60,7 +59,7 @@ class ApiDevicesCubit } Future getNewDeviceKey() async { - final APIGenericResult response = await api.createDeviceToken(); + final GenericResult 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 21d17a84..af20a8aa 100644 --- a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart @@ -40,7 +40,7 @@ class BackblazeFormCubit extends FormCubit { @override FutureOr asyncValidation() async { - late APIGenericResult backblazeResponse; + late GenericResult backblazeResponse; final BackblazeApi apiClient = BackblazeApi(isWithToken: false); try { @@ -51,7 +51,7 @@ class BackblazeFormCubit extends FormCubit { backblazeResponse = await apiClient.isApiTokenValid(encodedApiKey); } catch (e) { addError(e); - backblazeResponse = APIGenericResult( + backblazeResponse = GenericResult( success: false, data: false, message: e.toString(), diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart index 56800be3..76a572d1 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 APIGenericResult response = + final GenericResult response = await api.getRecoveryTokenStatus(); if (response.success) { return response.data; @@ -57,7 +57,7 @@ class RecoveryKeyCubit final DateTime? expirationDate, final int? numberOfUses, }) async { - final APIGenericResult response = + final GenericResult 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 d6928e40..17d90764 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -77,8 +77,8 @@ class ServerInstallationCubit extends Cubit { Future isServerProviderApiTokenValid( final String providerToken, ) async { - final APIGenericResult apiResponse = - await ProvidersController.currentServerProvider!.isApiTokenValid( + final GenericResult apiResponse = + await ProvidersController.currentServerProvider!.tryInitApiByToken( providerToken, ); @@ -95,7 +95,7 @@ class ServerInstallationCubit extends Cubit { Future isDnsProviderApiTokenValid( final String providerToken, ) async { - final APIGenericResult apiResponse = + final GenericResult apiResponse = await ApiController.currentDnsProviderApiFactory! .getDnsProvider( settings: const DnsProviderApiSettings(isWithToken: false), @@ -117,7 +117,7 @@ class ServerInstallationCubit extends Cubit { return []; } - final APIGenericResult apiResponse = await ProvidersController + final GenericResult apiResponse = await ProvidersController .currentServerProvider! .getAvailableLocations(); @@ -137,7 +137,7 @@ class ServerInstallationCubit extends Cubit { return []; } - final APIGenericResult apiResult = await ProvidersController + final GenericResult apiResult = await ProvidersController .currentServerProvider! .getServerTypes(location: location); @@ -173,21 +173,8 @@ class ServerInstallationCubit extends Cubit { void setServerType(final ServerType serverType) async { await repository.saveServerType(serverType); - ApiController.initServerProviderApiFactory( - ServerProviderSettings( - provider: getIt().serverProvider!, - location: serverType.location.identifier, - ), - ); - - // All server providers support volumes for now, - // so it's safe to initialize. - ApiController.initVolumeProviderApiFactory( - ServerProviderSettings( - provider: getIt().serverProvider!, - location: serverType.location.identifier, - ), - ); + await ProvidersController.currentServerProvider! + .trySetServerType(serverType); emit( (state as ServerInstallationNotFinished).copyWith( diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 50eb7998..d60b2119 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -259,7 +259,7 @@ class ServerInstallationRepository { actionButtonOnPressed: () async { ServerHostingDetails? serverDetails; try { - final APIGenericResult createResult = await api.createServer( + final GenericResult createResult = await api.createServer( dnsProvider: getIt().dnsProvider!, dnsApiToken: cloudFlareKey, rootUser: rootUser, @@ -283,7 +283,7 @@ class ServerInstallationRepository { } try { - final APIGenericResult createServerResult = + final GenericResult createServerResult = await api.createServer( dnsProvider: getIt().dnsProvider!, dnsApiToken: cloudFlareKey, @@ -309,7 +309,7 @@ class ServerInstallationRepository { ServerHostingDetails? serverDetails; try { - final APIGenericResult createResult = await api.createServer( + final GenericResult createResult = await api.createServer( dnsProvider: getIt().dnsProvider!, dnsApiToken: cloudFlareKey, rootUser: rootUser, @@ -366,7 +366,7 @@ class ServerInstallationRepository { ); } - final APIGenericResult removingResult = + final GenericResult removingResult = await dnsProviderApi.removeSimilarRecords( ip4: serverDetails.ip4, domain: domain, @@ -380,7 +380,7 @@ class ServerInstallationRepository { bool createdSuccessfully = false; String errorMessage = 'domain.error'.tr(); try { - final APIGenericResult createResult = + final GenericResult createResult = await dnsProviderApi.createMultipleDnsRecords( ip4: serverDetails.ip4, domain: domain, @@ -397,8 +397,7 @@ class ServerInstallationRepository { return false; } - final APIGenericResult createReverseResult = - await serverApi.createReverseDns( + final GenericResult createReverseResult = await serverApi.createReverseDns( serverDetails: serverDetails, domain: domain, ); @@ -520,7 +519,7 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final APIGenericResult result = await serverApi.authorizeDevice( + final GenericResult result = await serverApi.authorizeDevice( DeviceToken(device: await getDeviceName(), token: newDeviceKey), ); @@ -557,7 +556,7 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final APIGenericResult result = await serverApi.useRecoveryToken( + final GenericResult result = await serverApi.useRecoveryToken( DeviceToken(device: await getDeviceName(), token: recoveryKey), ); @@ -618,9 +617,9 @@ class ServerInstallationRepository { ); } } - final APIGenericResult deviceAuthKey = + final GenericResult deviceAuthKey = await serverApi.createDeviceToken(); - final APIGenericResult result = await serverApi.authorizeDevice( + final GenericResult result = await serverApi.authorizeDevice( DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data), ); @@ -771,7 +770,7 @@ class ServerInstallationRepository { } Future deleteServer(final ServerDomain serverDomain) async { - final APIGenericResult deletionResult = await ApiController + final GenericResult deletionResult = await ApiController .currentServerProviderApiFactory! .getServerProvider() .deleteServer( @@ -797,7 +796,7 @@ class ServerInstallationRepository { await box.put(BNames.isLoading, false); await box.put(BNames.serverDetails, null); - final APIGenericResult removalResult = await ApiController + final GenericResult removalResult = await ApiController .currentDnsProviderApiFactory! .getDnsProvider() .removeSimilarRecords(domain: serverDomain); diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 001ce8d0..de31cdcd 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 APIGenericResult result = + final GenericResult 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 APIGenericResult result = await api.deleteUser(user.login); + final GenericResult 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 APIGenericResult result = + final GenericResult 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 APIGenericResult result = + final GenericResult 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 APIGenericResult result = + final GenericResult result = await api.removeSshKey(user.login, publicKey); if (result.data != null) { final User updatedUser = result.data!; diff --git a/lib/logic/models/server_metadata.dart b/lib/logic/models/server_metadata.dart index 0275a2ef..553fcbb5 100644 --- a/lib/logic/models/server_metadata.dart +++ b/lib/logic/models/server_metadata.dart @@ -19,11 +19,11 @@ enum MetadataType { class ServerMetadataEntity { ServerMetadataEntity({ - required this.name, + required this.trId, required this.value, this.type = MetadataType.other, }); final MetadataType type; - final String name; + final String trId; final String value; } diff --git a/lib/logic/providers/server_provider.dart b/lib/logic/providers/server_provider.dart index aca7f477..e5c1c1bf 100644 --- a/lib/logic/providers/server_provider.dart +++ b/lib/logic/providers/server_provider.dart @@ -1,12 +1,16 @@ -import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/models/server_provider_location.dart'; import 'package:selfprivacy/logic/models/server_type.dart'; +export 'package:selfprivacy/logic/api_maps/generic_result.dart'; + abstract class ServerProvider { - Future> isApiTokenValid(final String apiToken); - Future>> - getAvailableLocations(); - Future>> getServerTypes({ + Future> trySetServerType(final ServerType type); + Future> tryInitApiByToken(final String token); + Future>> getAvailableLocations(); + Future>> getServerTypes({ required final ServerProviderLocation location, }); + + GenericResult get success => GenericResult(success: true, data: true); } diff --git a/lib/logic/providers/server_providers/hetzner.dart b/lib/logic/providers/server_providers/hetzner.dart index 2e4ff1e8..fdc8f11e 100644 --- a/lib/logic/providers/server_providers/hetzner.dart +++ b/lib/logic/providers/server_providers/hetzner.dart @@ -1,3 +1,390 @@ +import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart'; +import 'package:selfprivacy/logic/models/disk_size.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/models/metrics.dart'; +import 'package:selfprivacy/logic/models/price.dart'; +import 'package:selfprivacy/logic/models/server_basic_info.dart'; +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'; import 'package:selfprivacy/logic/providers/server_provider.dart'; +import 'package:selfprivacy/utils/extensions/string_extensions.dart'; -class HetznerServerProvider extends ServerProvider {} +class ApiAdapter { + ApiAdapter({final String? region, final bool isWithToken = true}) + : _api = HetznerApi( + region: region, + isWithToken: isWithToken, + ); + + HetznerApi api({final bool getInitialized = true}) => getInitialized + ? _api + : HetznerApi( + region: _api.region, + isWithToken: false, + ); + + final HetznerApi _api; +} + +class HetznerServerProvider extends ServerProvider { + HetznerServerProvider() : _adapter = ApiAdapter(); + HetznerServerProvider.load( + final ServerType serverType, + final bool isAuthotized, + ) : _adapter = ApiAdapter( + isWithToken: isAuthotized, + region: serverType.location.identifier, + ); + + ApiAdapter _adapter; + + @override + Future> trySetServerType(final ServerType type) async { + final bool apiInitialized = _adapter.api().isWithToken; + if (!apiInitialized) { + return GenericResult( + success: true, + data: false, + message: 'Not authorized!', + ); + } + + _adapter = ApiAdapter( + isWithToken: true, + region: type.location.identifier, + ); + return success; + } + + @override + Future> tryInitApiByToken(final String token) async { + final api = _adapter.api(getInitialized: false); + final result = await api.isApiTokenValid(token); + if (!result.data || !result.success) { + return result; + } + + _adapter = ApiAdapter(region: api.region, isWithToken: true); + return result; + } + + String? getEmojiFlag(final String query) { + String? emoji; + + switch (query.toLowerCase()) { + case 'de': + emoji = '🇩🇪'; + break; + + case 'fi': + emoji = '🇫🇮'; + break; + + case 'us': + emoji = '🇺🇸'; + break; + } + + return emoji; + } + + @override + Future>> + getAvailableLocations() async { + final List locations = []; + final result = await _adapter.api().getAvailableLocations(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: locations, + code: result.code, + message: result.message, + ); + } + + final List rawLocations = result.data; + for (final rawLocation in rawLocations) { + ServerProviderLocation? location; + try { + location = ServerProviderLocation( + title: rawLocation['city'], + description: rawLocation['description'], + flag: getEmojiFlag(rawLocation['country']), + identifier: rawLocation['name'], + ); + } catch (e) { + continue; + } + locations.add(location); + } + + return GenericResult(success: true, data: locations); + } + + @override + Future>> getServerTypes({ + required final ServerProviderLocation location, + }) async { + final List types = []; + final result = await _adapter.api().getAvailableServerTypes(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: types, + code: result.code, + message: result.message, + ); + } + + final List rawTypes = result.data; + for (final rawType in rawTypes) { + for (final rawPrice in rawType['prices']) { + if (rawPrice['location'].toString() == location.identifier) { + types.add( + ServerType( + title: rawType['description'], + identifier: rawType['name'], + ram: rawType['memory'], + cores: rawType['cores'], + disk: DiskSize(byte: rawType['disk'] * 1024 * 1024 * 1024), + price: Price( + value: double.parse(rawPrice['price_monthly']['gross']), + currency: 'EUR', + ), + location: location, + ), + ); + } + } + } + + return GenericResult(success: true, data: types); + } + + Future> createReverseDns({ + required final ServerHostingDetails serverDetails, + required final ServerDomain domain, + }) async => + _adapter.api().createReverseDns( + serverId: serverDetails.id, + ip4: serverDetails.ip4, + dnsPtr: domain.domainName, + ); + + Future>> getServers() async { + final List servers = []; + final result = await _adapter.api().getServers(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: servers, + code: result.code, + message: result.message, + ); + } + + final List hetznerServers = result.data; + for (final hetznerServer in hetznerServers) { + if (hetznerServer.publicNet.ipv4 == null) { + continue; + } + + ServerBasicInfo? server; + try { + server = ServerBasicInfo( + id: hetznerServer.id, + name: hetznerServer.name, + ip: hetznerServer.publicNet.ipv4!.ip, + reverseDns: hetznerServer.publicNet.ipv4!.reverseDns, + created: hetznerServer.created, + ); + } catch (e) { + continue; + } + + servers.add(server); + } + + return GenericResult(success: true, data: servers); + } + + Future>> getMetadata( + final int serverId, + ) async { + List metadata = []; + final result = await _adapter.api().getServers(); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: false, + data: metadata, + code: result.code, + message: result.message, + ); + } + + final List servers = result.data; + try { + final HetznerServerInfo server = servers.firstWhere( + (final server) => server.id == serverId, + ); + + metadata = [ + ServerMetadataEntity( + type: MetadataType.id, + trId: 'server.server_id', + value: server.id.toString(), + ), + ServerMetadataEntity( + type: MetadataType.status, + trId: 'server.status', + value: server.status.toString().split('.')[1].capitalize(), + ), + ServerMetadataEntity( + type: MetadataType.cpu, + trId: 'server.cpu', + value: server.serverType.cores.toString(), + ), + ServerMetadataEntity( + type: MetadataType.ram, + trId: 'server.ram', + value: '${server.serverType.memory.toString()} GB', + ), + ServerMetadataEntity( + type: MetadataType.cost, + trId: 'server.monthly_cost', + value: server.serverType.prices[1].monthly.toStringAsFixed(2), + ), + ServerMetadataEntity( + type: MetadataType.location, + trId: 'server.location', + value: '${server.location.city}, ${server.location.country}', + ), + ServerMetadataEntity( + type: MetadataType.other, + trId: 'server.provider', + value: _adapter.api().displayProviderName, + ), + ]; + } catch (e) { + return GenericResult( + success: false, + data: [], + message: e.toString(), + ); + } + + return GenericResult(success: true, data: metadata); + } + + Future> getMetrics( + final int serverId, + final DateTime start, + final DateTime end, + ) async { + ServerMetrics? metrics; + + List serializeTimeSeries( + final Map json, + final String type, + ) { + final List list = json['time_series'][type]['values']; + return list + .map((final el) => TimeSeriesData(el[0], double.parse(el[1]))) + .toList(); + } + + final cpuResult = await _adapter.api().getMetrics( + serverId, + start, + end, + 'cpu', + ); + if (cpuResult.data.isEmpty || !cpuResult.success) { + return GenericResult( + success: false, + data: metrics, + code: cpuResult.code, + message: cpuResult.message, + ); + } + + final netResult = await _adapter.api().getMetrics( + serverId, + start, + end, + 'network', + ); + + if (cpuResult.data.isEmpty || !netResult.success) { + return GenericResult( + success: false, + data: metrics, + code: netResult.code, + message: netResult.message, + ); + } + + metrics = ServerMetrics( + cpu: serializeTimeSeries( + cpuResult.data, + 'cpu', + ), + bandwidthIn: serializeTimeSeries( + netResult.data, + 'network.0.bandwidth.in', + ), + bandwidthOut: serializeTimeSeries( + netResult.data, + 'network.0.bandwidth.out', + ), + end: end, + start: start, + stepsInSecond: cpuResult.data['step'], + ); + + return GenericResult(data: metrics, success: true); + } + + Future> restart(final int serverId) async { + DateTime? timestamp; + final result = await _adapter.api().restart(serverId); + if (!result.success) { + return GenericResult( + success: false, + data: timestamp, + code: result.code, + message: result.message, + ); + } + + timestamp = DateTime.now(); + + return GenericResult( + success: true, + data: timestamp, + ); + } + + Future> powerOn(final int serverId) async { + DateTime? timestamp; + final result = await _adapter.api().powerOn(serverId); + if (!result.success) { + return GenericResult( + success: false, + data: timestamp, + code: result.code, + message: result.message, + ); + } + + timestamp = DateTime.now(); + + return GenericResult( + success: true, + data: timestamp, + ); + } +} diff --git a/lib/ui/pages/server_details/text_details.dart b/lib/ui/pages/server_details/text_details.dart index 5f447901..d8911b8c 100644 --- a/lib/ui/pages/server_details/text_details.dart +++ b/lib/ui/pages/server_details/text_details.dart @@ -27,7 +27,7 @@ class _TextDetails extends StatelessWidget { .map( (final metadata) => ListTileOnSurfaceVariant( leadingIcon: metadata.type.icon, - title: metadata.name, + title: metadata.trId.tr(), subtitle: metadata.value, ), )