chore: Implement server installation for Digital Ocean

pull/213/head
NaiJi ✨ 2023-04-19 10:41:30 -03:00
parent 15c0586ab3
commit f6591cbfc6
2 changed files with 65 additions and 97 deletions

View File

@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:dio/dio.dart'; 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/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/api_maps/staging_options.dart'; import 'package:selfprivacy/logic/api_maps/staging_options.dart';
import 'package:selfprivacy/logic/models/disk_size.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/hive/user.dart';
import 'package:selfprivacy/logic/models/price.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/logic/models/server_provider_location.dart';
import 'package:selfprivacy/utils/network_utils.dart';
import 'package:selfprivacy/utils/password_generator.dart'; import 'package:selfprivacy/utils/password_generator.dart';
class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
@ -315,7 +310,6 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
required final String databasePassword, required final String databasePassword,
required final String domainName, required final String domainName,
required final String hostName, required final String hostName,
required final int volumeId,
required final String serverType, required final String serverType,
}) async { }) async {
final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false'; final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false';
@ -360,95 +354,6 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
); );
} }
Future<GenericResult<ServerHostingDetails?>> 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<String, Object> 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<ServerBasicInfo> 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<GenericResult<void>> deleteServer(final int serverId) async { Future<GenericResult<void>> deleteServer(final int serverId) async {
final Dio client = await getClient(); final Dio client = await getClient();
try { try {

View File

@ -133,6 +133,9 @@ class DigitalOceanServerProvider extends ServerProvider {
Future<GenericResult<CallbackDialogueBranching?>> launchInstallation( Future<GenericResult<CallbackDialogueBranching?>> launchInstallation(
final LaunchInstallationData installationData, final LaunchInstallationData installationData,
) async { ) async {
ServerHostingDetails? serverDetails;
final serverApiToken = StringGenerators.apiToken();
final hostname = getHostnameFromDomain(installationData.domainName);
final serverResult = await _adapter.api().createServer( final serverResult = await _adapter.api().createServer(
dnsApiToken: installationData.dnsApiToken, dnsApiToken: installationData.dnsApiToken,
rootUser: installationData.rootUser, rootUser: installationData.rootUser,
@ -140,12 +143,12 @@ class DigitalOceanServerProvider extends ServerProvider {
serverType: installationData.serverTypeId, serverType: installationData.serverTypeId,
dnsProviderType: dnsProviderType:
dnsProviderToInfectName(installationData.dnsProviderType), dnsProviderToInfectName(installationData.dnsProviderType),
hostName: getHostnameFromDomain(installationData.domainName), hostName: hostname,
base64Password: base64.encode( base64Password: base64.encode(
utf8.encode(installationData.rootUser.password ?? 'PASS'), utf8.encode(installationData.rootUser.password ?? 'PASS'),
), ),
databasePassword: StringGenerators.dbPassword(), databasePassword: StringGenerators.dbPassword(),
serverApiToken: StringGenerators.apiToken(), serverApiToken: serverApiToken,
); );
if (!serverResult.success || serverResult.data == null) { if (!serverResult.success || serverResult.data == null) {
@ -169,6 +172,66 @@ class DigitalOceanServerProvider extends ServerProvider {
code: serverResult.code, 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 @override