mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-08 17:11:14 +00:00
chore: Merge branch master into backups-rewrite
This commit is contained in:
commit
02cb4dbf8b
|
@ -47,4 +47,11 @@ enum DnsProviderType {
|
||||||
return unknown;
|
return unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String toInfectName() => switch (this) {
|
||||||
|
digitalOcean => 'DIGITALOCEAN',
|
||||||
|
cloudflare => 'CLOUDFLARE',
|
||||||
|
desec => 'DESEC',
|
||||||
|
unknown => 'UNKNOWN',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/digital_oc
|
||||||
import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart';
|
import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
|
||||||
import 'package:selfprivacy/logic/models/json/digital_ocean_server_info.dart';
|
import 'package:selfprivacy/logic/models/json/digital_ocean_server_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/logic/models/price.dart';
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
|
@ -52,86 +51,41 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
ServerProviderType get type => ServerProviderType.digitalOcean;
|
ServerProviderType get type => ServerProviderType.digitalOcean;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<bool>> trySetServerLocation(
|
Future<GenericResult<List<ServerBasicInfo>>> getServers() async {
|
||||||
final String location,
|
List<ServerBasicInfo> servers = [];
|
||||||
) async {
|
final result = await _adapter.api().getServers();
|
||||||
final bool apiInitialized = _adapter.api().isWithToken;
|
if (result.data.isEmpty || !result.success) {
|
||||||
if (!apiInitialized) {
|
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
success: true,
|
success: result.success,
|
||||||
data: false,
|
data: servers,
|
||||||
message: 'Not authorized!',
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_adapter = ApiAdapter(
|
final List rawServers = result.data;
|
||||||
isWithToken: true,
|
servers = rawServers.map<ServerBasicInfo>(
|
||||||
region: location,
|
(final server) {
|
||||||
);
|
String ipv4 = '0.0.0.0';
|
||||||
return success;
|
if (server['networks']['v4'].isNotEmpty) {
|
||||||
}
|
for (final v4 in server['networks']['v4']) {
|
||||||
|
if (v4['type'].toString() == 'public') {
|
||||||
|
ipv4 = v4['ip_address'].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
return ServerBasicInfo(
|
||||||
Future<GenericResult<bool>> tryInitApiByToken(final String token) async {
|
id: server['id'],
|
||||||
final api = _adapter.api(getInitialized: false);
|
reverseDns: server['name'],
|
||||||
final result = await api.isApiTokenValid(token);
|
created: DateTime.now(),
|
||||||
if (!result.data || !result.success) {
|
ip: ipv4,
|
||||||
return result;
|
name: server['name'],
|
||||||
}
|
);
|
||||||
|
},
|
||||||
|
).toList();
|
||||||
|
|
||||||
_adapter = ApiAdapter(region: api.region, isWithToken: true);
|
return GenericResult(success: true, data: servers);
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
String? getEmojiFlag(final String query) {
|
|
||||||
String? emoji;
|
|
||||||
|
|
||||||
switch (query.toLowerCase().substring(0, 3)) {
|
|
||||||
case 'fra':
|
|
||||||
emoji = '🇩🇪';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ams':
|
|
||||||
emoji = '🇳🇱';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'sgp':
|
|
||||||
emoji = '🇸🇬';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'lon':
|
|
||||||
emoji = '🇬🇧';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'tor':
|
|
||||||
emoji = '🇨🇦';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'blr':
|
|
||||||
emoji = '🇮🇳';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'nyc':
|
|
||||||
case 'sfo':
|
|
||||||
emoji = '🇺🇸';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return emoji;
|
|
||||||
}
|
|
||||||
|
|
||||||
String dnsProviderToInfectName(final DnsProviderType dnsProvider) {
|
|
||||||
String dnsProviderType;
|
|
||||||
switch (dnsProvider) {
|
|
||||||
case DnsProviderType.digitalOcean:
|
|
||||||
dnsProviderType = 'DIGITALOCEAN';
|
|
||||||
break;
|
|
||||||
case DnsProviderType.cloudflare:
|
|
||||||
default:
|
|
||||||
dnsProviderType = 'CLOUDFLARE';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return dnsProviderType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -148,8 +102,7 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
rootUser: installationData.rootUser,
|
rootUser: installationData.rootUser,
|
||||||
domainName: installationData.serverDomain.domainName,
|
domainName: installationData.serverDomain.domainName,
|
||||||
serverType: installationData.serverTypeId,
|
serverType: installationData.serverTypeId,
|
||||||
dnsProviderType:
|
dnsProviderType: installationData.dnsProviderType.toInfectName(),
|
||||||
dnsProviderToInfectName(installationData.dnsProviderType),
|
|
||||||
hostName: hostname,
|
hostName: hostname,
|
||||||
base64Password: base64.encode(
|
base64Password: base64.encode(
|
||||||
utf8.encode(installationData.rootUser.password ?? 'PASS'),
|
utf8.encode(installationData.rootUser.password ?? 'PASS'),
|
||||||
|
@ -244,6 +197,139 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
return GenericResult(success: true, data: null);
|
return GenericResult(success: true, data: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<CallbackDialogueBranching?>> deleteServer(
|
||||||
|
final String hostname,
|
||||||
|
) async {
|
||||||
|
final String deletionName = getHostnameFromDomain(hostname);
|
||||||
|
final serversResult = await getServers();
|
||||||
|
try {
|
||||||
|
final servers = serversResult.data;
|
||||||
|
ServerBasicInfo? foundServer;
|
||||||
|
for (final server in servers) {
|
||||||
|
if (server.name == deletionName) {
|
||||||
|
foundServer = server;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final volumes = await getVolumes();
|
||||||
|
final ServerVolume volumeToRemove;
|
||||||
|
volumeToRemove = volumes.data.firstWhere(
|
||||||
|
(final el) => el.serverId == foundServer!.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
await _adapter.api().detachVolume(
|
||||||
|
volumeToRemove.name,
|
||||||
|
volumeToRemove.serverId!,
|
||||||
|
);
|
||||||
|
|
||||||
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
final List<Future> laterFutures = <Future>[];
|
||||||
|
laterFutures.add(_adapter.api().deleteVolume(volumeToRemove.uuid!));
|
||||||
|
laterFutures.add(_adapter.api().deleteServer(foundServer!.id));
|
||||||
|
|
||||||
|
await Future.wait(laterFutures);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
return GenericResult(
|
||||||
|
success: false,
|
||||||
|
data: CallbackDialogueBranching(
|
||||||
|
choices: [
|
||||||
|
CallbackDialogueChoice(
|
||||||
|
title: 'basis.cancel'.tr(),
|
||||||
|
callback: null,
|
||||||
|
),
|
||||||
|
CallbackDialogueChoice(
|
||||||
|
title: 'modals.try_again'.tr(),
|
||||||
|
callback: () async {
|
||||||
|
await Future.delayed(const Duration(seconds: 5));
|
||||||
|
return deleteServer(hostname);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
description: 'modals.try_again'.tr(),
|
||||||
|
title: 'modals.server_deletion_error'.tr(),
|
||||||
|
),
|
||||||
|
message: e.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
success: true,
|
||||||
|
data: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<bool>> tryInitApiByToken(final String token) async {
|
||||||
|
final api = _adapter.api(getInitialized: false);
|
||||||
|
final result = await api.isApiTokenValid(token);
|
||||||
|
if (!result.data || !result.success) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
_adapter = ApiAdapter(region: api.region, isWithToken: true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<bool>> trySetServerLocation(
|
||||||
|
final String location,
|
||||||
|
) async {
|
||||||
|
final bool apiInitialized = _adapter.api().isWithToken;
|
||||||
|
if (!apiInitialized) {
|
||||||
|
return GenericResult(
|
||||||
|
success: true,
|
||||||
|
data: false,
|
||||||
|
message: 'Not authorized!',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_adapter = ApiAdapter(
|
||||||
|
isWithToken: true,
|
||||||
|
region: location,
|
||||||
|
);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? getEmojiFlag(final String query) {
|
||||||
|
String? emoji;
|
||||||
|
|
||||||
|
switch (query.toLowerCase().substring(0, 3)) {
|
||||||
|
case 'fra':
|
||||||
|
emoji = '🇩🇪';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ams':
|
||||||
|
emoji = '🇳🇱';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sgp':
|
||||||
|
emoji = '🇸🇬';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'lon':
|
||||||
|
emoji = '🇬🇧';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tor':
|
||||||
|
emoji = '🇨🇦';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'blr':
|
||||||
|
emoji = '🇮🇳';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'nyc':
|
||||||
|
case 'sfo':
|
||||||
|
emoji = '🇺🇸';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerProviderLocation>>>
|
Future<GenericResult<List<ServerProviderLocation>>>
|
||||||
getAvailableLocations() async {
|
getAvailableLocations() async {
|
||||||
|
@ -318,43 +404,214 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerBasicInfo>>> getServers() async {
|
Future<GenericResult<DateTime?>> powerOn(final int serverId) async {
|
||||||
List<ServerBasicInfo> servers = [];
|
DateTime? timestamp;
|
||||||
final result = await _adapter.api().getServers();
|
final result = await _adapter.api().powerOn(serverId);
|
||||||
if (result.data.isEmpty || !result.success) {
|
if (!result.success) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
success: result.success,
|
success: false,
|
||||||
data: servers,
|
data: timestamp,
|
||||||
code: result.code,
|
code: result.code,
|
||||||
message: result.message,
|
message: result.message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List rawServers = result.data;
|
timestamp = DateTime.now();
|
||||||
servers = rawServers.map<ServerBasicInfo>(
|
|
||||||
(final server) {
|
|
||||||
String ipv4 = '0.0.0.0';
|
|
||||||
if (server['networks']['v4'].isNotEmpty) {
|
|
||||||
for (final v4 in server['networks']['v4']) {
|
|
||||||
if (v4['type'].toString() == 'public') {
|
|
||||||
ipv4 = v4['ip_address'].toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ServerBasicInfo(
|
return GenericResult(
|
||||||
id: server['id'],
|
success: true,
|
||||||
reverseDns: server['name'],
|
data: timestamp,
|
||||||
created: DateTime.now(),
|
);
|
||||||
ip: ipv4,
|
|
||||||
name: server['name'],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
).toList();
|
|
||||||
|
|
||||||
return GenericResult(success: true, data: servers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<DateTime?>> restart(final int serverId) async {
|
||||||
|
DateTime? timestamp;
|
||||||
|
final result = await _adapter.api().restart(serverId);
|
||||||
|
if (!result.success) {
|
||||||
|
return GenericResult(
|
||||||
|
success: false,
|
||||||
|
data: timestamp,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp = DateTime.now();
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
success: true,
|
||||||
|
data: timestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardcoded on their documentation and there is no pricing API at all
|
||||||
|
/// Probably we should scrap the doc page manually
|
||||||
|
@override
|
||||||
|
Future<GenericResult<Price?>> getPricePerGb() async => GenericResult(
|
||||||
|
success: true,
|
||||||
|
data: Price(
|
||||||
|
value: 0.10,
|
||||||
|
currency: currency,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<List<ServerVolume>>> getVolumes({
|
||||||
|
final String? status,
|
||||||
|
}) async {
|
||||||
|
final List<ServerVolume> volumes = [];
|
||||||
|
|
||||||
|
final result = await _adapter.api().getVolumes();
|
||||||
|
|
||||||
|
if (!result.success || result.data.isEmpty) {
|
||||||
|
return GenericResult(
|
||||||
|
data: [],
|
||||||
|
success: false,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int id = 0;
|
||||||
|
for (final rawVolume in result.data) {
|
||||||
|
final String volumeName = rawVolume.name;
|
||||||
|
final volume = ServerVolume(
|
||||||
|
id: id++,
|
||||||
|
name: volumeName,
|
||||||
|
sizeByte: rawVolume.sizeGigabytes * 1024 * 1024 * 1024,
|
||||||
|
serverId:
|
||||||
|
(rawVolume.dropletIds != null && rawVolume.dropletIds!.isNotEmpty)
|
||||||
|
? rawVolume.dropletIds![0]
|
||||||
|
: null,
|
||||||
|
linuxDevice: 'scsi-0DO_Volume_$volumeName',
|
||||||
|
uuid: rawVolume.id,
|
||||||
|
);
|
||||||
|
volumes.add(volume);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
return GenericResult(
|
||||||
|
data: [],
|
||||||
|
success: false,
|
||||||
|
message: e.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
data: volumes,
|
||||||
|
success: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<ServerVolume?>> createVolume() async {
|
||||||
|
ServerVolume? volume;
|
||||||
|
|
||||||
|
final result = await _adapter.api().createVolume();
|
||||||
|
|
||||||
|
if (!result.success || result.data == null) {
|
||||||
|
return GenericResult(
|
||||||
|
data: null,
|
||||||
|
success: false,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final getVolumesResult = await _adapter.api().getVolumes();
|
||||||
|
|
||||||
|
if (!getVolumesResult.success || getVolumesResult.data.isEmpty) {
|
||||||
|
return GenericResult(
|
||||||
|
data: null,
|
||||||
|
success: false,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String volumeName = result.data!.name;
|
||||||
|
volume = ServerVolume(
|
||||||
|
id: getVolumesResult.data.length,
|
||||||
|
name: volumeName,
|
||||||
|
sizeByte: result.data!.sizeGigabytes,
|
||||||
|
serverId: null,
|
||||||
|
linuxDevice: '/dev/disk/by-id/scsi-0DO_Volume_$volumeName',
|
||||||
|
uuid: result.data!.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
data: volume,
|
||||||
|
success: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<GenericResult<ServerVolume?>> getVolume(
|
||||||
|
final String volumeUuid,
|
||||||
|
) async {
|
||||||
|
ServerVolume? requestedVolume;
|
||||||
|
|
||||||
|
final result = await getVolumes();
|
||||||
|
|
||||||
|
if (!result.success || result.data.isEmpty) {
|
||||||
|
return GenericResult(
|
||||||
|
data: null,
|
||||||
|
success: false,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final volume in result.data) {
|
||||||
|
if (volume.uuid == volumeUuid) {
|
||||||
|
requestedVolume = volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
data: requestedVolume,
|
||||||
|
success: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<bool>> attachVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final int serverId,
|
||||||
|
) async =>
|
||||||
|
_adapter.api().attachVolume(
|
||||||
|
volume.name,
|
||||||
|
serverId,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<bool>> detachVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
) async =>
|
||||||
|
_adapter.api().detachVolume(
|
||||||
|
volume.name,
|
||||||
|
volume.serverId!,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<void>> deleteVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
) async =>
|
||||||
|
_adapter.api().deleteVolume(
|
||||||
|
volume.uuid!,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<bool>> resizeVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final DiskSize size,
|
||||||
|
) async =>
|
||||||
|
_adapter.api().resizeVolume(
|
||||||
|
volume.name,
|
||||||
|
size,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
||||||
final int serverId,
|
final int serverId,
|
||||||
|
@ -536,277 +793,4 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
|
|
||||||
return GenericResult(success: true, data: metrics);
|
return GenericResult(success: true, data: metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<DateTime?>> restart(final int serverId) async {
|
|
||||||
DateTime? timestamp;
|
|
||||||
final result = await _adapter.api().restart(serverId);
|
|
||||||
if (!result.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: timestamp,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp = DateTime.now();
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: timestamp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<CallbackDialogueBranching?>> deleteServer(
|
|
||||||
final String hostname,
|
|
||||||
) async {
|
|
||||||
final String deletionName = getHostnameFromDomain(hostname);
|
|
||||||
final serversResult = await getServers();
|
|
||||||
try {
|
|
||||||
final servers = serversResult.data;
|
|
||||||
ServerBasicInfo? foundServer;
|
|
||||||
for (final server in servers) {
|
|
||||||
if (server.name == deletionName) {
|
|
||||||
foundServer = server;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final volumes = await getVolumes();
|
|
||||||
final ServerVolume volumeToRemove;
|
|
||||||
volumeToRemove = volumes.data.firstWhere(
|
|
||||||
(final el) => el.serverId == foundServer!.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
await _adapter.api().detachVolume(
|
|
||||||
volumeToRemove.name,
|
|
||||||
volumeToRemove.serverId!,
|
|
||||||
);
|
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
|
||||||
final List<Future> laterFutures = <Future>[];
|
|
||||||
laterFutures.add(_adapter.api().deleteVolume(volumeToRemove.uuid!));
|
|
||||||
laterFutures.add(_adapter.api().deleteServer(foundServer!.id));
|
|
||||||
|
|
||||||
await Future.wait(laterFutures);
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: CallbackDialogueBranching(
|
|
||||||
choices: [
|
|
||||||
CallbackDialogueChoice(
|
|
||||||
title: 'basis.cancel'.tr(),
|
|
||||||
callback: null,
|
|
||||||
),
|
|
||||||
CallbackDialogueChoice(
|
|
||||||
title: 'modals.try_again'.tr(),
|
|
||||||
callback: () async {
|
|
||||||
await Future.delayed(const Duration(seconds: 5));
|
|
||||||
return deleteServer(hostname);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
description: 'modals.try_again'.tr(),
|
|
||||||
title: 'modals.server_deletion_error'.tr(),
|
|
||||||
),
|
|
||||||
message: e.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<List<ServerVolume>>> getVolumes({
|
|
||||||
final String? status,
|
|
||||||
}) async {
|
|
||||||
final List<ServerVolume> volumes = [];
|
|
||||||
|
|
||||||
final result = await _adapter.api().getVolumes();
|
|
||||||
|
|
||||||
if (!result.success || result.data.isEmpty) {
|
|
||||||
return GenericResult(
|
|
||||||
data: [],
|
|
||||||
success: false,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
int id = 0;
|
|
||||||
for (final rawVolume in result.data) {
|
|
||||||
final String volumeName = rawVolume.name;
|
|
||||||
final volume = ServerVolume(
|
|
||||||
id: id++,
|
|
||||||
name: volumeName,
|
|
||||||
sizeByte: rawVolume.sizeGigabytes * 1024 * 1024 * 1024,
|
|
||||||
serverId:
|
|
||||||
(rawVolume.dropletIds != null && rawVolume.dropletIds!.isNotEmpty)
|
|
||||||
? rawVolume.dropletIds![0]
|
|
||||||
: null,
|
|
||||||
linuxDevice: 'scsi-0DO_Volume_$volumeName',
|
|
||||||
uuid: rawVolume.id,
|
|
||||||
);
|
|
||||||
volumes.add(volume);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
return GenericResult(
|
|
||||||
data: [],
|
|
||||||
success: false,
|
|
||||||
message: e.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
data: volumes,
|
|
||||||
success: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<ServerVolume?>> createVolume() async {
|
|
||||||
ServerVolume? volume;
|
|
||||||
|
|
||||||
final result = await _adapter.api().createVolume();
|
|
||||||
|
|
||||||
if (!result.success || result.data == null) {
|
|
||||||
return GenericResult(
|
|
||||||
data: null,
|
|
||||||
success: false,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final getVolumesResult = await _adapter.api().getVolumes();
|
|
||||||
|
|
||||||
if (!getVolumesResult.success || getVolumesResult.data.isEmpty) {
|
|
||||||
return GenericResult(
|
|
||||||
data: null,
|
|
||||||
success: false,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String volumeName = result.data!.name;
|
|
||||||
volume = ServerVolume(
|
|
||||||
id: getVolumesResult.data.length,
|
|
||||||
name: volumeName,
|
|
||||||
sizeByte: result.data!.sizeGigabytes,
|
|
||||||
serverId: null,
|
|
||||||
linuxDevice: '/dev/disk/by-id/scsi-0DO_Volume_$volumeName',
|
|
||||||
uuid: result.data!.id,
|
|
||||||
);
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
data: volume,
|
|
||||||
success: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<GenericResult<ServerVolume?>> getVolume(
|
|
||||||
final String volumeUuid,
|
|
||||||
) async {
|
|
||||||
ServerVolume? requestedVolume;
|
|
||||||
|
|
||||||
final result = await getVolumes();
|
|
||||||
|
|
||||||
if (!result.success || result.data.isEmpty) {
|
|
||||||
return GenericResult(
|
|
||||||
data: null,
|
|
||||||
success: false,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final volume in result.data) {
|
|
||||||
if (volume.uuid == volumeUuid) {
|
|
||||||
requestedVolume = volume;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
data: requestedVolume,
|
|
||||||
success: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<void>> deleteVolume(
|
|
||||||
final ServerVolume volume,
|
|
||||||
) async =>
|
|
||||||
_adapter.api().deleteVolume(
|
|
||||||
volume.uuid!,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<bool>> attachVolume(
|
|
||||||
final ServerVolume volume,
|
|
||||||
final int serverId,
|
|
||||||
) async =>
|
|
||||||
_adapter.api().attachVolume(
|
|
||||||
volume.name,
|
|
||||||
serverId,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<bool>> detachVolume(
|
|
||||||
final ServerVolume volume,
|
|
||||||
) async =>
|
|
||||||
_adapter.api().detachVolume(
|
|
||||||
volume.name,
|
|
||||||
volume.serverId!,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<bool>> resizeVolume(
|
|
||||||
final ServerVolume volume,
|
|
||||||
final DiskSize size,
|
|
||||||
) async =>
|
|
||||||
_adapter.api().resizeVolume(
|
|
||||||
volume.name,
|
|
||||||
size,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Hardcoded on their documentation and there is no pricing API at all
|
|
||||||
/// Probably we should scrap the doc page manually
|
|
||||||
@override
|
|
||||||
Future<GenericResult<Price?>> getPricePerGb() async => GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: Price(
|
|
||||||
value: 0.10,
|
|
||||||
currency: currency,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<DateTime?>> powerOn(final int serverId) async {
|
|
||||||
DateTime? timestamp;
|
|
||||||
final result = await _adapter.api().powerOn(serverId);
|
|
||||||
if (!result.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: timestamp,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp = DateTime.now();
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: timestamp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/he
|
||||||
import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart';
|
import 'package:selfprivacy/logic/models/callback_dialogue_branching.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
|
||||||
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/logic/models/price.dart';
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
|
@ -51,131 +50,6 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
@override
|
@override
|
||||||
ServerProviderType get type => ServerProviderType.hetzner;
|
ServerProviderType get type => ServerProviderType.hetzner;
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<bool>> trySetServerLocation(
|
|
||||||
final String location,
|
|
||||||
) async {
|
|
||||||
final bool apiInitialized = _adapter.api().isWithToken;
|
|
||||||
if (!apiInitialized) {
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: false,
|
|
||||||
message: 'Not authorized!',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_adapter = ApiAdapter(
|
|
||||||
isWithToken: true,
|
|
||||||
region: location,
|
|
||||||
);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<bool>> tryInitApiByToken(final String token) async {
|
|
||||||
final api = _adapter.api(getInitialized: false);
|
|
||||||
final result = await api.isApiTokenValid(token);
|
|
||||||
if (!result.data || !result.success) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
_adapter = ApiAdapter(region: api.region, isWithToken: true);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
String? getEmojiFlag(final String query) {
|
|
||||||
String? emoji;
|
|
||||||
|
|
||||||
switch (query.toLowerCase()) {
|
|
||||||
case 'de':
|
|
||||||
emoji = '🇩🇪';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'fi':
|
|
||||||
emoji = '🇫🇮';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'us':
|
|
||||||
emoji = '🇺🇸';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return emoji;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<List<ServerProviderLocation>>>
|
|
||||||
getAvailableLocations() async {
|
|
||||||
final List<ServerProviderLocation> locations = [];
|
|
||||||
final result = await _adapter.api().getAvailableLocations();
|
|
||||||
if (result.data.isEmpty || !result.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: result.success,
|
|
||||||
data: locations,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<HetznerLocation> rawLocations = result.data;
|
|
||||||
for (final rawLocation in rawLocations) {
|
|
||||||
ServerProviderLocation? location;
|
|
||||||
try {
|
|
||||||
location = ServerProviderLocation(
|
|
||||||
title: rawLocation.city,
|
|
||||||
description: rawLocation.description,
|
|
||||||
flag: getEmojiFlag(rawLocation.country),
|
|
||||||
identifier: rawLocation.name,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
locations.add(location);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenericResult(success: true, data: locations);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<List<ServerType>>> getServerTypes({
|
|
||||||
required final ServerProviderLocation location,
|
|
||||||
}) async {
|
|
||||||
final List<ServerType> types = [];
|
|
||||||
final result = await _adapter.api().getAvailableServerTypes();
|
|
||||||
if (result.data.isEmpty || !result.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: result.success,
|
|
||||||
data: types,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final rawTypes = result.data;
|
|
||||||
for (final rawType in rawTypes) {
|
|
||||||
for (final rawPrice in rawType.prices) {
|
|
||||||
if (rawPrice.location == location.identifier) {
|
|
||||||
types.add(
|
|
||||||
ServerType(
|
|
||||||
title: rawType.description,
|
|
||||||
identifier: rawType.name,
|
|
||||||
ram: rawType.memory.toDouble(),
|
|
||||||
cores: rawType.cores,
|
|
||||||
disk: DiskSize(byte: rawType.disk * 1024 * 1024 * 1024),
|
|
||||||
price: Price(
|
|
||||||
value: rawPrice.monthly,
|
|
||||||
currency: currency,
|
|
||||||
),
|
|
||||||
location: location,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenericResult(success: true, data: types);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerBasicInfo>>> getServers() async {
|
Future<GenericResult<List<ServerBasicInfo>>> getServers() async {
|
||||||
final List<ServerBasicInfo> servers = [];
|
final List<ServerBasicInfo> servers = [];
|
||||||
|
@ -214,206 +88,6 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
return GenericResult(success: true, data: servers);
|
return GenericResult(success: true, data: servers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
|
||||||
final int serverId,
|
|
||||||
) async {
|
|
||||||
List<ServerMetadataEntity> metadata = [];
|
|
||||||
final result = await _adapter.api().getServers();
|
|
||||||
if (result.data.isEmpty || !result.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: metadata,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<HetznerServerInfo> servers = result.data;
|
|
||||||
try {
|
|
||||||
final HetznerServerInfo server = servers.firstWhere(
|
|
||||||
(final server) => server.id == serverId,
|
|
||||||
);
|
|
||||||
|
|
||||||
metadata = [
|
|
||||||
ServerMetadataEntity(
|
|
||||||
type: MetadataType.id,
|
|
||||||
trId: 'server.server_id',
|
|
||||||
value: server.id.toString(),
|
|
||||||
),
|
|
||||||
ServerMetadataEntity(
|
|
||||||
type: MetadataType.status,
|
|
||||||
trId: 'server.status',
|
|
||||||
value: server.status.toString().split('.')[1].capitalize(),
|
|
||||||
),
|
|
||||||
ServerMetadataEntity(
|
|
||||||
type: MetadataType.cpu,
|
|
||||||
trId: 'server.cpu',
|
|
||||||
value: server.serverType.cores.toString(),
|
|
||||||
),
|
|
||||||
ServerMetadataEntity(
|
|
||||||
type: MetadataType.ram,
|
|
||||||
trId: 'server.ram',
|
|
||||||
value: '${server.serverType.memory.toString()} GB',
|
|
||||||
),
|
|
||||||
ServerMetadataEntity(
|
|
||||||
type: MetadataType.cost,
|
|
||||||
trId: 'server.monthly_cost',
|
|
||||||
value:
|
|
||||||
'${server.serverType.prices[1].monthly.toStringAsFixed(2)} ${currency.shortcode}',
|
|
||||||
),
|
|
||||||
ServerMetadataEntity(
|
|
||||||
type: MetadataType.location,
|
|
||||||
trId: 'server.location',
|
|
||||||
value: '${server.location.city}, ${server.location.country}',
|
|
||||||
),
|
|
||||||
ServerMetadataEntity(
|
|
||||||
type: MetadataType.other,
|
|
||||||
trId: 'server.provider',
|
|
||||||
value: _adapter.api().displayProviderName,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
} catch (e) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: [],
|
|
||||||
message: e.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenericResult(success: true, data: metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<ServerMetrics?>> getMetrics(
|
|
||||||
final int serverId,
|
|
||||||
final DateTime start,
|
|
||||||
final DateTime end,
|
|
||||||
) async {
|
|
||||||
ServerMetrics? metrics;
|
|
||||||
|
|
||||||
List<TimeSeriesData> serializeTimeSeries(
|
|
||||||
final Map<String, dynamic> json,
|
|
||||||
final String type,
|
|
||||||
) {
|
|
||||||
final List list = json['time_series'][type]['values'];
|
|
||||||
return list
|
|
||||||
.map((final el) => TimeSeriesData(el[0], double.parse(el[1])))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
final cpuResult = await _adapter.api().getMetrics(
|
|
||||||
serverId,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
'cpu',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cpuResult.data.isEmpty || !cpuResult.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: metrics,
|
|
||||||
code: cpuResult.code,
|
|
||||||
message: cpuResult.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final netResult = await _adapter.api().getMetrics(
|
|
||||||
serverId,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
'network',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (cpuResult.data.isEmpty || !netResult.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: metrics,
|
|
||||||
code: netResult.code,
|
|
||||||
message: netResult.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics = ServerMetrics(
|
|
||||||
cpu: serializeTimeSeries(
|
|
||||||
cpuResult.data,
|
|
||||||
'cpu',
|
|
||||||
),
|
|
||||||
bandwidthIn: serializeTimeSeries(
|
|
||||||
netResult.data,
|
|
||||||
'network.0.bandwidth.in',
|
|
||||||
),
|
|
||||||
bandwidthOut: serializeTimeSeries(
|
|
||||||
netResult.data,
|
|
||||||
'network.0.bandwidth.out',
|
|
||||||
),
|
|
||||||
end: end,
|
|
||||||
start: start,
|
|
||||||
stepsInSecond: cpuResult.data['step'],
|
|
||||||
);
|
|
||||||
|
|
||||||
return GenericResult(data: metrics, success: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<DateTime?>> restart(final int serverId) async {
|
|
||||||
DateTime? timestamp;
|
|
||||||
final result = await _adapter.api().restart(serverId);
|
|
||||||
if (!result.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: timestamp,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp = DateTime.now();
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: timestamp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<DateTime?>> powerOn(final int serverId) async {
|
|
||||||
DateTime? timestamp;
|
|
||||||
final result = await _adapter.api().powerOn(serverId);
|
|
||||||
if (!result.success) {
|
|
||||||
return GenericResult(
|
|
||||||
success: false,
|
|
||||||
data: timestamp,
|
|
||||||
code: result.code,
|
|
||||||
message: result.message,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp = DateTime.now();
|
|
||||||
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: timestamp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String dnsProviderToInfectName(final DnsProviderType dnsProvider) {
|
|
||||||
String dnsProviderType;
|
|
||||||
switch (dnsProvider) {
|
|
||||||
case DnsProviderType.digitalOcean:
|
|
||||||
dnsProviderType = 'DIGITALOCEAN';
|
|
||||||
break;
|
|
||||||
case DnsProviderType.desec:
|
|
||||||
dnsProviderType = 'DESEC';
|
|
||||||
break;
|
|
||||||
case DnsProviderType.cloudflare:
|
|
||||||
default:
|
|
||||||
dnsProviderType = 'CLOUDFLARE';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return dnsProviderType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<CallbackDialogueBranching?>> launchInstallation(
|
Future<GenericResult<CallbackDialogueBranching?>> launchInstallation(
|
||||||
final LaunchInstallationData installationData,
|
final LaunchInstallationData installationData,
|
||||||
|
@ -454,8 +128,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
rootUser: installationData.rootUser,
|
rootUser: installationData.rootUser,
|
||||||
domainName: installationData.serverDomain.domainName,
|
domainName: installationData.serverDomain.domainName,
|
||||||
serverType: installationData.serverTypeId,
|
serverType: installationData.serverTypeId,
|
||||||
dnsProviderType:
|
dnsProviderType: installationData.dnsProviderType.toInfectName(),
|
||||||
dnsProviderToInfectName(installationData.dnsProviderType),
|
|
||||||
hostName: hostname,
|
hostName: hostname,
|
||||||
volumeId: volume.id,
|
volumeId: volume.id,
|
||||||
base64Password: base64.encode(
|
base64Password: base64.encode(
|
||||||
|
@ -651,10 +324,175 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<ServerVolume?>> createVolume() async {
|
Future<GenericResult<bool>> tryInitApiByToken(final String token) async {
|
||||||
ServerVolume? volume;
|
final api = _adapter.api(getInitialized: false);
|
||||||
|
final result = await api.isApiTokenValid(token);
|
||||||
|
if (!result.data || !result.success) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
final result = await _adapter.api().createVolume();
|
_adapter = ApiAdapter(region: api.region, isWithToken: true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? getEmojiFlag(final String query) {
|
||||||
|
String? emoji;
|
||||||
|
|
||||||
|
switch (query.toLowerCase()) {
|
||||||
|
case 'de':
|
||||||
|
emoji = '🇩🇪';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'fi':
|
||||||
|
emoji = '🇫🇮';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'us':
|
||||||
|
emoji = '🇺🇸';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<bool>> trySetServerLocation(
|
||||||
|
final String location,
|
||||||
|
) async {
|
||||||
|
final bool apiInitialized = _adapter.api().isWithToken;
|
||||||
|
if (!apiInitialized) {
|
||||||
|
return GenericResult(
|
||||||
|
success: true,
|
||||||
|
data: false,
|
||||||
|
message: 'Not authorized!',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_adapter = ApiAdapter(
|
||||||
|
isWithToken: true,
|
||||||
|
region: location,
|
||||||
|
);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<List<ServerProviderLocation>>>
|
||||||
|
getAvailableLocations() async {
|
||||||
|
final List<ServerProviderLocation> locations = [];
|
||||||
|
final result = await _adapter.api().getAvailableLocations();
|
||||||
|
if (result.data.isEmpty || !result.success) {
|
||||||
|
return GenericResult(
|
||||||
|
success: result.success,
|
||||||
|
data: locations,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<HetznerLocation> rawLocations = result.data;
|
||||||
|
for (final rawLocation in rawLocations) {
|
||||||
|
ServerProviderLocation? location;
|
||||||
|
try {
|
||||||
|
location = ServerProviderLocation(
|
||||||
|
title: rawLocation.city,
|
||||||
|
description: rawLocation.description,
|
||||||
|
flag: getEmojiFlag(rawLocation.country),
|
||||||
|
identifier: rawLocation.name,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
locations.add(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenericResult(success: true, data: locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<List<ServerType>>> getServerTypes({
|
||||||
|
required final ServerProviderLocation location,
|
||||||
|
}) async {
|
||||||
|
final List<ServerType> types = [];
|
||||||
|
final result = await _adapter.api().getAvailableServerTypes();
|
||||||
|
if (result.data.isEmpty || !result.success) {
|
||||||
|
return GenericResult(
|
||||||
|
success: result.success,
|
||||||
|
data: types,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final rawTypes = result.data;
|
||||||
|
for (final rawType in rawTypes) {
|
||||||
|
for (final rawPrice in rawType.prices) {
|
||||||
|
if (rawPrice.location == location.identifier) {
|
||||||
|
types.add(
|
||||||
|
ServerType(
|
||||||
|
title: rawType.description,
|
||||||
|
identifier: rawType.name,
|
||||||
|
ram: rawType.memory.toDouble(),
|
||||||
|
cores: rawType.cores,
|
||||||
|
disk: DiskSize(byte: rawType.disk * 1024 * 1024 * 1024),
|
||||||
|
price: Price(
|
||||||
|
value: rawPrice.monthly,
|
||||||
|
currency: currency,
|
||||||
|
),
|
||||||
|
location: location,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenericResult(success: true, data: types);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<DateTime?>> powerOn(final int serverId) async {
|
||||||
|
DateTime? timestamp;
|
||||||
|
final result = await _adapter.api().powerOn(serverId);
|
||||||
|
if (!result.success) {
|
||||||
|
return GenericResult(
|
||||||
|
success: false,
|
||||||
|
data: timestamp,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp = DateTime.now();
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
success: true,
|
||||||
|
data: timestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<DateTime?>> restart(final int serverId) async {
|
||||||
|
DateTime? timestamp;
|
||||||
|
final result = await _adapter.api().restart(serverId);
|
||||||
|
if (!result.success) {
|
||||||
|
return GenericResult(
|
||||||
|
success: false,
|
||||||
|
data: timestamp,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp = DateTime.now();
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
success: true,
|
||||||
|
data: timestamp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<Price?>> getPricePerGb() async {
|
||||||
|
final result = await _adapter.api().getPricePerGb();
|
||||||
|
|
||||||
if (!result.success || result.data == null) {
|
if (!result.success || result.data == null) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
|
@ -665,28 +503,12 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
volume = ServerVolume(
|
|
||||||
id: result.data!.id,
|
|
||||||
name: result.data!.name,
|
|
||||||
sizeByte: result.data!.size * 1024 * 1024 * 1024,
|
|
||||||
serverId: result.data!.serverId,
|
|
||||||
linuxDevice: result.data!.linuxDevice,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
return GenericResult(
|
|
||||||
data: null,
|
|
||||||
success: false,
|
|
||||||
message: e.toString(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
data: volume,
|
|
||||||
success: true,
|
success: true,
|
||||||
code: result.code,
|
data: Price(
|
||||||
message: result.message,
|
value: result.data!,
|
||||||
|
currency: currency,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,10 +561,66 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<ServerVolume?>> createVolume() async {
|
||||||
|
ServerVolume? volume;
|
||||||
|
|
||||||
|
final result = await _adapter.api().createVolume();
|
||||||
|
|
||||||
|
if (!result.success || result.data == null) {
|
||||||
|
return GenericResult(
|
||||||
|
data: null,
|
||||||
|
success: false,
|
||||||
|
message: result.message,
|
||||||
|
code: result.code,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
volume = ServerVolume(
|
||||||
|
id: result.data!.id,
|
||||||
|
name: result.data!.name,
|
||||||
|
sizeByte: result.data!.size * 1024 * 1024 * 1024,
|
||||||
|
serverId: result.data!.serverId,
|
||||||
|
linuxDevice: result.data!.linuxDevice,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
return GenericResult(
|
||||||
|
data: null,
|
||||||
|
success: false,
|
||||||
|
message: e.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenericResult(
|
||||||
|
data: volume,
|
||||||
|
success: true,
|
||||||
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<void>> deleteVolume(final ServerVolume volume) async =>
|
Future<GenericResult<void>> deleteVolume(final ServerVolume volume) async =>
|
||||||
_adapter.api().deleteVolume(volume.id);
|
_adapter.api().deleteVolume(volume.id);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<bool>> resizeVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final DiskSize size,
|
||||||
|
) async =>
|
||||||
|
_adapter.api().resizeVolume(
|
||||||
|
HetznerVolume(
|
||||||
|
volume.id,
|
||||||
|
volume.sizeByte,
|
||||||
|
volume.serverId,
|
||||||
|
volume.name,
|
||||||
|
volume.linuxDevice,
|
||||||
|
),
|
||||||
|
size,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<bool>> attachVolume(
|
Future<GenericResult<bool>> attachVolume(
|
||||||
final ServerVolume volume,
|
final ServerVolume volume,
|
||||||
|
@ -768,40 +646,143 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<bool>> resizeVolume(
|
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
||||||
final ServerVolume volume,
|
final int serverId,
|
||||||
final DiskSize size,
|
) async {
|
||||||
) async =>
|
List<ServerMetadataEntity> metadata = [];
|
||||||
_adapter.api().resizeVolume(
|
final result = await _adapter.api().getServers();
|
||||||
HetznerVolume(
|
if (result.data.isEmpty || !result.success) {
|
||||||
volume.id,
|
|
||||||
volume.sizeByte,
|
|
||||||
volume.serverId,
|
|
||||||
volume.name,
|
|
||||||
volume.linuxDevice,
|
|
||||||
),
|
|
||||||
size,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<Price?>> getPricePerGb() async {
|
|
||||||
final result = await _adapter.api().getPricePerGb();
|
|
||||||
|
|
||||||
if (!result.success || result.data == null) {
|
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
data: null,
|
|
||||||
success: false,
|
success: false,
|
||||||
message: result.message,
|
data: metadata,
|
||||||
code: result.code,
|
code: result.code,
|
||||||
|
message: result.message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GenericResult(
|
final List<HetznerServerInfo> servers = result.data;
|
||||||
success: true,
|
try {
|
||||||
data: Price(
|
final HetznerServerInfo server = servers.firstWhere(
|
||||||
value: result.data!,
|
(final server) => server.id == serverId,
|
||||||
currency: currency,
|
);
|
||||||
|
|
||||||
|
metadata = [
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.id,
|
||||||
|
trId: 'server.server_id',
|
||||||
|
value: server.id.toString(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.status,
|
||||||
|
trId: 'server.status',
|
||||||
|
value: server.status.toString().split('.')[1].capitalize(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.cpu,
|
||||||
|
trId: 'server.cpu',
|
||||||
|
value: server.serverType.cores.toString(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.ram,
|
||||||
|
trId: 'server.ram',
|
||||||
|
value: '${server.serverType.memory.toString()} GB',
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.cost,
|
||||||
|
trId: 'server.monthly_cost',
|
||||||
|
value:
|
||||||
|
'${server.serverType.prices[1].monthly.toStringAsFixed(2)} ${currency.shortcode}',
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.location,
|
||||||
|
trId: 'server.location',
|
||||||
|
value: '${server.location.city}, ${server.location.country}',
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.other,
|
||||||
|
trId: 'server.provider',
|
||||||
|
value: _adapter.api().displayProviderName,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} catch (e) {
|
||||||
|
return GenericResult(
|
||||||
|
success: false,
|
||||||
|
data: [],
|
||||||
|
message: e.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenericResult(success: true, data: metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<GenericResult<ServerMetrics?>> getMetrics(
|
||||||
|
final int serverId,
|
||||||
|
final DateTime start,
|
||||||
|
final DateTime end,
|
||||||
|
) async {
|
||||||
|
ServerMetrics? metrics;
|
||||||
|
|
||||||
|
List<TimeSeriesData> serializeTimeSeries(
|
||||||
|
final Map<String, dynamic> json,
|
||||||
|
final String type,
|
||||||
|
) {
|
||||||
|
final List list = json['time_series'][type]['values'];
|
||||||
|
return list
|
||||||
|
.map((final el) => TimeSeriesData(el[0], double.parse(el[1])))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
final cpuResult = await _adapter.api().getMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
'cpu',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cpuResult.data.isEmpty || !cpuResult.success) {
|
||||||
|
return GenericResult(
|
||||||
|
success: false,
|
||||||
|
data: metrics,
|
||||||
|
code: cpuResult.code,
|
||||||
|
message: cpuResult.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final netResult = await _adapter.api().getMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
'network',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cpuResult.data.isEmpty || !netResult.success) {
|
||||||
|
return GenericResult(
|
||||||
|
success: false,
|
||||||
|
data: metrics,
|
||||||
|
code: netResult.code,
|
||||||
|
message: netResult.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics = ServerMetrics(
|
||||||
|
cpu: serializeTimeSeries(
|
||||||
|
cpuResult.data,
|
||||||
|
'cpu',
|
||||||
),
|
),
|
||||||
|
bandwidthIn: serializeTimeSeries(
|
||||||
|
netResult.data,
|
||||||
|
'network.0.bandwidth.in',
|
||||||
|
),
|
||||||
|
bandwidthOut: serializeTimeSeries(
|
||||||
|
netResult.data,
|
||||||
|
'network.0.bandwidth.out',
|
||||||
|
),
|
||||||
|
end: end,
|
||||||
|
start: start,
|
||||||
|
stepsInSecond: cpuResult.data['step'],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return GenericResult(data: metrics, success: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,44 +14,124 @@ export 'package:selfprivacy/logic/api_maps/generic_result.dart';
|
||||||
export 'package:selfprivacy/logic/models/launch_installation_data.dart';
|
export 'package:selfprivacy/logic/models/launch_installation_data.dart';
|
||||||
|
|
||||||
abstract class ServerProvider {
|
abstract class ServerProvider {
|
||||||
|
/// Returns an assigned enum value, respectively to which
|
||||||
|
/// provider implements [ServerProvider] interface.
|
||||||
ServerProviderType get type;
|
ServerProviderType get type;
|
||||||
|
|
||||||
|
/// Returns [ServerBasicInfo] of all available machines
|
||||||
|
/// assigned to the authorized user.
|
||||||
|
///
|
||||||
|
/// Only with public IPv4 addresses.
|
||||||
Future<GenericResult<List<ServerBasicInfo>>> getServers();
|
Future<GenericResult<List<ServerBasicInfo>>> getServers();
|
||||||
Future<GenericResult<bool>> trySetServerLocation(final String location);
|
|
||||||
|
/// Tries to launch installation of SelfPrivacy on
|
||||||
|
/// the requested server entry for the authorized account.
|
||||||
|
/// Depending on a server provider, the algorithm
|
||||||
|
/// and instructions vary drastically.
|
||||||
|
///
|
||||||
|
/// If successly launched, stores new server information.
|
||||||
|
///
|
||||||
|
/// If failure, returns error dialogue information to
|
||||||
|
/// write in ServerInstallationState.
|
||||||
|
Future<GenericResult<CallbackDialogueBranching?>> launchInstallation(
|
||||||
|
final LaunchInstallationData installationData,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Tries to delete the requested server entry
|
||||||
|
/// from the authorized account, including all assigned
|
||||||
|
/// storage extensions.
|
||||||
|
///
|
||||||
|
/// If failure, returns error dialogue information to
|
||||||
|
/// write in ServerInstallationState.
|
||||||
|
Future<GenericResult<CallbackDialogueBranching?>> deleteServer(
|
||||||
|
final String hostname,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Tries to access an account linked to the provided token.
|
||||||
|
///
|
||||||
|
/// To generate a token for your account follow instructions of your
|
||||||
|
/// server provider respectfully.
|
||||||
|
///
|
||||||
|
/// If success, saves it for future usage.
|
||||||
Future<GenericResult<bool>> tryInitApiByToken(final String token);
|
Future<GenericResult<bool>> tryInitApiByToken(final String token);
|
||||||
|
|
||||||
|
/// Tries to assign the location shortcode for future usage.
|
||||||
|
///
|
||||||
|
/// If API wasn't initialized with token by [tryInitApiByToken] beforehand,
|
||||||
|
/// returns 'Not authorized!' error.
|
||||||
|
Future<GenericResult<bool>> trySetServerLocation(final String location);
|
||||||
|
|
||||||
|
/// Returns all available server locations
|
||||||
|
/// of the authorized user's server provider.
|
||||||
Future<GenericResult<List<ServerProviderLocation>>> getAvailableLocations();
|
Future<GenericResult<List<ServerProviderLocation>>> getAvailableLocations();
|
||||||
|
|
||||||
|
/// Returns [ServerType] of all available
|
||||||
|
/// machine configurations available to the authorized account
|
||||||
|
/// within the requested provider location,
|
||||||
|
/// accessed from [getAvailableLocations].
|
||||||
Future<GenericResult<List<ServerType>>> getServerTypes({
|
Future<GenericResult<List<ServerType>>> getServerTypes({
|
||||||
required final ServerProviderLocation location,
|
required final ServerProviderLocation location,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<GenericResult<CallbackDialogueBranching?>> deleteServer(
|
/// Tries to power on the requested accessible machine.
|
||||||
final String hostname,
|
///
|
||||||
);
|
/// If success, returns [DateTime] of when the server
|
||||||
Future<GenericResult<CallbackDialogueBranching?>> launchInstallation(
|
/// answered the request.
|
||||||
final LaunchInstallationData installationData,
|
|
||||||
);
|
|
||||||
Future<GenericResult<DateTime?>> powerOn(final int serverId);
|
Future<GenericResult<DateTime?>> powerOn(final int serverId);
|
||||||
|
|
||||||
|
/// Tries to restart the requested accessible machine.
|
||||||
|
///
|
||||||
|
/// If success, returns [DateTime] of when the server
|
||||||
|
/// answered the request.
|
||||||
Future<GenericResult<DateTime?>> restart(final int serverId);
|
Future<GenericResult<DateTime?>> restart(final int serverId);
|
||||||
|
|
||||||
|
/// Returns [Price] information per one gigabyte of storage extension for
|
||||||
|
/// the requested accessible machine.
|
||||||
|
Future<GenericResult<Price?>> getPricePerGb();
|
||||||
|
|
||||||
|
/// Returns [ServerVolume] of all available volumes
|
||||||
|
/// assigned to the authorized user and attached to active machine.
|
||||||
|
Future<GenericResult<List<ServerVolume>>> getVolumes({final String? status});
|
||||||
|
|
||||||
|
/// Tries to create an empty unattached [ServerVolume].
|
||||||
|
///
|
||||||
|
/// If success, returns this volume information.
|
||||||
|
Future<GenericResult<ServerVolume?>> createVolume();
|
||||||
|
|
||||||
|
/// Tries to delete the requested accessible [ServerVolume].
|
||||||
|
Future<GenericResult<void>> deleteVolume(final ServerVolume volume);
|
||||||
|
|
||||||
|
/// Tries to resize the requested accessible [ServerVolume]
|
||||||
|
/// to the provided size **(not by!)**, must be greater than current size.
|
||||||
|
Future<GenericResult<bool>> resizeVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final DiskSize size,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Tries to attach the requested accessible [ServerVolume]
|
||||||
|
/// to an accessible machine by the provided identificator.
|
||||||
|
Future<GenericResult<bool>> attachVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final int serverId,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Tries to attach the requested accessible [ServerVolume]
|
||||||
|
/// from any machine.
|
||||||
|
Future<GenericResult<bool>> detachVolume(final ServerVolume volume);
|
||||||
|
|
||||||
|
/// Returns metedata of an accessible machine by the provided identificator
|
||||||
|
/// to show on ServerDetailsScreen.
|
||||||
|
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
||||||
|
final int serverId,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Returns information about cpu and bandwidth load within the provided
|
||||||
|
/// time period of the requested accessible machine.
|
||||||
Future<GenericResult<ServerMetrics?>> getMetrics(
|
Future<GenericResult<ServerMetrics?>> getMetrics(
|
||||||
final int serverId,
|
final int serverId,
|
||||||
final DateTime start,
|
final DateTime start,
|
||||||
final DateTime end,
|
final DateTime end,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<GenericResult<Price?>> getPricePerGb();
|
|
||||||
Future<GenericResult<List<ServerVolume>>> getVolumes({final String? status});
|
|
||||||
Future<GenericResult<ServerVolume?>> createVolume();
|
|
||||||
Future<GenericResult<void>> deleteVolume(final ServerVolume volume);
|
|
||||||
Future<GenericResult<bool>> resizeVolume(
|
|
||||||
final ServerVolume volume,
|
|
||||||
final DiskSize size,
|
|
||||||
);
|
|
||||||
Future<GenericResult<bool>> attachVolume(
|
|
||||||
final ServerVolume volume,
|
|
||||||
final int serverId,
|
|
||||||
);
|
|
||||||
Future<GenericResult<bool>> detachVolume(final ServerVolume volume);
|
|
||||||
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
|
||||||
final int serverId,
|
|
||||||
);
|
|
||||||
GenericResult<bool> get success => GenericResult(success: true, data: true);
|
GenericResult<bool> get success => GenericResult(success: true, data: true);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue