From e0b32404bedb60f913cd16313d8297882d79ec94 Mon Sep 17 00:00:00 2001
From: NaiJi <naijiworld@protonmail.com>
Date: Fri, 2 Dec 2022 22:40:08 +0400
Subject: [PATCH] refactor: Implement better error handling on create server
 stage

Replace try-catch hell with APIGenericResult chain
---
 lib/logic/api_maps/api_generic_result.dart    |  2 +
 .../graphql_maps/server_api/jobs_api.dart     |  6 +-
 .../graphql_maps/server_api/server_api.dart   | 10 ---
 .../graphql_maps/server_api/services_api.dart | 36 ++++----
 .../graphql_maps/server_api/users_api.dart    | 30 +++----
 .../graphql_maps/server_api/volume_api.dart   |  8 +-
 .../digital_ocean/digital_ocean.dart          | 54 +++++++++---
 .../server_providers/hetzner/hetzner.dart     | 68 ++++++++++-----
 .../server_providers/server_provider.dart     |  2 +-
 .../server_providers/volume_provider.dart     | 10 ++-
 .../initializing/backblaze_form_cubit.dart    |  1 -
 .../provider_volume_cubit.dart                |  9 +-
 .../server_installation_repository.dart       | 82 +++++++++++--------
 lib/logic/cubit/users/users_cubit.dart        | 10 +--
 14 files changed, 197 insertions(+), 131 deletions(-)

diff --git a/lib/logic/api_maps/api_generic_result.dart b/lib/logic/api_maps/api_generic_result.dart
index b5bacfb6..81e1760a 100644
--- a/lib/logic/api_maps/api_generic_result.dart
+++ b/lib/logic/api_maps/api_generic_result.dart
@@ -3,6 +3,7 @@ class APIGenericResult<T> {
     required this.success,
     required this.data,
     this.message,
+    this.code,
   });
 
   /// Whether was a response successfully received,
@@ -10,4 +11,5 @@ class APIGenericResult<T> {
   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<GenericMutationResult<bool>> removeApiJob(final String uid) async {
+  Future<APIGenericResult<bool>> removeApiJob(final String uid) async {
     try {
       final GraphQLClient client = await getClient();
       final variables = Variables$Mutation$RemoveJob(jobId: uid);
       final mutation = Options$Mutation$RemoveJob(variables: variables);
       final response = await client.mutate$RemoveJob(mutation);
-      return GenericMutationResult(
+      return APIGenericResult(
         data: response.parsedData?.removeJob.success ?? false,
         success: true,
         code: response.parsedData?.removeJob.code ?? 0,
@@ -36,7 +36,7 @@ mixin JobsApi on ApiMap {
       );
     } catch (e) {
       print(e);
-      return GenericMutationResult(
+      return APIGenericResult(
         data: false,
         success: false,
         code: 0,
diff --git a/lib/logic/api_maps/graphql_maps/server_api/server_api.dart b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart
index 5216ffa8..bb703103 100644
--- a/lib/logic/api_maps/graphql_maps/server_api/server_api.dart
+++ b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart
@@ -31,16 +31,6 @@ part 'services_api.dart';
 part 'users_api.dart';
 part 'volume_api.dart';
 
-class GenericMutationResult<T> extends APIGenericResult<T> {
-  GenericMutationResult({
-    required super.success,
-    required this.code,
-    required super.data,
-    super.message,
-  });
-  final int code;
-}
-
 class ServerApi extends ApiMap
     with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi {
   ServerApi({
diff --git a/lib/logic/api_maps/graphql_maps/server_api/services_api.dart b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart
index a2e85914..adfe806f 100644
--- a/lib/logic/api_maps/graphql_maps/server_api/services_api.dart
+++ b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart
@@ -20,7 +20,7 @@ mixin ServicesApi on ApiMap {
     return services;
   }
 
-  Future<GenericMutationResult<bool>> enableService(
+  Future<APIGenericResult<bool>> 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<GenericMutationResult<void>> disableService(
+  Future<APIGenericResult<void>> 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<GenericMutationResult<bool>> stopService(
+  Future<APIGenericResult<bool>> 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<GenericMutationResult> startService(final String serviceId) async {
+  Future<APIGenericResult> 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<GenericMutationResult<bool>> restartService(
+  Future<APIGenericResult<bool>> 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<GenericMutationResult<ServerJob?>> moveService(
+  Future<APIGenericResult<ServerJob?>> 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<GenericMutationResult<User?>> createUser(
+  Future<APIGenericResult<User?>> 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<GenericMutationResult<bool>> deleteUser(
+  Future<APIGenericResult<bool>> 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<GenericMutationResult<User?>> updateUser(
+  Future<APIGenericResult<User?>> 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<GenericMutationResult<User?>> addSshKey(
+  Future<APIGenericResult<User?>> 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<GenericMutationResult<User?>> removeSshKey(
+  Future<APIGenericResult<User?>> 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<GenericMutationResult<String?>> migrateToBinds(
+  Future<APIGenericResult<String?>> migrateToBinds(
     final Map<String, String> serviceToDisk,
   ) async {
-    GenericMutationResult<String?>? mutation;
+    APIGenericResult<String?>? mutation;
 
     try {
       final GraphQLClient client = await getClient();
@@ -78,7 +78,7 @@ mixin VolumeApi on ApiMap {
           await client.mutate$MigrateToBinds(
         migrateMutation,
       );
-      mutation = mutation = GenericMutationResult(
+      mutation = mutation = APIGenericResult(
         success: true,
         code: result.parsedData!.migrateToBinds.code,
         message: result.parsedData!.migrateToBinds.message,
@@ -86,7 +86,7 @@ mixin VolumeApi on ApiMap {
       );
     } catch (e) {
       print(e);
-      mutation = GenericMutationResult(
+      mutation = APIGenericResult(
         success: false,
         code: 0,
         message: e.toString(),
diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart
index 86cda482..1ab5795c 100644
--- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart
+++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart
@@ -114,10 +114,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
       );
 
   @override
-  Future<ServerVolume?> createVolume() async {
+  Future<APIGenericResult<ServerVolume?>> createVolume() async {
     ServerVolume? volume;
 
-    final Response createVolumeResponse;
+    Response? createVolumeResponse;
     final Dio client = await getClient();
     try {
       final List<ServerVolume> volumes = await getVolumes();
@@ -146,11 +146,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
       );
     } catch (e) {
       print(e);
+      return APIGenericResult(
+        data: null,
+        success: false,
+        message: e.toString(),
+      );
     } finally {
       client.close();
     }
 
-    return volume;
+    return APIGenericResult(
+      data: volume,
+      success: true,
+      code: createVolumeResponse.statusCode,
+      message: createVolumeResponse.statusMessage,
+    );
   }
 
   @override
@@ -219,13 +229,13 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
   }
 
   @override
-  Future<bool> attachVolume(
+  Future<APIGenericResult<bool>> attachVolume(
     final ServerVolume volume,
     final int serverId,
   ) async {
     bool success = false;
 
-    final Response attachVolumeResponse;
+    Response? attachVolumeResponse;
     final Dio client = await getClient();
     try {
       attachVolumeResponse = await client.post(
@@ -241,11 +251,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
           attachVolumeResponse.data['action']['status'].toString() != 'error';
     } catch (e) {
       print(e);
+      return APIGenericResult(
+        data: false,
+        success: false,
+        message: e.toString(),
+      );
     } finally {
       close(client);
     }
 
-    return success;
+    return APIGenericResult(
+      data: success,
+      success: true,
+      code: attachVolumeResponse.statusCode,
+      message: attachVolumeResponse.statusMessage,
+    );
   }
 
   @override
@@ -323,7 +343,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
   }
 
   @override
-  Future<ServerHostingDetails?> createServer({
+  Future<APIGenericResult<ServerHostingDetails?>> createServer({
     required final String dnsApiToken,
     required final User rootUser,
     required final String domainName,
@@ -345,6 +365,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
         "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$formattedHostname bash 2>&1 | tee /tmp/infect.log";
     print(userdataString);
 
+    Response? serverCreateResponse;
     final Dio client = await getClient();
     try {
       final Map<String, Object> data = {
@@ -356,14 +377,15 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
       };
       print('Decoded data: $data');
 
-      final Response serverCreateResponse = await client.post(
+      serverCreateResponse = await client.post(
         '/droplets',
         data: data,
       );
 
       final int serverId = serverCreateResponse.data['droplet']['id'];
-      final ServerVolume? newVolume = await createVolume();
-      final bool attachedVolume = await attachVolume(newVolume!, serverId);
+      final ServerVolume? newVolume = (await createVolume()).data;
+      final bool attachedVolume =
+          (await attachVolume(newVolume!, serverId)).data;
 
       String? ipv4;
       int attempts = 0;
@@ -391,11 +413,21 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
       }
     } catch (e) {
       print(e);
+      return APIGenericResult(
+        success: false,
+        data: null,
+        message: e.toString(),
+      );
     } finally {
       close(client);
     }
 
-    return serverDetails;
+    return APIGenericResult(
+      data: serverDetails,
+      success: true,
+      code: serverCreateResponse.statusCode,
+      message: serverCreateResponse.statusMessage,
+    );
   }
 
   @override
diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart
index 4b51b27c..6e2049e9 100644
--- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart
+++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart
@@ -140,10 +140,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
   }
 
   @override
-  Future<ServerVolume?> createVolume() async {
+  Future<APIGenericResult<ServerVolume?>> createVolume() async {
     ServerVolume? volume;
 
-    final Response createVolumeResponse;
+    Response? createVolumeResponse;
     final Dio client = await getClient();
     try {
       createVolumeResponse = await client.post(
@@ -171,11 +171,21 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
       );
     } catch (e) {
       print(e);
+      return APIGenericResult(
+        data: null,
+        success: false,
+        message: e.toString(),
+      );
     } finally {
       client.close();
     }
 
-    return volume;
+    return APIGenericResult(
+      data: volume,
+      success: true,
+      code: createVolumeResponse.statusCode,
+      message: createVolumeResponse.statusMessage,
+    );
   }
 
   @override
@@ -259,13 +269,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
   }
 
   @override
-  Future<bool> attachVolume(
+  Future<APIGenericResult<bool>> attachVolume(
     final ServerVolume volume,
     final int serverId,
   ) async {
     bool success = false;
 
-    final Response attachVolumeResponse;
+    Response? attachVolumeResponse;
     final Dio client = await getClient();
     try {
       attachVolumeResponse = await client.post(
@@ -283,7 +293,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
       client.close();
     }
 
-    return success;
+    return APIGenericResult(
+      data: success,
+      success: true,
+      code: attachVolumeResponse?.statusCode,
+      message: attachVolumeResponse?.statusMessage,
+    );
   }
 
   @override
@@ -335,31 +350,33 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
   }
 
   @override
-  Future<ServerHostingDetails?> createServer({
+  Future<APIGenericResult<ServerHostingDetails?>> createServer({
     required final String dnsApiToken,
     required final User rootUser,
     required final String domainName,
     required final String serverType,
   }) async {
-    ServerHostingDetails? details;
+    final APIGenericResult<ServerVolume?> 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<ServerHostingDetails?> createServerWithVolume({
+  Future<APIGenericResult<ServerHostingDetails?>> createServerWithVolume({
     required final String dnsApiToken,
     required final User rootUser,
     required final String domainName,
@@ -381,6 +398,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
     final String userdataString =
         "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
 
+    Response? serverCreateResponse;
     ServerHostingDetails? serverDetails;
     DioError? hetznerError;
     bool success = false;
@@ -400,7 +418,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
       };
       print('Decoded data: $data');
 
-      final Response serverCreateResponse = await client.post(
+      serverCreateResponse = await client.post(
         '/servers',
         data: data,
       );
@@ -428,11 +446,19 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
       await deleteVolume(volume);
     }
 
-    if (hetznerError != null) {
-      throw hetznerError;
+    String? apiResultMessage = serverCreateResponse?.statusMessage;
+    if (hetznerError != null &&
+        hetznerError.response!.data['error']['code'] == 'uniqueness_error') {
+      apiResultMessage = 'uniqueness_error';
     }
 
-    return serverDetails;
+    return APIGenericResult(
+      data: serverDetails,
+      success: success && hetznerError == null,
+      code: serverCreateResponse?.statusCode ??
+          hetznerError?.response?.statusCode,
+      message: apiResultMessage,
+    );
   }
 
   static String getHostnameFromDomain(final String domain) {
diff --git a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart
index 21f6f376..05fb5e61 100644
--- a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart
+++ b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart
@@ -32,7 +32,7 @@ abstract class ServerProviderApi extends ApiMap {
   Future<ServerHostingDetails> powerOn();
 
   Future<void> deleteServer({required final String domainName});
-  Future<ServerHostingDetails?> createServer({
+  Future<APIGenericResult<ServerHostingDetails?>> createServer({
     required final String dnsApiToken,
     required final User rootUser,
     required final String domainName,
diff --git a/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart
index d3ae6f2a..5e01d268 100644
--- a/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart
+++ b/lib/logic/api_maps/rest_maps/server_providers/volume_provider.dart
@@ -1,12 +1,18 @@
+import 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
 import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
 import 'package:selfprivacy/logic/models/disk_size.dart';
 import 'package:selfprivacy/logic/models/hive/server_details.dart';
 import 'package:selfprivacy/logic/models/price.dart';
 
+export 'package:selfprivacy/logic/api_maps/api_generic_result.dart';
+
 mixin VolumeProviderApi on ApiMap {
-  Future<ServerVolume?> createVolume();
+  Future<APIGenericResult<ServerVolume?>> createVolume();
   Future<List<ServerVolume>> getVolumes({final String? status});
-  Future<bool> attachVolume(final ServerVolume volume, final int serverId);
+  Future<APIGenericResult<bool>> attachVolume(
+    final ServerVolume volume,
+    final int serverId,
+  );
   Future<bool> detachVolume(final ServerVolume volume);
   Future<bool> resizeVolume(final ServerVolume volume, final DiskSize size);
   Future<void> deleteVolume(final ServerVolume volume);
diff --git a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart
index 7390eee1..21d17a84 100644
--- a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart
+++ b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart
@@ -3,7 +3,6 @@ import 'package:cubit_form/cubit_form.dart';
 import 'package:selfprivacy/config/get_it_config.dart';
 import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
 import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
-import 'package:selfprivacy/logic/get_it/navigation.dart';
 import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
 import 'package:easy_localization/easy_localization.dart';
 
diff --git a/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart b/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart
index 11e180d0..ff8eb797 100644
--- a/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart
+++ b/lib/logic/cubit/provider_volumes/provider_volume_cubit.dart
@@ -113,10 +113,11 @@ class ApiProviderVolumeCubit
   }
 
   Future<void> createVolume() async {
-    final ServerVolume? volume = await ApiController
-        .currentVolumeProviderApiFactory!
-        .getVolumeProvider()
-        .createVolume();
+    final ServerVolume? volume = (await ApiController
+            .currentVolumeProviderApiFactory!
+            .getVolumeProvider()
+            .createVolume())
+        .data;
 
     final diskVolume = DiskVolume(providerVolume: volume);
     await attachVolume(diskVolume);
diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart
index 80c3ee0d..b495ec0d 100644
--- a/lib/logic/cubit/server_installation/server_installation_repository.dart
+++ b/lib/logic/cubit/server_installation/server_installation_repository.dart
@@ -246,22 +246,52 @@ class ServerInstallationRepository {
   }) async {
     final ServerProviderApi api =
         ApiController.currentServerProviderApiFactory!.getServerProvider();
+
+    void showInstallationErrorPopUp() {
+      showPopUpAlert(
+        alertTitle: 'modals.unexpected_error'.tr(),
+        description: 'modals.try_again'.tr(),
+        actionButtonTitle: 'modals.yes'.tr(),
+        actionButtonOnPressed: () async {
+          ServerHostingDetails? serverDetails;
+          try {
+            final APIGenericResult createResult = await api.createServer(
+              dnsApiToken: cloudFlareKey,
+              rootUser: rootUser,
+              domainName: domainName,
+              serverType: getIt<ApiConfigModel>().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<ServerHostingDetails?> createServerResult =
+          await api.createServer(
         dnsApiToken: cloudFlareKey,
         rootUser: rootUser,
         domainName: domainName,
         serverType: getIt<ApiConfigModel>().serverType!,
       );
 
-      if (serverDetails == null) {
-        print('Server is not initialized!');
-        return;
+      if (createServerResult.data == null) {
+        const String e = 'Server is not initialized!';
+        print(e);
       }
-      saveServerDetails(serverDetails);
-      onSuccess(serverDetails);
-    } on DioError catch (e) {
-      if (e.response!.data['error']['code'] == 'uniqueness_error') {
+
+      if (createServerResult.message == 'uniqueness_error') {
         showPopUpAlert(
           alertTitle: 'modals.already_exists'.tr(),
           description: 'modals.destroy_server'.tr(),
@@ -273,39 +303,13 @@ class ServerInstallationRepository {
 
             ServerHostingDetails? serverDetails;
             try {
-              serverDetails = await api.createServer(
-                dnsApiToken: cloudFlareKey,
-                rootUser: rootUser,
-                domainName: domainName,
-                serverType: getIt<ApiConfigModel>().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<ApiConfigModel>().serverType!,
               );
+              serverDetails = createResult.data;
             } catch (e) {
               print(e);
             }
@@ -320,6 +324,12 @@ class ServerInstallationRepository {
           cancelButtonOnPressed: onCancel,
         );
       }
+
+      saveServerDetails(createServerResult.data!);
+      onSuccess(createServerResult.data!);
+    } catch (e) {
+      print(e);
+      showInstallationErrorPopUp();
     }
   }
 
diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart
index 070fce2c..001ce8d0 100644
--- a/lib/logic/cubit/users/users_cubit.dart
+++ b/lib/logic/cubit/users/users_cubit.dart
@@ -78,7 +78,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
       return;
     }
     // If API returned error, do nothing
-    final GenericMutationResult<User?> result =
+    final APIGenericResult<User?> result =
         await api.createUser(user.login, password);
     if (result.data == null) {
       getIt<NavigationService>()
@@ -101,7 +101,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
       return;
     }
     final List<User> loadedUsers = List<User>.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<UsersState> {
           .showSnackBar('users.could_not_change_password'.tr());
       return;
     }
-    final GenericMutationResult<User?> result =
+    final APIGenericResult<User?> result =
         await api.updateUser(user.login, newPassword);
     if (result.data == null) {
       getIt<NavigationService>().showSnackBar(
@@ -138,7 +138,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
   }
 
   Future<void> addSshKey(final User user, final String publicKey) async {
-    final GenericMutationResult<User?> result =
+    final APIGenericResult<User?> result =
         await api.addSshKey(user.login, publicKey);
     if (result.data != null) {
       final User updatedUser = result.data!;
@@ -157,7 +157,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
   }
 
   Future<void> deleteSshKey(final User user, final String publicKey) async {
-    final GenericMutationResult<User?> result =
+    final APIGenericResult<User?> result =
         await api.removeSshKey(user.login, publicKey);
     if (result.data != null) {
       final User updatedUser = result.data!;