fix: Make improvements by Code Review
parent
a70e793360
commit
8c6b56f61d
|
@ -271,7 +271,7 @@
|
||||||
"connect_to_server": "Подключите сервер",
|
"connect_to_server": "Подключите сервер",
|
||||||
"place_where_data": "Здесь будут жить ваши данные и SelfPrivacy-сервисы:",
|
"place_where_data": "Здесь будут жить ваши данные и SelfPrivacy-сервисы:",
|
||||||
"how": "Как получить API Token",
|
"how": "Как получить API Token",
|
||||||
"provider_bad_key_error": "Provider API key is invalid",
|
"provider_bad_key_error": "API ключ провайдера неверен",
|
||||||
"choose_location_type": "Выберите локацию и тип вашего сервера:",
|
"choose_location_type": "Выберите локацию и тип вашего сервера:",
|
||||||
"back_to_locations": "Назад к доступным локациям!",
|
"back_to_locations": "Назад к доступным локациям!",
|
||||||
"no_locations_found": "Не найдено локаций. Убедитесь, что ваш аккаунт доступен.",
|
"no_locations_found": "Не найдено локаций. Убедитесь, что ваш аккаунт доступен.",
|
||||||
|
|
|
@ -7,19 +7,21 @@ import 'package:selfprivacy/logic/api_maps/staging_options.dart';
|
||||||
|
|
||||||
abstract class ApiMap {
|
abstract class ApiMap {
|
||||||
Future<GraphQLClient> getClient() async {
|
Future<GraphQLClient> getClient() async {
|
||||||
final HttpClient httpClient = HttpClient();
|
IOClient? ioClient;
|
||||||
if (StagingOptions.stagingAcme) {
|
if (StagingOptions.stagingAcme) {
|
||||||
|
final HttpClient httpClient = HttpClient();
|
||||||
httpClient.badCertificateCallback = (
|
httpClient.badCertificateCallback = (
|
||||||
final cert,
|
final cert,
|
||||||
final host,
|
final host,
|
||||||
final port,
|
final port,
|
||||||
) =>
|
) =>
|
||||||
true;
|
true;
|
||||||
|
ioClient = IOClient(httpClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
final httpLink = HttpLink(
|
final httpLink = HttpLink(
|
||||||
'https://api.$rootAddress/graphql',
|
'https://api.$rootAddress/graphql',
|
||||||
httpClient: IOClient(httpClient),
|
httpClient: ioClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
final String token = _getApiToken();
|
final String token = _getApiToken();
|
||||||
|
|
|
@ -90,7 +90,7 @@ class ServerApi extends ApiMap
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerProvider> getServerProviderType() async {
|
Future<ServerProvider> getServerProviderType() async {
|
||||||
QueryResult response;
|
QueryResult<Query$SystemServerProvider> response;
|
||||||
ServerProvider providerType = ServerProvider.unknown;
|
ServerProvider providerType = ServerProvider.unknown;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -99,15 +99,9 @@ class ServerApi extends ApiMap
|
||||||
if (response.hasException) {
|
if (response.hasException) {
|
||||||
print(response.exception.toString());
|
print(response.exception.toString());
|
||||||
}
|
}
|
||||||
final rawProviderValue = response.data!['system']['provider']['provider'];
|
providerType = ServerProvider.fromGraphQL(
|
||||||
switch (rawProviderValue) {
|
response.parsedData!.system.provider.provider,
|
||||||
case 'HETZNER':
|
);
|
||||||
providerType = ServerProvider.hetzner;
|
|
||||||
break;
|
|
||||||
case 'DIGITALOCEAN':
|
|
||||||
providerType = ServerProvider.digitalOcean;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
String get infectProviderName => 'digitalocean';
|
String get infectProviderName => 'digitalocean';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appearanceProviderName => 'Digital Ocean';
|
String get displayProviderName => 'Digital Ocean';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> isApiTokenValid(final String token) async {
|
Future<bool> isApiTokenValid(final String token) async {
|
||||||
|
@ -102,32 +102,32 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<ServerVolume?> createVolume() async {
|
Future<ServerVolume?> createVolume() async {
|
||||||
ServerVolume? volume;
|
ServerVolume? volume;
|
||||||
|
|
||||||
final Response dbCreateResponse;
|
final Response createVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
final List<ServerVolume> volumes = await getVolumes();
|
final List<ServerVolume> volumes = await getVolumes();
|
||||||
await Future.delayed(const Duration(seconds: 6));
|
await Future.delayed(const Duration(seconds: 6));
|
||||||
|
|
||||||
dbCreateResponse = await client.post(
|
createVolumeResponse = await client.post(
|
||||||
'/volumes',
|
'/volumes',
|
||||||
data: {
|
data: {
|
||||||
'size_gigabytes': 10,
|
'size_gigabytes': 10,
|
||||||
'name': 'volume${StringGenerators.dbStorageName()}',
|
'name': 'volume${StringGenerators.storageName()}',
|
||||||
'labels': {'labelkey': 'value'},
|
'labels': {'labelkey': 'value'},
|
||||||
'region': region,
|
'region': region,
|
||||||
'filesystem_type': 'ext4',
|
'filesystem_type': 'ext4',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final dbId = dbCreateResponse.data['volume']['id'];
|
final volumeId = createVolumeResponse.data['volume']['id'];
|
||||||
final dbSize = dbCreateResponse.data['volume']['size_gigabytes'];
|
final volumeSize = createVolumeResponse.data['volume']['size_gigabytes'];
|
||||||
final dbName = dbCreateResponse.data['volume']['name'];
|
final volumeName = createVolumeResponse.data['volume']['name'];
|
||||||
volume = ServerVolume(
|
volume = ServerVolume(
|
||||||
id: volumes.length,
|
id: volumes.length,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: null,
|
serverId: null,
|
||||||
linuxDevice: 'scsi-0DO_Volume_$dbName',
|
linuxDevice: '/dev/disk/by-id/scsi-0DO_Volume_$volumeName',
|
||||||
uuid: dbId,
|
uuid: volumeId,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -142,29 +142,29 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<List<ServerVolume>> getVolumes({final String? status}) async {
|
Future<List<ServerVolume>> getVolumes({final String? status}) async {
|
||||||
final List<ServerVolume> volumes = [];
|
final List<ServerVolume> volumes = [];
|
||||||
|
|
||||||
final Response dbGetResponse;
|
final Response getVolumesResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbGetResponse = await client.get(
|
getVolumesResponse = await client.get(
|
||||||
'/volumes',
|
'/volumes',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'status': status,
|
'status': status,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final List<dynamic> rawVolumes = dbGetResponse.data['volumes'];
|
final List<dynamic> rawVolumes = getVolumesResponse.data['volumes'];
|
||||||
int id = 0;
|
int id = 0;
|
||||||
for (final rawVolume in rawVolumes) {
|
for (final rawVolume in rawVolumes) {
|
||||||
final dbId = rawVolume['id'];
|
final volumeId = rawVolume['id'];
|
||||||
final int dbSize = rawVolume['size_gigabytes'] * 1024 * 1024 * 1024;
|
final int volumeSize = rawVolume['size_gigabytes'] * 1024 * 1024 * 1024;
|
||||||
final dbDropletIds = rawVolume['droplet_ids'];
|
final volumeDropletIds = rawVolume['droplet_ids'];
|
||||||
final String dbName = rawVolume['name'];
|
final String volumeName = rawVolume['name'];
|
||||||
final volume = ServerVolume(
|
final volume = ServerVolume(
|
||||||
id: id++,
|
id: id++,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: dbDropletIds.isNotEmpty ? dbDropletIds[0] : null,
|
serverId: volumeDropletIds.isNotEmpty ? volumeDropletIds[0] : null,
|
||||||
linuxDevice: 'scsi-0DO_Volume_$dbName',
|
linuxDevice: 'scsi-0DO_Volume_$volumeName',
|
||||||
uuid: dbId,
|
uuid: volumeId,
|
||||||
);
|
);
|
||||||
volumes.add(volume);
|
volumes.add(volume);
|
||||||
}
|
}
|
||||||
|
@ -178,24 +178,24 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerVolume?> getVolume(final String volumeUuid) async {
|
Future<ServerVolume?> getVolume(final String volumeUuid) async {
|
||||||
ServerVolume? neededVolume;
|
ServerVolume? requestedVolume;
|
||||||
|
|
||||||
final List<ServerVolume> volumes = await getVolumes();
|
final List<ServerVolume> volumes = await getVolumes();
|
||||||
|
|
||||||
for (final volume in volumes) {
|
for (final volume in volumes) {
|
||||||
if (volume.uuid == volumeUuid) {
|
if (volume.uuid == volumeUuid) {
|
||||||
neededVolume = volume;
|
requestedVolume = volume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return neededVolume;
|
return requestedVolume;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deleteVolume(final ServerVolume volume) async {
|
Future<void> deleteVolume(final ServerVolume volume) async {
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
await client.delete('/volumes/$volume.uuid');
|
await client.delete('/volumes/${volume.uuid}');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -210,10 +210,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
) async {
|
) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response attachVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
attachVolumeResponse = await client.post(
|
||||||
'/volumes/actions',
|
'/volumes/actions',
|
||||||
data: {
|
data: {
|
||||||
'type': 'attach',
|
'type': 'attach',
|
||||||
|
@ -222,7 +222,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
'droplet_id': serverId,
|
'droplet_id': serverId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
attachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -236,10 +237,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<bool> detachVolume(final ServerVolume volume) async {
|
Future<bool> detachVolume(final ServerVolume volume) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response detachVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
detachVolumeResponse = await client.post(
|
||||||
'/volumes/actions',
|
'/volumes/actions',
|
||||||
data: {
|
data: {
|
||||||
'type': 'detach',
|
'type': 'detach',
|
||||||
|
@ -248,7 +249,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
'region': region,
|
'region': region,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
detachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -265,10 +267,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
) async {
|
) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response resizeVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
resizeVolumeResponse = await client.post(
|
||||||
'/volumes/actions',
|
'/volumes/actions',
|
||||||
data: {
|
data: {
|
||||||
'type': 'resize',
|
'type': 'resize',
|
||||||
|
@ -277,7 +279,8 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
'region': region,
|
'region': region,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
resizeVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -450,7 +453,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return server.copyWith(startTime: DateTime.now());
|
return server.copyWith(startTime: DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Digital Ocean returns a map of lists of /proc/state values,
|
/// Digital Ocean returns a map of lists of /proc/stat values,
|
||||||
/// so here we are trying to implement average CPU
|
/// so here we are trying to implement average CPU
|
||||||
/// load calculation for each point in time on a given interval.
|
/// load calculation for each point in time on a given interval.
|
||||||
///
|
///
|
||||||
|
@ -500,63 +503,63 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
|
|
||||||
const int step = 15;
|
const int step = 15;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
//try {
|
try {
|
||||||
Response response = await client.get(
|
Response response = await client.get(
|
||||||
'/monitoring/metrics/droplet/bandwidth',
|
'/monitoring/metrics/droplet/bandwidth',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
'host_id': '$serverId',
|
'host_id': '$serverId',
|
||||||
'interface': 'public',
|
'interface': 'public',
|
||||||
'direction': 'inbound',
|
'direction': 'inbound',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
final List inbound = response.data['data']['result'][0]['values'];
|
final List inbound = response.data['data']['result'][0]['values'];
|
||||||
|
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
'/monitoring/metrics/droplet/bandwidth',
|
'/monitoring/metrics/droplet/bandwidth',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
'host_id': '$serverId',
|
'host_id': '$serverId',
|
||||||
'interface': 'public',
|
'interface': 'public',
|
||||||
'direction': 'outbound',
|
'direction': 'outbound',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
final List outbound = response.data['data']['result'][0]['values'];
|
final List outbound = response.data['data']['result'][0]['values'];
|
||||||
|
|
||||||
response = await client.get(
|
response = await client.get(
|
||||||
'/monitoring/metrics/droplet/cpu',
|
'/monitoring/metrics/droplet/cpu',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
'host_id': '$serverId',
|
'host_id': '$serverId',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
metrics = ServerMetrics(
|
metrics = ServerMetrics(
|
||||||
bandwidthIn: inbound
|
bandwidthIn: inbound
|
||||||
.map(
|
.map(
|
||||||
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
|
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
bandwidthOut: outbound
|
bandwidthOut: outbound
|
||||||
.map(
|
.map(
|
||||||
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
|
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
cpu: calculateCpuLoadMetrics(response.data['data']['result']),
|
cpu: calculateCpuLoadMetrics(response.data['data']['result']),
|
||||||
start: start,
|
start: start,
|
||||||
end: end,
|
end: end,
|
||||||
stepsInSecond: step,
|
stepsInSecond: step,
|
||||||
);
|
);
|
||||||
/* } catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
close(client);
|
close(client);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
return metrics;
|
return metrics;
|
||||||
}
|
}
|
||||||
|
@ -604,7 +607,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
ServerMetadataEntity(
|
ServerMetadataEntity(
|
||||||
type: MetadataType.other,
|
type: MetadataType.other,
|
||||||
name: 'server.provider'.tr(),
|
name: 'server.provider'.tr(),
|
||||||
value: appearanceProviderName,
|
value: displayProviderName,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
String get infectProviderName => 'hetzner';
|
String get infectProviderName => 'hetzner';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get appearanceProviderName => 'Hetzner';
|
String get displayProviderName => 'Hetzner';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> isApiTokenValid(final String token) async {
|
Future<bool> isApiTokenValid(final String token) async {
|
||||||
|
@ -102,12 +102,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<Price?> getPricePerGb() async {
|
Future<Price?> getPricePerGb() async {
|
||||||
double? price;
|
double? price;
|
||||||
|
|
||||||
final Response dbGetResponse;
|
final Response pricingResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbGetResponse = await client.get('/pricing');
|
pricingResponse = await client.get('/pricing');
|
||||||
|
|
||||||
final volume = dbGetResponse.data['pricing']['volume'];
|
final volume = pricingResponse.data['pricing']['volume'];
|
||||||
final volumePrice = volume['price_per_gb_month']['gross'];
|
final volumePrice = volume['price_per_gb_month']['gross'];
|
||||||
price = double.parse(volumePrice);
|
price = double.parse(volumePrice);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -128,31 +128,31 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<ServerVolume?> createVolume() async {
|
Future<ServerVolume?> createVolume() async {
|
||||||
ServerVolume? volume;
|
ServerVolume? volume;
|
||||||
|
|
||||||
final Response dbCreateResponse;
|
final Response createVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbCreateResponse = await client.post(
|
createVolumeResponse = await client.post(
|
||||||
'/volumes',
|
'/volumes',
|
||||||
data: {
|
data: {
|
||||||
'size': 10,
|
'size': 10,
|
||||||
'name': StringGenerators.dbStorageName(),
|
'name': StringGenerators.storageName(),
|
||||||
'labels': {'labelkey': 'value'},
|
'labels': {'labelkey': 'value'},
|
||||||
'location': region,
|
'location': region,
|
||||||
'automount': false,
|
'automount': false,
|
||||||
'format': 'ext4'
|
'format': 'ext4'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final dbId = dbCreateResponse.data['volume']['id'];
|
final volumeId = createVolumeResponse.data['volume']['id'];
|
||||||
final dbSize = dbCreateResponse.data['volume']['size'];
|
final volumeSize = createVolumeResponse.data['volume']['size'];
|
||||||
final dbServer = dbCreateResponse.data['volume']['server'];
|
final volumeServer = createVolumeResponse.data['volume']['server'];
|
||||||
final dbName = dbCreateResponse.data['volume']['name'];
|
final volumeName = createVolumeResponse.data['volume']['name'];
|
||||||
final dbDevice = dbCreateResponse.data['volume']['linux_device'];
|
final volumeDevice = createVolumeResponse.data['volume']['linux_device'];
|
||||||
volume = ServerVolume(
|
volume = ServerVolume(
|
||||||
id: dbId,
|
id: volumeId,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: dbServer,
|
serverId: volumeServer,
|
||||||
linuxDevice: dbDevice,
|
linuxDevice: volumeDevice,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -167,28 +167,28 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<List<ServerVolume>> getVolumes({final String? status}) async {
|
Future<List<ServerVolume>> getVolumes({final String? status}) async {
|
||||||
final List<ServerVolume> volumes = [];
|
final List<ServerVolume> volumes = [];
|
||||||
|
|
||||||
final Response dbGetResponse;
|
final Response getVolumesResonse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbGetResponse = await client.get(
|
getVolumesResonse = await client.get(
|
||||||
'/volumes',
|
'/volumes',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'status': status,
|
'status': status,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final List<dynamic> rawVolumes = dbGetResponse.data['volumes'];
|
final List<dynamic> rawVolumes = getVolumesResonse.data['volumes'];
|
||||||
for (final rawVolume in rawVolumes) {
|
for (final rawVolume in rawVolumes) {
|
||||||
final int dbId = rawVolume['id'];
|
final int volumeId = rawVolume['id'];
|
||||||
final int dbSize = rawVolume['size'] * 1024 * 1024 * 1024;
|
final int volumeSize = rawVolume['size'] * 1024 * 1024 * 1024;
|
||||||
final dbServer = rawVolume['server'];
|
final volumeServer = rawVolume['server'];
|
||||||
final String dbName = rawVolume['name'];
|
final String volumeName = rawVolume['name'];
|
||||||
final dbDevice = rawVolume['linux_device'];
|
final volumeDevice = rawVolume['linux_device'];
|
||||||
final volume = ServerVolume(
|
final volume = ServerVolume(
|
||||||
id: dbId,
|
id: volumeId,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: dbServer,
|
serverId: volumeServer,
|
||||||
linuxDevice: dbDevice,
|
linuxDevice: volumeDevice,
|
||||||
);
|
);
|
||||||
volumes.add(volume);
|
volumes.add(volume);
|
||||||
}
|
}
|
||||||
|
@ -206,21 +206,21 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
) async {
|
) async {
|
||||||
ServerVolume? volume;
|
ServerVolume? volume;
|
||||||
|
|
||||||
final Response dbGetResponse;
|
final Response getVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbGetResponse = await client.get('/volumes/$volumeId');
|
getVolumeResponse = await client.get('/volumes/$volumeId');
|
||||||
final int dbId = dbGetResponse.data['volume']['id'];
|
final int responseVolumeId = getVolumeResponse.data['volume']['id'];
|
||||||
final int dbSize = dbGetResponse.data['volume']['size'];
|
final int volumeSize = getVolumeResponse.data['volume']['size'];
|
||||||
final int dbServer = dbGetResponse.data['volume']['server'];
|
final int volumeServer = getVolumeResponse.data['volume']['server'];
|
||||||
final String dbName = dbGetResponse.data['volume']['name'];
|
final String volumeName = getVolumeResponse.data['volume']['name'];
|
||||||
final dbDevice = dbGetResponse.data['volume']['linux_device'];
|
final volumeDevice = getVolumeResponse.data['volume']['linux_device'];
|
||||||
volume = ServerVolume(
|
volume = ServerVolume(
|
||||||
id: dbId,
|
id: responseVolumeId,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: dbServer,
|
serverId: volumeServer,
|
||||||
linuxDevice: dbDevice,
|
linuxDevice: volumeDevice,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -250,17 +250,18 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
) async {
|
) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response attachVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
attachVolumeResponse = await client.post(
|
||||||
'/volumes/${volume.id}/actions/attach',
|
'/volumes/${volume.id}/actions/attach',
|
||||||
data: {
|
data: {
|
||||||
'automount': true,
|
'automount': true,
|
||||||
'server': serverId,
|
'server': serverId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
attachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -274,13 +275,14 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<bool> detachVolume(final ServerVolume volume) async {
|
Future<bool> detachVolume(final ServerVolume volume) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response detachVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
detachVolumeResponse = await client.post(
|
||||||
'/volumes/${volume.id}/actions/detach',
|
'/volumes/${volume.id}/actions/detach',
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
detachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -297,16 +299,17 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
) async {
|
) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response resizeVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
resizeVolumeResponse = await client.post(
|
||||||
'/volumes/${volume.id}/actions/resize',
|
'/volumes/${volume.id}/actions/resize',
|
||||||
data: {
|
data: {
|
||||||
'size': size.gibibyte,
|
'size': size.gibibyte,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
resizeVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -334,7 +337,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
dnsApiToken: dnsApiToken,
|
dnsApiToken: dnsApiToken,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
dataBase: newVolume,
|
volume: newVolume,
|
||||||
serverType: serverType,
|
serverType: serverType,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -345,13 +348,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
required final String dnsApiToken,
|
required final String dnsApiToken,
|
||||||
required final User rootUser,
|
required final User rootUser,
|
||||||
required final String domainName,
|
required final String domainName,
|
||||||
required final ServerVolume dataBase,
|
required final ServerVolume volume,
|
||||||
required final String serverType,
|
required final String serverType,
|
||||||
}) async {
|
}) async {
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
|
||||||
final String dbPassword = StringGenerators.dbPassword();
|
final String dbPassword = StringGenerators.dbPassword();
|
||||||
final int dbId = dataBase.id;
|
final int volumeId = volume.id;
|
||||||
|
|
||||||
final String apiToken = StringGenerators.apiToken();
|
final String apiToken = StringGenerators.apiToken();
|
||||||
final String hostname = getHostnameFromDomain(domainName);
|
final String hostname = getHostnameFromDomain(domainName);
|
||||||
|
@ -373,7 +376,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
'server_type': serverType,
|
'server_type': serverType,
|
||||||
'start_after_create': false,
|
'start_after_create': false,
|
||||||
'image': 'ubuntu-20.04',
|
'image': 'ubuntu-20.04',
|
||||||
'volumes': [dbId],
|
'volumes': [volumeId],
|
||||||
'networks': [],
|
'networks': [],
|
||||||
'user_data': userdataString,
|
'user_data': userdataString,
|
||||||
'labels': {},
|
'labels': {},
|
||||||
|
@ -391,7 +394,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
id: serverCreateResponse.data['server']['id'],
|
id: serverCreateResponse.data['server']['id'],
|
||||||
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
|
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
|
||||||
createTime: DateTime.now(),
|
createTime: DateTime.now(),
|
||||||
volume: dataBase,
|
volume: volume,
|
||||||
apiToken: apiToken,
|
apiToken: apiToken,
|
||||||
provider: ServerProvider.hetzner,
|
provider: ServerProvider.hetzner,
|
||||||
);
|
);
|
||||||
|
@ -407,7 +410,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
await deleteVolume(dataBase);
|
await deleteVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hetznerError != null) {
|
if (hetznerError != null) {
|
||||||
|
@ -621,7 +624,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
ServerMetadataEntity(
|
ServerMetadataEntity(
|
||||||
type: MetadataType.other,
|
type: MetadataType.other,
|
||||||
name: 'server.provider'.tr(),
|
name: 'server.provider'.tr(),
|
||||||
value: appearanceProviderName,
|
value: displayProviderName,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -54,5 +54,5 @@ abstract class ServerProviderApi extends ApiMap {
|
||||||
|
|
||||||
/// Actual provider name to render on information page for user,
|
/// Actual provider name to render on information page for user,
|
||||||
/// for example 'Digital Ocean' for Digital Ocean
|
/// for example 'Digital Ocean' for Digital Ocean
|
||||||
String get appearanceProviderName;
|
String get displayProviderName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
|
||||||
|
|
||||||
part 'server_details.g.dart';
|
part 'server_details.g.dart';
|
||||||
|
|
||||||
|
@ -82,5 +83,16 @@ enum ServerProvider {
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
hetzner,
|
hetzner,
|
||||||
@HiveField(2)
|
@HiveField(2)
|
||||||
digitalOcean,
|
digitalOcean;
|
||||||
|
|
||||||
|
factory ServerProvider.fromGraphQL(final Enum$ServerProvider provider) {
|
||||||
|
switch (provider) {
|
||||||
|
case Enum$ServerProvider.HETZNER:
|
||||||
|
return hetzner;
|
||||||
|
case Enum$ServerProvider.DIGITALOCEAN:
|
||||||
|
return digitalOcean;
|
||||||
|
default:
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,20 @@
|
||||||
enum MetadataType {
|
import 'package:flutter/material.dart';
|
||||||
id,
|
|
||||||
status,
|
|
||||||
cpu,
|
|
||||||
ram,
|
|
||||||
cost,
|
|
||||||
location,
|
|
||||||
|
|
||||||
other,
|
enum MetadataType {
|
||||||
|
id(icon: Icons.numbers_outlined),
|
||||||
|
status(icon: Icons.mode_standby_outlined),
|
||||||
|
cpu(icon: Icons.memory_outlined),
|
||||||
|
ram(icon: Icons.memory_outlined),
|
||||||
|
cost(icon: Icons.payments_outlined),
|
||||||
|
location(icon: Icons.location_on_outlined),
|
||||||
|
|
||||||
|
other(icon: Icons.info_outlined);
|
||||||
|
|
||||||
|
const MetadataType({
|
||||||
|
required this.icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
final IconData icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerMetadataEntity {
|
class ServerMetadataEntity {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
|
||||||
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
||||||
import 'package:selfprivacy/logic/models/job.dart';
|
import 'package:selfprivacy/logic/models/job.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
|
||||||
import 'package:selfprivacy/ui/components/brand_button/segmented_buttons.dart';
|
import 'package:selfprivacy/ui/components/brand_button/segmented_buttons.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
part of 'server_details_screen.dart';
|
part of 'server_details_screen.dart';
|
||||||
|
|
||||||
class _TextDetails extends StatelessWidget {
|
class _TextDetails extends StatelessWidget {
|
||||||
final Map<MetadataType, IconData> metadataToIcon = const {
|
|
||||||
MetadataType.id: Icons.numbers_outlined,
|
|
||||||
MetadataType.status: Icons.mode_standby_outlined,
|
|
||||||
MetadataType.cpu: Icons.memory_outlined,
|
|
||||||
MetadataType.ram: Icons.memory_outlined,
|
|
||||||
MetadataType.cost: Icons.euro_outlined,
|
|
||||||
MetadataType.location: Icons.location_on_outlined,
|
|
||||||
MetadataType.other: Icons.info_outlined,
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final details = context.watch<ServerDetailsCubit>().state;
|
final details = context.watch<ServerDetailsCubit>().state;
|
||||||
|
@ -36,7 +26,7 @@ class _TextDetails extends StatelessWidget {
|
||||||
...details.metadata
|
...details.metadata
|
||||||
.map(
|
.map(
|
||||||
(final metadata) => ListTileOnSurfaceVariant(
|
(final metadata) => ListTileOnSurfaceVariant(
|
||||||
leadingIcon: metadataToIcon[metadata.type],
|
leadingIcon: metadata.type.icon,
|
||||||
title: metadata.name,
|
title: metadata.name,
|
||||||
subtitle: metadata.value,
|
subtitle: metadata.value,
|
||||||
),
|
),
|
||||||
|
|
|
@ -101,7 +101,7 @@ class StringGenerators {
|
||||||
hasSymbols: true,
|
hasSymbols: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
static StringGeneratorFunction dbStorageName = () => getRandomString(
|
static StringGeneratorFunction storageName = () => getRandomString(
|
||||||
6,
|
6,
|
||||||
hasLowercaseLetters: true,
|
hasLowercaseLetters: true,
|
||||||
hasUppercaseLetters: false,
|
hasUppercaseLetters: false,
|
||||||
|
|
Loading…
Reference in New Issue