From f6591cbfc65f7d9dec193f54987afe447f4096d2 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Wed, 19 Apr 2023 10:41:30 -0300 Subject: [PATCH] chore: Implement server installation for Digital Ocean --- .../digital_ocean/digital_ocean_api.dart | 95 ------------------- .../server_providers/digital_ocean.dart | 67 ++++++++++++- 2 files changed, 65 insertions(+), 97 deletions(-) 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 264f227d..f6ba941a 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 @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; @@ -7,13 +6,9 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_pro import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart'; import 'package:selfprivacy/logic/api_maps/staging_options.dart'; import 'package:selfprivacy/logic/models/disk_size.dart'; -import 'package:selfprivacy/logic/models/hive/server_domain.dart'; -import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/price.dart'; -import 'package:selfprivacy/logic/models/server_basic_info.dart'; import 'package:selfprivacy/logic/models/server_provider_location.dart'; -import 'package:selfprivacy/utils/network_utils.dart'; import 'package:selfprivacy/utils/password_generator.dart'; class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { @@ -315,7 +310,6 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { required final String databasePassword, required final String domainName, required final String hostName, - required final int volumeId, required final String serverType, }) async { final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false'; @@ -360,95 +354,6 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { ); } - Future> creatfgdfeServer({ - required final String dnsApiToken, - required final User rootUser, - required final String domainName, - required final String serverType, - required final DnsProviderType dnsProvider, - }) async { - ServerHostingDetails? serverDetails; - - final String dbPassword = StringGenerators.dbPassword(); - final String apiToken = StringGenerators.apiToken(); - - final String base64Password = - base64.encode(utf8.encode(rootUser.password ?? 'PASS')); - - final String formattedHostname = getHostnameFromDomain(domainName); - const String infectBranch = 'testing/digital-ocean'; - final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false'; - final String dnsProviderType = dnsProviderToInfectName(dnsProvider); - - final String userdataString = - "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | PROVIDER=$infectProviderName DNS_PROVIDER_TYPE=$dnsProviderType 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 data = { - 'name': formattedHostname, - 'size': serverType, - 'image': 'ubuntu-20-04-x64', - 'user_data': userdataString, - 'region': region!, - }; - print('Decoded data: $data'); - - serverCreateResponse = await client.post( - '/droplets', - data: data, - ); - - final int serverId = serverCreateResponse.data['droplet']['id']; - final ServerVolume? newVolume = (await createVolume()).data; - final bool attachedVolume = - (await attachVolume(newVolume!, serverId)).data; - - String? ipv4; - int attempts = 0; - while (attempts < 5 && ipv4 == null) { - await Future.delayed(const Duration(seconds: 20)); - final List servers = await getServers(); - for (final server in servers) { - if (server.name == formattedHostname && server.ip != '0.0.0.0') { - ipv4 = server.ip; - break; - } - } - ++attempts; - } - - if (attachedVolume && ipv4 != null) { - serverDetails = ServerHostingDetails( - id: serverId, - ip4: ipv4, - createTime: DateTime.now(), - volume: newVolume, - apiToken: apiToken, - provider: ServerProviderType.digitalOcean, - ); - } - } catch (e) { - print(e); - return GenericResult( - success: false, - data: null, - message: e.toString(), - ); - } finally { - close(client); - } - - return GenericResult( - data: serverDetails, - success: true, - code: serverCreateResponse.statusCode, - message: serverCreateResponse.statusMessage, - ); - } - Future> deleteServer(final int serverId) async { final Dio client = await getClient(); try { diff --git a/lib/logic/providers/server_providers/digital_ocean.dart b/lib/logic/providers/server_providers/digital_ocean.dart index 4393f80e..c883ac8c 100644 --- a/lib/logic/providers/server_providers/digital_ocean.dart +++ b/lib/logic/providers/server_providers/digital_ocean.dart @@ -133,6 +133,9 @@ class DigitalOceanServerProvider extends ServerProvider { Future> launchInstallation( final LaunchInstallationData installationData, ) async { + ServerHostingDetails? serverDetails; + final serverApiToken = StringGenerators.apiToken(); + final hostname = getHostnameFromDomain(installationData.domainName); final serverResult = await _adapter.api().createServer( dnsApiToken: installationData.dnsApiToken, rootUser: installationData.rootUser, @@ -140,12 +143,12 @@ class DigitalOceanServerProvider extends ServerProvider { serverType: installationData.serverTypeId, dnsProviderType: dnsProviderToInfectName(installationData.dnsProviderType), - hostName: getHostnameFromDomain(installationData.domainName), + hostName: hostname, base64Password: base64.encode( utf8.encode(installationData.rootUser.password ?? 'PASS'), ), databasePassword: StringGenerators.dbPassword(), - serverApiToken: StringGenerators.apiToken(), + serverApiToken: serverApiToken, ); if (!serverResult.success || serverResult.data == null) { @@ -169,6 +172,66 @@ class DigitalOceanServerProvider extends ServerProvider { code: serverResult.code, ); } + + try { + final int dropletId = serverResult.data['droplet']['id']; + final ServerVolume? newVolume = (await createVolume()).data; + final bool attachedVolume = + (await attachVolume(newVolume!, dropletId)).data; + + String? ipv4; + int attempts = 0; + while (attempts < 5 && ipv4 == null) { + await Future.delayed(const Duration(seconds: 20)); + final servers = await getServers(); + for (final server in servers.data) { + if (server.name == hostname && server.ip != '0.0.0.0') { + ipv4 = server.ip; + break; + } + } + ++attempts; + } + + if (attachedVolume && ipv4 != null) { + serverDetails = ServerHostingDetails( + id: dropletId, + ip4: ipv4, + createTime: DateTime.now(), + volume: newVolume, + apiToken: serverApiToken, + provider: ServerProviderType.digitalOcean, + ); + } + } catch (e) { + return GenericResult( + success: false, + data: CallbackDialogueBranching( + choices: [ + CallbackDialogueChoice( + title: 'basis.cancel'.tr(), + callback: null, + ), + CallbackDialogueChoice( + title: 'basis.try_again'.tr(), + callback: () async { + await Future.delayed(const Duration(seconds: 5)); + final deletion = await deleteServer(hostname); + return deletion.success + ? await launchInstallation(installationData) + : deletion; + }, + ), + ], + description: 'modals.try_again'.tr(), + title: 'modals.server_deletion_error'.tr(), + ), + message: e.toString(), + ); + } + + await installationData.successCallback(serverDetails!); + return GenericResult(success: true, data: null); } @override