From dab2c569ec044c1216abf274ccfa57f5746e105b Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 22 Jul 2022 19:22:10 +0300 Subject: [PATCH 1/2] Fix installation Timer errors Co-authored-by: Inex Code --- assets/translations/en.json | 2 +- lib/logic/api_maps/rest_maps/server.dart | 17 ++++++--- .../server_providers/hetzner/hetzner.dart | 1 + .../cubit/forms/user/ssh_form_cubit.dart | 3 +- .../server_installation_cubit.dart | 36 +++++++++---------- .../server_installation_repository.dart | 22 ++++++------ .../server_installation_state.dart | 20 +++++------ lib/ui/pages/providers/providers.dart | 2 +- lib/ui/pages/setup/initializing.dart | 4 +-- lib/utils/ui_helpers.dart | 2 +- 10 files changed, 58 insertions(+), 51 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index c0e95639..4f32320e 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -284,7 +284,7 @@ "22": "Create master account", "23": "Enter a nickname and strong password", "finish": "Everything is initialized", - "checks": "Checks have been completed \n{} ouf of {}" + "checks": "Checks have been completed \n{} out of {}" }, "recovering": { "recovery_main_header": "Connect to an existing server", diff --git a/lib/logic/api_maps/rest_maps/server.dart b/lib/logic/api_maps/rest_maps/server.dart index 3cb2ed73..c0ef23b0 100644 --- a/lib/logic/api_maps/rest_maps/server.dart +++ b/lib/logic/api_maps/rest_maps/server.dart @@ -46,7 +46,10 @@ class ServerApi extends ApiMap { @override BaseOptions get options { - BaseOptions options = BaseOptions(); + BaseOptions options = BaseOptions( + connectTimeout: 10000, + receiveTimeout: 10000, + ); if (isWithToken) { final ServerDomain? cloudFlareDomain = @@ -56,6 +59,8 @@ class ServerApi extends ApiMap { options = BaseOptions( baseUrl: 'https://api.$domainName', + connectTimeout: 10000, + receiveTimeout: 10000, headers: { 'Authorization': 'Bearer $apiToken', }, @@ -65,6 +70,8 @@ class ServerApi extends ApiMap { if (overrideDomain != null) { options = BaseOptions( baseUrl: 'https://api.$overrideDomain', + connectTimeout: 10000, + receiveTimeout: 10000, headers: customToken != null ? {'Authorization': 'Bearer $customToken'} : null, @@ -619,7 +626,7 @@ class ServerApi extends ApiMap { } } - Future getDkim() async { + Future getDkim() async { Response response; final Dio client = await getClient(); @@ -627,13 +634,13 @@ class ServerApi extends ApiMap { response = await client.get('/services/mailserver/dkim'); } on DioError catch (e) { print(e.message); - return null; + throw Exception('No DKIM key found'); } finally { close(client); } if (response.statusCode == null) { - return null; + throw Exception('No DKIM key found'); } if (response.statusCode == HttpStatus.notFound || response.data == null) { @@ -641,7 +648,7 @@ class ServerApi extends ApiMap { } if (response.statusCode != HttpStatus.ok) { - return ''; + throw Exception('No DKIM key found'); } final Codec base64toString = utf8.fuse(base64); 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 fa75db17..0a2c3c73 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 @@ -341,6 +341,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { rethrow; } catch (e) { print(e); + deleteVolume(dataBase.id); } finally { client.close(); } diff --git a/lib/logic/cubit/forms/user/ssh_form_cubit.dart b/lib/logic/cubit/forms/user/ssh_form_cubit.dart index ba38a642..b6493ee7 100644 --- a/lib/logic/cubit/forms/user/ssh_form_cubit.dart +++ b/lib/logic/cubit/forms/user/ssh_form_cubit.dart @@ -41,7 +41,8 @@ class SshFormCubit extends FormCubit { @override FutureOr onSubmit() { print(key.state.isValid); - jobsCubit.addJob(CreateSSHKeyJob(user: user, publicKey: key.state.value)); + jobsCubit + .addJob(CreateSSHKeyJob(user: user, publicKey: key.state.value.trim())); } late FieldCubit key; diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 6899240f..39f5bf61 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -195,24 +195,10 @@ class ServerInstallationCubit extends Cubit { ); if (matches.values.every((final bool value) => value)) { - final ServerHostingDetails? server = await repository.startServer( + final ServerHostingDetails server = await repository.startServer( dataState.serverDetails!, ); - if (server == null) { - final ServerInstallationNotFinished newState = dataState.copyWith( - isLoading: false, - dnsMatches: matches, - ); - emit(newState); - runDelayed( - startServerIfDnsIsOkay, - const Duration(seconds: 30), - newState, - ); - return; - } - await repository.saveServerDetails(server); await repository.saveIsServerStarted(true); @@ -338,10 +324,22 @@ class ServerInstallationCubit extends Cubit { final bool isServerWorking = await repository.isHttpServerWorking(); if (isServerWorking) { - await repository.createDkimRecord(dataState.serverDomain!); - await repository.saveHasFinalChecked(true); - - emit(dataState.finish()); + bool dkimCreated = true; + try { + await repository.createDkimRecord(dataState.serverDomain!); + } catch (e) { + dkimCreated = false; + } + if (dkimCreated) { + await repository.saveHasFinalChecked(true); + emit(dataState.finish()); + } else { + runDelayed( + finishCheckIfServerIsOkay, + const Duration(seconds: 60), + dataState, + ); + } } else { runDelayed( finishCheckIfServerIsOkay, diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 1c94a2d0..6236052a 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -151,10 +151,10 @@ class ServerInstallationRepository { usersBox.clear(); } - Future startServer( + Future startServer( final ServerHostingDetails hetznerServer, ) async { - ServerHostingDetails? serverDetails; + ServerHostingDetails serverDetails; final ServerProviderApi api = serverProviderApiFactory!.getServerProvider(); serverDetails = await api.powerOn(); @@ -359,20 +359,20 @@ class ServerInstallationRepository { dnsProviderApiFactory!.getDnsProvider(); final ServerApi api = ServerApi(); - final String? dkimRecordString = await api.getDkim(); + String dkimRecordString = ''; + try { + dkimRecordString = await api.getDkim(); + } catch (e) { + print(e); + rethrow; + } - await dnsProviderApi.setDkim(dkimRecordString ?? '', cloudFlareDomain); + await dnsProviderApi.setDkim(dkimRecordString, cloudFlareDomain); } Future isHttpServerWorking() async { final ServerApi api = ServerApi(); - final bool isHttpServerWorking = await api.isHttpServerWorking(); - try { - await api.getDkim(); - } catch (e) { - return false; - } - return isHttpServerWorking; + return api.isHttpServerWorking(); } Future restart() async { diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index 5bb59275..bb04c07d 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -35,11 +35,11 @@ abstract class ServerInstallationState extends Equatable { final bool isServerResetedFirstTime; final bool isServerResetedSecondTime; - bool get isProviderFilled => providerApiToken != null; - bool get isCloudFlareFilled => cloudFlareKey != null; - bool get isBackblazeFilled => backblazeCredential != null; - bool get isDomainFilled => serverDomain != null; - bool get isUserFilled => rootUser != null; + bool get isServerProviderFilled => providerApiToken != null; + bool get isDnsProviderFilled => cloudFlareKey != null; + bool get isBackupsProviderFilled => backblazeCredential != null; + bool get isDomainSelected => serverDomain != null; + bool get isPrimaryUserFilled => rootUser != null; bool get isServerCreated => serverDetails != null; bool get isFullyInitilized => _fulfilementList.every((final el) => el!); @@ -58,11 +58,11 @@ abstract class ServerInstallationState extends Equatable { List get _fulfilementList { final List res = [ - isProviderFilled, - isCloudFlareFilled, - isBackblazeFilled, - isDomainFilled, - isUserFilled, + isServerProviderFilled, + isDnsProviderFilled, + isBackupsProviderFilled, + isDomainSelected, + isPrimaryUserFilled, isServerCreated, isServerStarted, isServerResetedFirstTime, diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index 97e4aeeb..640e620f 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -104,7 +104,7 @@ class _Card extends StatelessWidget { context.watch().state; final String domainName = - appConfig.isDomainFilled ? appConfig.serverDomain!.domainName : ''; + appConfig.isDomainSelected ? appConfig.serverDomain!.domainName : ''; switch (provider.type) { case ProviderType.server: diff --git a/lib/ui/pages/setup/initializing.dart b/lib/ui/pages/setup/initializing.dart index b4a504b8..2cde89ce 100644 --- a/lib/ui/pages/setup/initializing.dart +++ b/lib/ui/pages/setup/initializing.dart @@ -42,7 +42,7 @@ class InitializingPage extends StatelessWidget { () => _stepCheck(cubit), () => _stepCheck(cubit), () => _stepCheck(cubit), - () => Center(child: Text('initializing.finish'.tr())) + () => _stepCheck(cubit) ][cubit.state.progress.index](); return BlocListener( @@ -60,7 +60,7 @@ class InitializingPage extends StatelessWidget { children: [ Padding( padding: paddingH15V0.copyWith(top: 10, bottom: 10), - child: cubit.state.isFullyInitilized + child: cubit.state is ServerInstallationFinished ? const SizedBox( height: 80, ) diff --git a/lib/utils/ui_helpers.dart b/lib/utils/ui_helpers.dart index c34721e1..f4d58a05 100644 --- a/lib/utils/ui_helpers.dart +++ b/lib/utils/ui_helpers.dart @@ -4,5 +4,5 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_ class UiHelpers { static String getDomainName(final ServerInstallationState config) => - config.isDomainFilled ? config.serverDomain!.domainName : 'example.com'; + config.isDomainSelected ? config.serverDomain!.domainName : 'example.com'; } From d8ea5286214ede7a7fcf26a8961a3b1f2e91ca47 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 25 Jul 2022 03:03:19 +0300 Subject: [PATCH 2/2] Add magic sleep before deleteVolume on creation failure It seems Hetzner fails to accept our deleteVolume request so quickly after failing. We need to wait for about 10 seconds, I guess. The same magic sleep happens on deleteServer pack of requests. Please have patience... --- .../server_providers/hetzner/hetzner.dart | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) 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 0a2c3c73..4c9664bb 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 @@ -271,10 +271,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { dataBase: newVolume, ); - if (details == null) { - deleteVolume(newVolume.id); - } - return details; } @@ -320,6 +316,8 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { print('Decoded data: $data'); ServerHostingDetails? serverDetails; + DioError? hetznerError; + bool success = false; try { final Response serverCreateResponse = await client.post( @@ -335,17 +333,25 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { apiToken: apiToken, provider: ServerProvider.hetzner, ); + success = true; } on DioError catch (e) { print(e); - deleteVolume(dataBase.id); - rethrow; + hetznerError = e; } catch (e) { print(e); - deleteVolume(dataBase.id); } finally { client.close(); } + if (!success) { + await Future.delayed(const Duration(seconds: 10)); + await deleteVolume(dbId); + } + + if (hetznerError != null) { + throw hetznerError; + } + return serverDetails; }