From 41dc77103f8a98b7076a9bee7469f387c6bf2d83 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Thu, 22 Dec 2022 22:45:06 +0400 Subject: [PATCH] feat: Implement error handling on server deletion Notify users when errors occured and handle application state accordingly --- assets/translations/en.json | 2 + assets/translations/ru.json | 2 + .../digital_ocean/digital_ocean.dart | 48 +++++++++++++++--- .../server_providers/hetzner/hetzner.dart | 49 ++++++++++++------- .../server_providers/server_provider.dart | 4 +- .../server_installation_cubit.dart | 6 ++- .../server_installation_repository.dart | 25 ++++++++-- 7 files changed, 107 insertions(+), 29 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index daa4544f..1b596cba 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -392,6 +392,8 @@ "generation_error": "Couldn't generate a recovery key. {}" }, "modals": { + "dns_removal_error": "Couldn't remove DNS records.", + "server_deletion_error": "Couldn't delete active server.", "server_validators_error": "Couldn't fetch available servers.", "already_exists": "Such server already exists.", "unexpected_error": "Unexpected error during placement from the provider side.", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index a5cc6634..8764580a 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -392,6 +392,8 @@ "generation_error": "Не удалось сгенерировать ключ. {}" }, "modals": { + "dns_removal_error": "Невозможно удалить DNS записи.", + "server_deletion_error": "Невозможно удалить сервер.", "server_validators_error": "Не удалось получить список серверов.", "already_exists": "Такой сервер уже существует.", "unexpected_error": "Непредвиденная ошибка со стороны провайдера.", 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 c7928653..2898b609 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 @@ -431,17 +431,41 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { } @override - Future deleteServer({ + Future> deleteServer({ required final String domainName, }) async { final Dio client = await getClient(); - final ServerBasicInfo serverToRemove = (await getServers()).firstWhere( - (final el) => el.name == domainName, - ); - final ServerVolume volumeToRemove = (await getVolumes()).firstWhere( - (final el) => el.serverId == serverToRemove.id, - ); + final servers = await getServers(); + final ServerBasicInfo serverToRemove; + try { + serverToRemove = servers.firstWhere( + (final el) => el.name == domainName, + ); + } catch (e) { + print(e); + return APIGenericResult( + data: false, + success: false, + message: e.toString(), + ); + } + + final volumes = await getVolumes(); + final ServerVolume volumeToRemove; + try { + volumeToRemove = volumes.firstWhere( + (final el) => el.serverId == serverToRemove.id, + ); + } catch (e) { + print(e); + return APIGenericResult( + data: false, + success: false, + message: e.toString(), + ); + } + final List laterFutures = []; await detachVolume(volumeToRemove); @@ -453,9 +477,19 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { await Future.wait(laterFutures); } catch (e) { print(e); + return APIGenericResult( + success: false, + data: false, + message: e.toString(), + ); } finally { close(client); } + + return APIGenericResult( + success: true, + data: true, + ); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index f0e032e8..d7a13b95 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 @@ -479,31 +479,46 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { } @override - Future deleteServer({ + Future> deleteServer({ required final String domainName, }) async { final Dio client = await getClient(); + try { + final String hostname = getHostnameFromDomain(domainName); - final String hostname = getHostnameFromDomain(domainName); + final Response serversReponse = await client.get('/servers'); + final List servers = serversReponse.data['servers']; + final Map server = + servers.firstWhere((final el) => el['name'] == hostname); + final List volumes = server['volumes']; + final List laterFutures = []; - final Response serversReponse = await client.get('/servers'); - final List servers = serversReponse.data['servers']; - final Map server = servers.firstWhere((final el) => el['name'] == hostname); - final List volumes = server['volumes']; - final List laterFutures = []; + for (final volumeId in volumes) { + await client.post('/volumes/$volumeId/actions/detach'); + } + await Future.delayed(const Duration(seconds: 10)); - for (final volumeId in volumes) { - await client.post('/volumes/$volumeId/actions/detach'); + for (final volumeId in volumes) { + laterFutures.add(client.delete('/volumes/$volumeId')); + } + laterFutures.add(client.delete('/servers/${server['id']}')); + + await Future.wait(laterFutures); + } catch (e) { + print(e); + return APIGenericResult( + success: false, + data: false, + message: e.toString(), + ); + } finally { + close(client); } - await Future.delayed(const Duration(seconds: 10)); - for (final volumeId in volumes) { - laterFutures.add(client.delete('/volumes/$volumeId')); - } - laterFutures.add(client.delete('/servers/${server['id']}')); - - await Future.wait(laterFutures); - close(client); + return APIGenericResult( + success: true, + data: true, + ); } @override diff --git a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart index 05fb5e61..c858d67b 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 @@ -31,7 +31,9 @@ abstract class ServerProviderApi extends ApiMap { Future restart(); Future powerOn(); - Future deleteServer({required final String domainName}); + Future> deleteServer({ + required final String domainName, + }); Future> createServer({ required final String dnsApiToken, required final User rootUser, diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 4da57479..5638b765 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -756,7 +756,11 @@ class ServerInstallationCubit extends Cubit { closeTimer(); if (state.serverDetails != null) { - await repository.deleteServer(state.serverDomain!); + final bool deletionResult = + await repository.deleteServer(state.serverDomain!); + if (!deletionResult) { + return; + } } await repository.deleteServerRelatedRecords(); emit( diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index f1aeadf5..5d45e7b9 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -759,13 +759,26 @@ class ServerInstallationRepository { await box.put(BNames.hasFinalChecked, value); } - Future deleteServer(final ServerDomain serverDomain) async { - await ApiController.currentServerProviderApiFactory! + Future deleteServer(final ServerDomain serverDomain) async { + final APIGenericResult deletionResult = await ApiController + .currentServerProviderApiFactory! .getServerProvider() .deleteServer( domainName: serverDomain.domainName, ); + if (!deletionResult.success) { + getIt() + .showSnackBar('modals.server_validators_error'.tr()); + return false; + } + + if (!deletionResult.data) { + getIt() + .showSnackBar('modals.server_deletion_error'.tr()); + return false; + } + await box.put(BNames.hasFinalChecked, false); await box.put(BNames.isServerStarted, false); await box.put(BNames.isServerResetedFirstTime, false); @@ -773,9 +786,15 @@ class ServerInstallationRepository { await box.put(BNames.isLoading, false); await box.put(BNames.serverDetails, null); - await ApiController.currentDnsProviderApiFactory! + final APIGenericResult removalResult = await ApiController + .currentDnsProviderApiFactory! .getDnsProvider() .removeSimilarRecords(domain: serverDomain); + + if (!removalResult.success) { + getIt().showSnackBar('modals.dns_removal_error'.tr()); + } + return true; } Future deleteServerRelatedRecords() async {