feat: Implement error handling on server deletion

Notify users when errors occured and handle application state accordingly
pull/155/head
NaiJi ✨ 2022-12-22 22:45:06 +04:00
parent a0e531f18c
commit 41dc77103f
7 changed files with 107 additions and 29 deletions

View File

@ -392,6 +392,8 @@
"generation_error": "Couldn't generate a recovery key. {}" "generation_error": "Couldn't generate a recovery key. {}"
}, },
"modals": { "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.", "server_validators_error": "Couldn't fetch available servers.",
"already_exists": "Such server already exists.", "already_exists": "Such server already exists.",
"unexpected_error": "Unexpected error during placement from the provider side.", "unexpected_error": "Unexpected error during placement from the provider side.",

View File

@ -392,6 +392,8 @@
"generation_error": "Не удалось сгенерировать ключ. {}" "generation_error": "Не удалось сгенерировать ключ. {}"
}, },
"modals": { "modals": {
"dns_removal_error": "Невозможно удалить DNS записи.",
"server_deletion_error": "Невозможно удалить сервер.",
"server_validators_error": "Не удалось получить список серверов.", "server_validators_error": "Не удалось получить список серверов.",
"already_exists": "Такой сервер уже существует.", "already_exists": "Такой сервер уже существует.",
"unexpected_error": "Непредвиденная ошибка со стороны провайдера.", "unexpected_error": "Непредвиденная ошибка со стороны провайдера.",

View File

@ -431,17 +431,41 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<void> deleteServer({ Future<APIGenericResult<bool>> deleteServer({
required final String domainName, required final String domainName,
}) async { }) async {
final Dio client = await getClient(); final Dio client = await getClient();
final ServerBasicInfo serverToRemove = (await getServers()).firstWhere( final servers = await getServers();
(final el) => el.name == domainName, final ServerBasicInfo serverToRemove;
); try {
final ServerVolume volumeToRemove = (await getVolumes()).firstWhere( serverToRemove = servers.firstWhere(
(final el) => el.serverId == serverToRemove.id, (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<Future> laterFutures = <Future>[]; final List<Future> laterFutures = <Future>[];
await detachVolume(volumeToRemove); await detachVolume(volumeToRemove);
@ -453,9 +477,19 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
await Future.wait(laterFutures); await Future.wait(laterFutures);
} catch (e) { } catch (e) {
print(e); print(e);
return APIGenericResult(
success: false,
data: false,
message: e.toString(),
);
} finally { } finally {
close(client); close(client);
} }
return APIGenericResult(
success: true,
data: true,
);
} }
@override @override

View File

@ -479,31 +479,46 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
} }
@override @override
Future<void> deleteServer({ Future<APIGenericResult<bool>> deleteServer({
required final String domainName, required final String domainName,
}) async { }) async {
final Dio client = await getClient(); 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<Future> laterFutures = <Future>[];
final Response serversReponse = await client.get('/servers'); for (final volumeId in volumes) {
final List servers = serversReponse.data['servers']; await client.post('/volumes/$volumeId/actions/detach');
final Map server = servers.firstWhere((final el) => el['name'] == hostname); }
final List volumes = server['volumes']; await Future.delayed(const Duration(seconds: 10));
final List<Future> laterFutures = <Future>[];
for (final volumeId in volumes) { for (final volumeId in volumes) {
await client.post('/volumes/$volumeId/actions/detach'); 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) { return APIGenericResult(
laterFutures.add(client.delete('/volumes/$volumeId')); success: true,
} data: true,
laterFutures.add(client.delete('/servers/${server['id']}')); );
await Future.wait(laterFutures);
close(client);
} }
@override @override

View File

@ -31,7 +31,9 @@ abstract class ServerProviderApi extends ApiMap {
Future<ServerHostingDetails> restart(); Future<ServerHostingDetails> restart();
Future<ServerHostingDetails> powerOn(); Future<ServerHostingDetails> powerOn();
Future<void> deleteServer({required final String domainName}); Future<APIGenericResult<bool>> deleteServer({
required final String domainName,
});
Future<APIGenericResult<ServerHostingDetails?>> createServer({ Future<APIGenericResult<ServerHostingDetails?>> createServer({
required final String dnsApiToken, required final String dnsApiToken,
required final User rootUser, required final User rootUser,

View File

@ -756,7 +756,11 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
closeTimer(); closeTimer();
if (state.serverDetails != null) { if (state.serverDetails != null) {
await repository.deleteServer(state.serverDomain!); final bool deletionResult =
await repository.deleteServer(state.serverDomain!);
if (!deletionResult) {
return;
}
} }
await repository.deleteServerRelatedRecords(); await repository.deleteServerRelatedRecords();
emit( emit(

View File

@ -759,13 +759,26 @@ class ServerInstallationRepository {
await box.put(BNames.hasFinalChecked, value); await box.put(BNames.hasFinalChecked, value);
} }
Future<void> deleteServer(final ServerDomain serverDomain) async { Future<bool> deleteServer(final ServerDomain serverDomain) async {
await ApiController.currentServerProviderApiFactory! final APIGenericResult<bool> deletionResult = await ApiController
.currentServerProviderApiFactory!
.getServerProvider() .getServerProvider()
.deleteServer( .deleteServer(
domainName: serverDomain.domainName, domainName: serverDomain.domainName,
); );
if (!deletionResult.success) {
getIt<NavigationService>()
.showSnackBar('modals.server_validators_error'.tr());
return false;
}
if (!deletionResult.data) {
getIt<NavigationService>()
.showSnackBar('modals.server_deletion_error'.tr());
return false;
}
await box.put(BNames.hasFinalChecked, false); await box.put(BNames.hasFinalChecked, false);
await box.put(BNames.isServerStarted, false); await box.put(BNames.isServerStarted, false);
await box.put(BNames.isServerResetedFirstTime, false); await box.put(BNames.isServerResetedFirstTime, false);
@ -773,9 +786,15 @@ class ServerInstallationRepository {
await box.put(BNames.isLoading, false); await box.put(BNames.isLoading, false);
await box.put(BNames.serverDetails, null); await box.put(BNames.serverDetails, null);
await ApiController.currentDnsProviderApiFactory! final APIGenericResult<void> removalResult = await ApiController
.currentDnsProviderApiFactory!
.getDnsProvider() .getDnsProvider()
.removeSimilarRecords(domain: serverDomain); .removeSimilarRecords(domain: serverDomain);
if (!removalResult.success) {
getIt<NavigationService>().showSnackBar('modals.dns_removal_error'.tr());
}
return true;
} }
Future<void> deleteServerRelatedRecords() async { Future<void> deleteServerRelatedRecords() async {