mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-23 09:16:54 +00:00
refactor: Server location is no longer a part of server provider controller.
This commit is contained in:
parent
55616c3e69
commit
e13b324afa
|
@ -10,7 +10,6 @@ import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
class DigitalOceanApi extends RestApiMap {
|
class DigitalOceanApi extends RestApiMap {
|
||||||
DigitalOceanApi({
|
DigitalOceanApi({
|
||||||
required this.region,
|
|
||||||
this.token = '',
|
this.token = '',
|
||||||
this.hasLogger = true,
|
this.hasLogger = true,
|
||||||
this.isWithToken = true,
|
this.isWithToken = true,
|
||||||
|
@ -21,7 +20,6 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
@override
|
@override
|
||||||
bool isWithToken;
|
bool isWithToken;
|
||||||
|
|
||||||
final String? region;
|
|
||||||
final String token;
|
final String token;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -79,6 +77,7 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
required final String hostName,
|
required final String hostName,
|
||||||
required final String serverType,
|
required final String serverType,
|
||||||
required final String? customSshKey,
|
required final String? customSshKey,
|
||||||
|
required final String region,
|
||||||
}) async {
|
}) async {
|
||||||
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
||||||
|
|
||||||
|
@ -98,7 +97,7 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
"HOSTNAME=$hostName LUSER='${rootUser.login}' NIX_VERSION=2.18.1 PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' "
|
"HOSTNAME=$hostName LUSER='${rootUser.login}' NIX_VERSION=2.18.1 PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' "
|
||||||
"${customSshKey != null ? "SSH_AUTHORIZED_KEY='$customSshKey'" : ""} "
|
"${customSshKey != null ? "SSH_AUTHORIZED_KEY='$customSshKey'" : ""} "
|
||||||
'bash 2>&1 | tee /root/nixos-infect.log',
|
'bash 2>&1 | tee /root/nixos-infect.log',
|
||||||
'region': region!,
|
'region': region,
|
||||||
};
|
};
|
||||||
print('Decoded data: $data');
|
print('Decoded data: $data');
|
||||||
|
|
||||||
|
@ -326,7 +325,10 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<GenericResult<DigitalOceanVolume?>> createVolume(final int gb) async {
|
Future<GenericResult<DigitalOceanVolume?>> createVolume({
|
||||||
|
required final int gb,
|
||||||
|
required final String region,
|
||||||
|
}) async {
|
||||||
DigitalOceanVolume? volume;
|
DigitalOceanVolume? volume;
|
||||||
Response? createVolumeResponse;
|
Response? createVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
@ -363,10 +365,11 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<GenericResult<bool>> attachVolume(
|
Future<GenericResult<bool>> attachVolume({
|
||||||
final String name,
|
required final String name,
|
||||||
final int serverId,
|
required final int serverId,
|
||||||
) async {
|
required final String region,
|
||||||
|
}) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
Response? attachVolumeResponse;
|
Response? attachVolumeResponse;
|
||||||
|
@ -402,10 +405,11 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<GenericResult<bool>> detachVolume(
|
Future<GenericResult<bool>> detachVolume({
|
||||||
final String name,
|
required final String name,
|
||||||
final int serverId,
|
required final int serverId,
|
||||||
) async {
|
required final String region,
|
||||||
|
}) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response detachVolumeResponse;
|
final Response detachVolumeResponse;
|
||||||
|
@ -460,10 +464,11 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<GenericResult<bool>> resizeVolume(
|
Future<GenericResult<bool>> resizeVolume({
|
||||||
final String uuid,
|
required final String uuid,
|
||||||
final int gb,
|
required final int gb,
|
||||||
) async {
|
required final String region,
|
||||||
|
}) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response resizeVolumeResponse;
|
final Response resizeVolumeResponse;
|
||||||
|
|
|
@ -11,7 +11,6 @@ import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
class HetznerApi extends RestApiMap {
|
class HetznerApi extends RestApiMap {
|
||||||
HetznerApi({
|
HetznerApi({
|
||||||
this.region,
|
|
||||||
this.token = '',
|
this.token = '',
|
||||||
this.hasLogger = true,
|
this.hasLogger = true,
|
||||||
this.isWithToken = true,
|
this.isWithToken = true,
|
||||||
|
@ -22,7 +21,6 @@ class HetznerApi extends RestApiMap {
|
||||||
@override
|
@override
|
||||||
bool isWithToken;
|
bool isWithToken;
|
||||||
|
|
||||||
final String? region;
|
|
||||||
final String token;
|
final String token;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -85,6 +83,7 @@ class HetznerApi extends RestApiMap {
|
||||||
required final int volumeId,
|
required final int volumeId,
|
||||||
required final String serverType,
|
required final String serverType,
|
||||||
required final String? customSshKey,
|
required final String? customSshKey,
|
||||||
|
required final String region,
|
||||||
}) async {
|
}) async {
|
||||||
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
||||||
Response? serverCreateResponse;
|
Response? serverCreateResponse;
|
||||||
|
@ -111,7 +110,7 @@ class HetznerApi extends RestApiMap {
|
||||||
'bash 2>&1 | tee /root/nixos-infect.log',
|
'bash 2>&1 | tee /root/nixos-infect.log',
|
||||||
'labels': {},
|
'labels': {},
|
||||||
'automount': true,
|
'automount': true,
|
||||||
'location': region!,
|
'location': region,
|
||||||
};
|
};
|
||||||
print('Decoded data: $data');
|
print('Decoded data: $data');
|
||||||
|
|
||||||
|
@ -326,7 +325,9 @@ class HetznerApi extends RestApiMap {
|
||||||
return GenericResult(success: true, data: null);
|
return GenericResult(success: true, data: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<GenericResult<HetznerPricing?>> getPricing() async {
|
Future<GenericResult<HetznerPricing?>> getPricing({
|
||||||
|
required final String region,
|
||||||
|
}) async {
|
||||||
HetznerPricing? pricing;
|
HetznerPricing? pricing;
|
||||||
|
|
||||||
final Response pricingResponse;
|
final Response pricingResponse;
|
||||||
|
@ -341,14 +342,14 @@ class HetznerApi extends RestApiMap {
|
||||||
for (final primaryIp in primaryIps) {
|
for (final primaryIp in primaryIps) {
|
||||||
if (primaryIp['type'] == 'ipv4') {
|
if (primaryIp['type'] == 'ipv4') {
|
||||||
for (final primaryIpPrice in primaryIp['prices']) {
|
for (final primaryIpPrice in primaryIp['prices']) {
|
||||||
if (primaryIpPrice['location'] == region!) {
|
if (primaryIpPrice['location'] == region) {
|
||||||
ipPrice = primaryIpPrice['price_monthly']['gross'];
|
ipPrice = primaryIpPrice['price_monthly']['gross'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pricing = HetznerPricing(
|
pricing = HetznerPricing(
|
||||||
region!,
|
region,
|
||||||
double.parse(volumePrice),
|
double.parse(volumePrice),
|
||||||
double.parse(ipPrice!),
|
double.parse(ipPrice!),
|
||||||
);
|
);
|
||||||
|
@ -395,7 +396,10 @@ class HetznerApi extends RestApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<GenericResult<HetznerVolume?>> createVolume(final int gb) async {
|
Future<GenericResult<HetznerVolume?>> createVolume({
|
||||||
|
required final int gb,
|
||||||
|
required final String region,
|
||||||
|
}) async {
|
||||||
Response? createVolumeResponse;
|
Response? createVolumeResponse;
|
||||||
HetznerVolume? volume;
|
HetznerVolume? volume;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
|
|
@ -201,7 +201,7 @@ class TokensBloc extends Bloc<TokensEvent, TokensState> {
|
||||||
),
|
),
|
||||||
apiToken: event.server.hostingDetails.apiToken,
|
apiToken: event.server.hostingDetails.apiToken,
|
||||||
provider: event.serverProviderCredential.provider,
|
provider: event.serverProviderCredential.provider,
|
||||||
serverLocation: event.server.hostingDetails.serverLocation,
|
serverLocation: event.providerServer.location,
|
||||||
serverType: event.server.hostingDetails.serverType,
|
serverType: event.server.hostingDetails.serverType,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc_concurrency/bloc_concurrency.dart';
|
import 'package:bloc_concurrency/bloc_concurrency.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -88,14 +89,21 @@ class VolumesBloc extends Bloc<VolumesEvent, VolumesState> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Price? price;
|
Price? price;
|
||||||
final pricingResult =
|
final location = state.location;
|
||||||
await ProvidersController.currentServerProvider!.getAdditionalPricing();
|
if (location != null) {
|
||||||
|
final pricingResult = await ProvidersController.currentServerProvider!
|
||||||
|
.getAdditionalPricing(location);
|
||||||
if (pricingResult.data == null || !pricingResult.success) {
|
if (pricingResult.data == null || !pricingResult.success) {
|
||||||
getIt<NavigationService>().showSnackBar('server.pricing_error'.tr());
|
getIt<NavigationService>().showSnackBar('server.pricing_error'.tr());
|
||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
price = pricingResult.data!.perVolumeGb;
|
price = pricingResult.data!.perVolumeGb;
|
||||||
return price;
|
return price;
|
||||||
|
} else {
|
||||||
|
await Future.delayed(Duration.zero);
|
||||||
|
getIt<NavigationService>().showSnackBar('server.pricing_error'.tr());
|
||||||
|
return price;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadState(
|
Future<void> _loadState(
|
||||||
|
|
|
@ -17,6 +17,11 @@ sealed class VolumesState extends Equatable {
|
||||||
orElse: () => DiskVolume(),
|
orElse: () => DiskVolume(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
String? get location => volumes
|
||||||
|
.firstWhereOrNull((final volume) => volume.isResizable)
|
||||||
|
?.providerVolume
|
||||||
|
?.location;
|
||||||
|
|
||||||
bool get isProviderVolumesLoaded => providerVolumes.isNotEmpty;
|
bool get isProviderVolumesLoaded => providerVolumes.isNotEmpty;
|
||||||
|
|
||||||
VolumesState copyWith({
|
VolumesState copyWith({
|
||||||
|
|
|
@ -45,17 +45,26 @@ class ServerDetailsCubit
|
||||||
Future<List<ServerMetadataEntity>> get _metadata async {
|
Future<List<ServerMetadataEntity>> get _metadata async {
|
||||||
final List<ServerMetadataEntity> data = [];
|
final List<ServerMetadataEntity> data = [];
|
||||||
|
|
||||||
|
final Server? server = getIt<ResourcesModel>().servers.firstOrNull;
|
||||||
|
|
||||||
|
if (server == null) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
final serverProviderApi = ProvidersController.currentServerProvider;
|
final serverProviderApi = ProvidersController.currentServerProvider;
|
||||||
final dnsProviderApi = ProvidersController.currentDnsProvider;
|
final dnsProviderApi = ProvidersController.currentDnsProvider;
|
||||||
if (serverProviderApi?.isAuthorized ?? false) {
|
if (server.hostingDetails.serverLocation != null &&
|
||||||
final serverId = getIt<ResourcesModel>().serverDetails?.id ?? 0;
|
(serverProviderApi?.isAuthorized ?? false)) {
|
||||||
final metadataResult = await serverProviderApi?.getMetadata(serverId);
|
final serverId = server.hostingDetails.id;
|
||||||
|
final metadataResult = await serverProviderApi?.getMetadata(
|
||||||
|
serverId,
|
||||||
|
server.hostingDetails.serverLocation!,
|
||||||
|
);
|
||||||
|
|
||||||
data.addAll(metadataResult?.data ?? []);
|
data.addAll(metadataResult?.data ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverProviderApi == null || !serverProviderApi.isAuthorized) {
|
if (serverProviderApi == null || !serverProviderApi.isAuthorized) {
|
||||||
final Server server = getIt<ResourcesModel>().servers.first;
|
|
||||||
data.add(
|
data.add(
|
||||||
ServerMetadataEntity(
|
ServerMetadataEntity(
|
||||||
type: MetadataType.other,
|
type: MetadataType.other,
|
||||||
|
@ -74,7 +83,6 @@ class ServerDetailsCubit
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final Server server = getIt<ResourcesModel>().servers.first;
|
|
||||||
data.add(
|
data.add(
|
||||||
ServerMetadataEntity(
|
ServerMetadataEntity(
|
||||||
trId: 'server.dns_provider',
|
trId: 'server.dns_provider',
|
||||||
|
|
|
@ -168,10 +168,12 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
return apiResult.data;
|
return apiResult.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<AdditionalPricing?> fetchAvailableAdditionalPricing() async {
|
Future<AdditionalPricing?> fetchAvailableAdditionalPricing(
|
||||||
|
final ServerProviderLocation location,
|
||||||
|
) async {
|
||||||
AdditionalPricing? prices;
|
AdditionalPricing? prices;
|
||||||
final pricingResult =
|
final pricingResult = await ProvidersController.currentServerProvider!
|
||||||
await ProvidersController.currentServerProvider!.getAdditionalPricing();
|
.getAdditionalPricing(location.identifier);
|
||||||
if (pricingResult.data == null || !pricingResult.success) {
|
if (pricingResult.data == null || !pricingResult.success) {
|
||||||
getIt<NavigationService>().showSnackBar('server.pricing_error'.tr());
|
getIt<NavigationService>().showSnackBar('server.pricing_error'.tr());
|
||||||
return prices;
|
return prices;
|
||||||
|
@ -202,8 +204,11 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setLocationIdentifier(final String locationId) async {
|
Future<void> setLocationIdentifier(final String locationId) async {
|
||||||
await ProvidersController.currentServerProvider!
|
emit(
|
||||||
.trySetServerLocation(locationId);
|
(state as ServerInstallationNotFinished).copyWith(
|
||||||
|
serverLocation: locationId,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setServerType(final ServerType serverType) async {
|
void setServerType(final ServerType serverType) async {
|
||||||
|
@ -212,6 +217,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
emit(
|
emit(
|
||||||
(state as ServerInstallationNotFinished).copyWith(
|
(state as ServerInstallationNotFinished).copyWith(
|
||||||
serverTypeIdentificator: serverType.identifier,
|
serverTypeIdentificator: serverType.identifier,
|
||||||
|
serverLocation: serverType.location.identifier,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -335,6 +341,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
successCallback: onCreateServerSuccess,
|
successCallback: onCreateServerSuccess,
|
||||||
storageSize: initialStorage,
|
storageSize: initialStorage,
|
||||||
customSshKey: (state as ServerInstallationNotFinished).customSshKey,
|
customSshKey: (state as ServerInstallationNotFinished).customSshKey,
|
||||||
|
location: state.serverLocation!,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result =
|
final result =
|
||||||
|
@ -794,12 +801,14 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
),
|
),
|
||||||
apiToken: dataState.serverDetails!.apiToken,
|
apiToken: dataState.serverDetails!.apiToken,
|
||||||
provider: dataState.serverDetails!.provider,
|
provider: dataState.serverDetails!.provider,
|
||||||
|
serverLocation: server.location,
|
||||||
);
|
);
|
||||||
await repository.saveDomain(serverDomain);
|
await repository.saveDomain(serverDomain);
|
||||||
await repository.saveServerDetails(serverDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
emit(
|
emit(
|
||||||
dataState.copyWith(
|
dataState.copyWith(
|
||||||
serverDetails: serverDetails,
|
serverDetails: serverDetails,
|
||||||
|
serverLocation: server.location,
|
||||||
currentStep: RecoveryStep.dnsProviderToken,
|
currentStep: RecoveryStep.dnsProviderToken,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -858,8 +867,6 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
?.getServerType(state.serverDetails!.id);
|
?.getServerType(state.serverDetails!.id);
|
||||||
if (serverType != null) {
|
if (serverType != null) {
|
||||||
await repository.saveServerType(serverType.data!);
|
await repository.saveServerType(serverType.data!);
|
||||||
await ProvidersController.currentServerProvider!
|
|
||||||
.trySetServerLocation(serverType.data!.location.identifier);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
serverType = null;
|
serverType = null;
|
||||||
|
|
|
@ -64,7 +64,6 @@ class ServerInstallationRepository {
|
||||||
ServerProviderSettings(
|
ServerProviderSettings(
|
||||||
provider: serverProvider ?? serverDetails!.provider,
|
provider: serverProvider ?? serverDetails!.provider,
|
||||||
isAuthorized: providerApiToken != null,
|
isAuthorized: providerApiToken != null,
|
||||||
location: location,
|
|
||||||
token: providerApiToken,
|
token: providerApiToken,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -100,6 +99,7 @@ class ServerInstallationRepository {
|
||||||
serverDomain: serverDomain!,
|
serverDomain: serverDomain!,
|
||||||
backblazeCredential: backblazeCredential!,
|
backblazeCredential: backblazeCredential!,
|
||||||
serverDetails: serverDetails!,
|
serverDetails: serverDetails!,
|
||||||
|
serverLocation: location,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,6 @@ class ServerInstallationRepository {
|
||||||
ServerProviderSettings(
|
ServerProviderSettings(
|
||||||
provider: wizardData.serverProviderType!,
|
provider: wizardData.serverProviderType!,
|
||||||
isAuthorized: wizardData.serverProviderKey != null,
|
isAuthorized: wizardData.serverProviderKey != null,
|
||||||
location: wizardData.serverLocation,
|
|
||||||
token: wizardData.serverProviderKey,
|
token: wizardData.serverProviderKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -134,6 +133,7 @@ class ServerInstallationRepository {
|
||||||
dnsApiToken: wizardData.dnsProviderKey,
|
dnsApiToken: wizardData.dnsProviderKey,
|
||||||
serverDomain: wizardData.serverDomain,
|
serverDomain: wizardData.serverDomain,
|
||||||
serverTypeIdentificator: wizardData.serverTypeIdentifier,
|
serverTypeIdentificator: wizardData.serverTypeIdentifier,
|
||||||
|
serverLocation: wizardData.serverLocation,
|
||||||
backblazeCredential: wizardData.backupsCredential,
|
backblazeCredential: wizardData.backupsCredential,
|
||||||
serverDetails: wizardData.serverDetails,
|
serverDetails: wizardData.serverDetails,
|
||||||
currentStep: _getCurrentRecoveryStep(
|
currentStep: _getCurrentRecoveryStep(
|
||||||
|
|
|
@ -13,6 +13,7 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
required this.isServerResetedFirstTime,
|
required this.isServerResetedFirstTime,
|
||||||
required this.isServerResetedSecondTime,
|
required this.isServerResetedSecondTime,
|
||||||
required this.installationDialoguePopUp,
|
required this.installationDialoguePopUp,
|
||||||
|
required this.serverLocation,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -27,11 +28,13 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
isServerStarted,
|
isServerStarted,
|
||||||
isServerResetedFirstTime,
|
isServerResetedFirstTime,
|
||||||
installationDialoguePopUp,
|
installationDialoguePopUp,
|
||||||
|
serverLocation,
|
||||||
];
|
];
|
||||||
|
|
||||||
final String? providerApiToken;
|
final String? providerApiToken;
|
||||||
final String? dnsApiToken;
|
final String? dnsApiToken;
|
||||||
final String? serverTypeIdentificator;
|
final String? serverTypeIdentificator;
|
||||||
|
final String? serverLocation;
|
||||||
final BackupsCredential? backblazeCredential;
|
final BackupsCredential? backblazeCredential;
|
||||||
final ServerDomain? serverDomain;
|
final ServerDomain? serverDomain;
|
||||||
final User? rootUser;
|
final User? rootUser;
|
||||||
|
@ -91,6 +94,7 @@ class TimerState extends ServerInstallationNotFinished {
|
||||||
}) : super(
|
}) : super(
|
||||||
providerApiToken: dataState.providerApiToken,
|
providerApiToken: dataState.providerApiToken,
|
||||||
serverTypeIdentificator: dataState.serverTypeIdentificator,
|
serverTypeIdentificator: dataState.serverTypeIdentificator,
|
||||||
|
serverLocation: dataState.serverLocation,
|
||||||
dnsApiToken: dataState.dnsApiToken,
|
dnsApiToken: dataState.dnsApiToken,
|
||||||
backblazeCredential: dataState.backblazeCredential,
|
backblazeCredential: dataState.backblazeCredential,
|
||||||
serverDomain: dataState.serverDomain,
|
serverDomain: dataState.serverDomain,
|
||||||
|
@ -139,6 +143,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
required this.dnsMatches,
|
required this.dnsMatches,
|
||||||
required this.customSshKey,
|
required this.customSshKey,
|
||||||
super.providerApiToken,
|
super.providerApiToken,
|
||||||
|
super.serverLocation,
|
||||||
super.serverTypeIdentificator,
|
super.serverTypeIdentificator,
|
||||||
super.dnsApiToken,
|
super.dnsApiToken,
|
||||||
super.backblazeCredential,
|
super.backblazeCredential,
|
||||||
|
@ -155,6 +160,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
dnsApiToken: data.dnsProviderKey,
|
dnsApiToken: data.dnsProviderKey,
|
||||||
serverDomain: data.serverDomain,
|
serverDomain: data.serverDomain,
|
||||||
serverTypeIdentificator: data.serverTypeIdentifier,
|
serverTypeIdentificator: data.serverTypeIdentifier,
|
||||||
|
serverLocation: data.serverLocation,
|
||||||
backblazeCredential: data.backupsCredential,
|
backblazeCredential: data.backupsCredential,
|
||||||
serverDetails: data.serverDetails,
|
serverDetails: data.serverDetails,
|
||||||
rootUser: data.rootUser,
|
rootUser: data.rootUser,
|
||||||
|
@ -175,6 +181,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
providerApiToken,
|
providerApiToken,
|
||||||
serverTypeIdentificator,
|
serverTypeIdentificator,
|
||||||
|
serverLocation,
|
||||||
dnsApiToken,
|
dnsApiToken,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
serverDomain,
|
serverDomain,
|
||||||
|
@ -190,6 +197,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationNotFinished copyWith({
|
ServerInstallationNotFinished copyWith({
|
||||||
final String? providerApiToken,
|
final String? providerApiToken,
|
||||||
|
final String? serverLocation,
|
||||||
final String? serverTypeIdentificator,
|
final String? serverTypeIdentificator,
|
||||||
final String? dnsApiToken,
|
final String? dnsApiToken,
|
||||||
final BackupsCredential? backblazeCredential,
|
final BackupsCredential? backblazeCredential,
|
||||||
|
@ -206,6 +214,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
}) =>
|
}) =>
|
||||||
ServerInstallationNotFinished(
|
ServerInstallationNotFinished(
|
||||||
providerApiToken: providerApiToken ?? this.providerApiToken,
|
providerApiToken: providerApiToken ?? this.providerApiToken,
|
||||||
|
serverLocation: serverLocation ?? this.serverLocation,
|
||||||
serverTypeIdentificator:
|
serverTypeIdentificator:
|
||||||
serverTypeIdentificator ?? this.serverTypeIdentificator,
|
serverTypeIdentificator ?? this.serverTypeIdentificator,
|
||||||
dnsApiToken: dnsApiToken ?? this.dnsApiToken,
|
dnsApiToken: dnsApiToken ?? this.dnsApiToken,
|
||||||
|
@ -227,6 +236,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationFinished finish() => ServerInstallationFinished(
|
ServerInstallationFinished finish() => ServerInstallationFinished(
|
||||||
providerApiToken: providerApiToken,
|
providerApiToken: providerApiToken,
|
||||||
|
serverLocation: serverLocation,
|
||||||
serverTypeIdentificator: serverTypeIdentificator,
|
serverTypeIdentificator: serverTypeIdentificator,
|
||||||
dnsApiToken: dnsApiToken!,
|
dnsApiToken: dnsApiToken!,
|
||||||
backblazeCredential: backblazeCredential!,
|
backblazeCredential: backblazeCredential!,
|
||||||
|
@ -239,6 +249,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished {
|
||||||
const ServerInstallationEmpty()
|
const ServerInstallationEmpty()
|
||||||
: super(
|
: super(
|
||||||
providerApiToken: null,
|
providerApiToken: null,
|
||||||
|
serverLocation: null,
|
||||||
serverTypeIdentificator: null,
|
serverTypeIdentificator: null,
|
||||||
dnsApiToken: null,
|
dnsApiToken: null,
|
||||||
backblazeCredential: null,
|
backblazeCredential: null,
|
||||||
|
@ -263,6 +274,7 @@ class ServerInstallationFinished extends ServerInstallationState {
|
||||||
required ServerHostingDetails super.serverDetails,
|
required ServerHostingDetails super.serverDetails,
|
||||||
super.providerApiToken,
|
super.providerApiToken,
|
||||||
super.serverTypeIdentificator,
|
super.serverTypeIdentificator,
|
||||||
|
super.serverLocation,
|
||||||
}) : super(
|
}) : super(
|
||||||
rootUser: null,
|
rootUser: null,
|
||||||
isServerStarted: true,
|
isServerStarted: true,
|
||||||
|
@ -275,6 +287,7 @@ class ServerInstallationFinished extends ServerInstallationState {
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
providerApiToken,
|
providerApiToken,
|
||||||
serverTypeIdentificator,
|
serverTypeIdentificator,
|
||||||
|
serverLocation,
|
||||||
dnsApiToken,
|
dnsApiToken,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
serverDomain,
|
serverDomain,
|
||||||
|
@ -315,6 +328,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
required this.recoveryCapabilities,
|
required this.recoveryCapabilities,
|
||||||
super.providerApiToken,
|
super.providerApiToken,
|
||||||
super.serverTypeIdentificator,
|
super.serverTypeIdentificator,
|
||||||
|
super.serverLocation,
|
||||||
super.dnsApiToken,
|
super.dnsApiToken,
|
||||||
super.backblazeCredential,
|
super.backblazeCredential,
|
||||||
super.serverDomain,
|
super.serverDomain,
|
||||||
|
@ -333,6 +347,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
providerApiToken,
|
providerApiToken,
|
||||||
serverTypeIdentificator,
|
serverTypeIdentificator,
|
||||||
|
serverLocation,
|
||||||
dnsApiToken,
|
dnsApiToken,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
serverDomain,
|
serverDomain,
|
||||||
|
@ -346,6 +361,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationRecovery copyWith({
|
ServerInstallationRecovery copyWith({
|
||||||
final String? providerApiToken,
|
final String? providerApiToken,
|
||||||
|
final String? serverLocation,
|
||||||
final String? serverTypeIdentificator,
|
final String? serverTypeIdentificator,
|
||||||
final String? dnsApiToken,
|
final String? dnsApiToken,
|
||||||
final BackupsCredential? backblazeCredential,
|
final BackupsCredential? backblazeCredential,
|
||||||
|
@ -356,6 +372,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
}) =>
|
}) =>
|
||||||
ServerInstallationRecovery(
|
ServerInstallationRecovery(
|
||||||
providerApiToken: providerApiToken ?? this.providerApiToken,
|
providerApiToken: providerApiToken ?? this.providerApiToken,
|
||||||
|
serverLocation: serverLocation ?? this.serverLocation,
|
||||||
serverTypeIdentificator:
|
serverTypeIdentificator:
|
||||||
serverTypeIdentificator ?? this.serverTypeIdentificator,
|
serverTypeIdentificator ?? this.serverTypeIdentificator,
|
||||||
dnsApiToken: dnsApiToken ?? this.dnsApiToken,
|
dnsApiToken: dnsApiToken ?? this.dnsApiToken,
|
||||||
|
@ -368,6 +385,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationFinished finish() => ServerInstallationFinished(
|
ServerInstallationFinished finish() => ServerInstallationFinished(
|
||||||
providerApiToken: providerApiToken,
|
providerApiToken: providerApiToken,
|
||||||
|
serverLocation: serverLocation,
|
||||||
serverTypeIdentificator: serverTypeIdentificator,
|
serverTypeIdentificator: serverTypeIdentificator,
|
||||||
dnsApiToken: dnsApiToken!,
|
dnsApiToken: dnsApiToken!,
|
||||||
backblazeCredential: backblazeCredential!,
|
backblazeCredential: backblazeCredential!,
|
||||||
|
|
|
@ -76,6 +76,7 @@ class ServerProviderVolume {
|
||||||
required this.serverId,
|
required this.serverId,
|
||||||
required this.linuxDevice,
|
required this.linuxDevice,
|
||||||
this.uuid,
|
this.uuid,
|
||||||
|
this.location,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
|
@ -90,6 +91,8 @@ class ServerProviderVolume {
|
||||||
String? linuxDevice;
|
String? linuxDevice;
|
||||||
@HiveField(6, defaultValue: null)
|
@HiveField(6, defaultValue: null)
|
||||||
String? uuid;
|
String? uuid;
|
||||||
|
@HiveField(7, defaultValue: null)
|
||||||
|
String? location;
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiveType(typeId: 101)
|
@HiveType(typeId: 101)
|
||||||
|
|
|
@ -83,13 +83,14 @@ class ServerProviderVolumeAdapter extends TypeAdapter<ServerProviderVolume> {
|
||||||
serverId: fields[4] as int?,
|
serverId: fields[4] as int?,
|
||||||
linuxDevice: fields[5] as String?,
|
linuxDevice: fields[5] as String?,
|
||||||
uuid: fields[6] as String?,
|
uuid: fields[6] as String?,
|
||||||
|
location: fields[7] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, ServerProviderVolume obj) {
|
void write(BinaryWriter writer, ServerProviderVolume obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(6)
|
..writeByte(7)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
..write(obj.id)
|
..write(obj.id)
|
||||||
..writeByte(2)
|
..writeByte(2)
|
||||||
|
@ -101,7 +102,9 @@ class ServerProviderVolumeAdapter extends TypeAdapter<ServerProviderVolume> {
|
||||||
..writeByte(5)
|
..writeByte(5)
|
||||||
..write(obj.linuxDevice)
|
..write(obj.linuxDevice)
|
||||||
..writeByte(6)
|
..writeByte(6)
|
||||||
..write(obj.uuid);
|
..write(obj.uuid)
|
||||||
|
..writeByte(7)
|
||||||
|
..write(obj.location);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -9,6 +9,7 @@ class DigitalOceanVolume {
|
||||||
this.name,
|
this.name,
|
||||||
this.sizeGigabytes,
|
this.sizeGigabytes,
|
||||||
this.dropletIds,
|
this.dropletIds,
|
||||||
|
this.region,
|
||||||
);
|
);
|
||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
@ -20,6 +21,8 @@ class DigitalOceanVolume {
|
||||||
@JsonKey(name: 'size_gigabytes')
|
@JsonKey(name: 'size_gigabytes')
|
||||||
final int sizeGigabytes;
|
final int sizeGigabytes;
|
||||||
|
|
||||||
|
final DigitalOceanLocation region;
|
||||||
|
|
||||||
static DigitalOceanVolume fromJson(final Map<String, dynamic> json) =>
|
static DigitalOceanVolume fromJson(final Map<String, dynamic> json) =>
|
||||||
_$DigitalOceanVolumeFromJson(json);
|
_$DigitalOceanVolumeFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ DigitalOceanVolume _$DigitalOceanVolumeFromJson(Map<String, dynamic> json) =>
|
||||||
(json['droplet_ids'] as List<dynamic>?)
|
(json['droplet_ids'] as List<dynamic>?)
|
||||||
?.map((e) => (e as num).toInt())
|
?.map((e) => (e as num).toInt())
|
||||||
.toList(),
|
.toList(),
|
||||||
|
DigitalOceanLocation.fromJson(json['region'] as Map<String, dynamic>),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) =>
|
Map<String, dynamic> _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) =>
|
||||||
|
@ -22,6 +23,7 @@ Map<String, dynamic> _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) =>
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'droplet_ids': instance.dropletIds,
|
'droplet_ids': instance.dropletIds,
|
||||||
'size_gigabytes': instance.sizeGigabytes,
|
'size_gigabytes': instance.sizeGigabytes,
|
||||||
|
'region': instance.region,
|
||||||
};
|
};
|
||||||
|
|
||||||
DigitalOceanLocation _$DigitalOceanLocationFromJson(
|
DigitalOceanLocation _$DigitalOceanLocationFromJson(
|
||||||
|
|
|
@ -127,6 +127,9 @@ class HetznerLocation {
|
||||||
this.zone,
|
this.zone,
|
||||||
this.name,
|
this.name,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
HetznerLocation.empty() : this('', '', '', '', '');
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final String country;
|
final String country;
|
||||||
final String city;
|
final String city;
|
||||||
|
@ -192,6 +195,7 @@ class HetznerVolume {
|
||||||
this.serverId,
|
this.serverId,
|
||||||
this.name,
|
this.name,
|
||||||
this.linuxDevice,
|
this.linuxDevice,
|
||||||
|
this.location,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// ID of the Resource
|
/// ID of the Resource
|
||||||
|
@ -210,6 +214,8 @@ class HetznerVolume {
|
||||||
@JsonKey(name: 'linux_device')
|
@JsonKey(name: 'linux_device')
|
||||||
final String? linuxDevice;
|
final String? linuxDevice;
|
||||||
|
|
||||||
|
final HetznerLocation location;
|
||||||
|
|
||||||
static HetznerVolume fromJson(final Map<String, dynamic> json) =>
|
static HetznerVolume fromJson(final Map<String, dynamic> json) =>
|
||||||
_$HetznerVolumeFromJson(json);
|
_$HetznerVolumeFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,6 +139,7 @@ HetznerVolume _$HetznerVolumeFromJson(Map<String, dynamic> json) =>
|
||||||
(json['serverId'] as num?)?.toInt(),
|
(json['serverId'] as num?)?.toInt(),
|
||||||
json['name'] as String,
|
json['name'] as String,
|
||||||
json['linux_device'] as String?,
|
json['linux_device'] as String?,
|
||||||
|
HetznerLocation.fromJson(json['location'] as Map<String, dynamic>),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$HetznerVolumeToJson(HetznerVolume instance) =>
|
Map<String, dynamic> _$HetznerVolumeToJson(HetznerVolume instance) =>
|
||||||
|
@ -148,4 +149,5 @@ Map<String, dynamic> _$HetznerVolumeToJson(HetznerVolume instance) =>
|
||||||
'serverId': instance.serverId,
|
'serverId': instance.serverId,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'linux_device': instance.linuxDevice,
|
'linux_device': instance.linuxDevice,
|
||||||
|
'location': instance.location,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@ class LaunchInstallationData {
|
||||||
required this.successCallback,
|
required this.successCallback,
|
||||||
required this.storageSize,
|
required this.storageSize,
|
||||||
required this.customSshKey,
|
required this.customSshKey,
|
||||||
|
required this.location,
|
||||||
});
|
});
|
||||||
|
|
||||||
final User rootUser;
|
final User rootUser;
|
||||||
|
@ -25,4 +26,5 @@ class LaunchInstallationData {
|
||||||
final Function(ServerHostingDetails details) successCallback;
|
final Function(ServerHostingDetails details) successCallback;
|
||||||
final DiskSize storageSize;
|
final DiskSize storageSize;
|
||||||
final String? customSshKey;
|
final String? customSshKey;
|
||||||
|
final String location;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@ class ServerBasicInfo {
|
||||||
required this.reverseDns,
|
required this.reverseDns,
|
||||||
required this.ip,
|
required this.ip,
|
||||||
required this.created,
|
required this.created,
|
||||||
|
required this.location,
|
||||||
});
|
});
|
||||||
final int id;
|
final int id;
|
||||||
final String name;
|
final String name;
|
||||||
final String reverseDns;
|
final String reverseDns;
|
||||||
final String ip;
|
final String ip;
|
||||||
final DateTime created;
|
final DateTime created;
|
||||||
|
final String location;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
|
@ -24,6 +26,7 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
reverseDns: serverBasicInfo.reverseDns,
|
reverseDns: serverBasicInfo.reverseDns,
|
||||||
ip: serverBasicInfo.ip,
|
ip: serverBasicInfo.ip,
|
||||||
created: serverBasicInfo.created,
|
created: serverBasicInfo.created,
|
||||||
|
location: serverBasicInfo.location,
|
||||||
isIpValid: isIpValid,
|
isIpValid: isIpValid,
|
||||||
isReverseDnsValid: isReverseDnsValid,
|
isReverseDnsValid: isReverseDnsValid,
|
||||||
);
|
);
|
||||||
|
@ -34,6 +37,7 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
required super.reverseDns,
|
required super.reverseDns,
|
||||||
required super.ip,
|
required super.ip,
|
||||||
required super.created,
|
required super.created,
|
||||||
|
required super.location,
|
||||||
required this.isIpValid,
|
required this.isIpValid,
|
||||||
required this.isReverseDnsValid,
|
required this.isReverseDnsValid,
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,13 +7,11 @@ class ServerProviderSettings {
|
||||||
required this.provider,
|
required this.provider,
|
||||||
this.token,
|
this.token,
|
||||||
this.isAuthorized = false,
|
this.isAuthorized = false,
|
||||||
this.location,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final bool isAuthorized;
|
final bool isAuthorized;
|
||||||
final ServerProviderType provider;
|
final ServerProviderType provider;
|
||||||
final String? token;
|
final String? token;
|
||||||
final String? location;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DnsProviderSettings {
|
class DnsProviderSettings {
|
||||||
|
|
|
@ -19,11 +19,9 @@ import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
class ApiAdapter {
|
class ApiAdapter {
|
||||||
ApiAdapter({
|
ApiAdapter({
|
||||||
final String? region,
|
|
||||||
final bool isWithToken = true,
|
final bool isWithToken = true,
|
||||||
final String? token,
|
final String? token,
|
||||||
}) : _api = DigitalOceanApi(
|
}) : _api = DigitalOceanApi(
|
||||||
region: region,
|
|
||||||
isWithToken: isWithToken,
|
isWithToken: isWithToken,
|
||||||
token: token ?? '',
|
token: token ?? '',
|
||||||
);
|
);
|
||||||
|
@ -31,7 +29,6 @@ class ApiAdapter {
|
||||||
DigitalOceanApi api({final bool getInitialized = true}) => getInitialized
|
DigitalOceanApi api({final bool getInitialized = true}) => getInitialized
|
||||||
? _api
|
? _api
|
||||||
: DigitalOceanApi(
|
: DigitalOceanApi(
|
||||||
region: _api.region,
|
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -41,16 +38,14 @@ class ApiAdapter {
|
||||||
class DigitalOceanServerProvider extends ServerProvider {
|
class DigitalOceanServerProvider extends ServerProvider {
|
||||||
DigitalOceanServerProvider() : _adapter = ApiAdapter(isWithToken: false);
|
DigitalOceanServerProvider() : _adapter = ApiAdapter(isWithToken: false);
|
||||||
DigitalOceanServerProvider.load(
|
DigitalOceanServerProvider.load(
|
||||||
final String? location,
|
|
||||||
final bool isAuthorized,
|
final bool isAuthorized,
|
||||||
final String? token,
|
final String? token,
|
||||||
) : _adapter = ApiAdapter(
|
) : _adapter = ApiAdapter(
|
||||||
isWithToken: isAuthorized,
|
isWithToken: isAuthorized,
|
||||||
region: location,
|
|
||||||
token: token,
|
token: token,
|
||||||
);
|
);
|
||||||
|
|
||||||
ApiAdapter _adapter;
|
final ApiAdapter _adapter;
|
||||||
final Currency currency = Currency.fromType(CurrencyType.usd);
|
final Currency currency = Currency.fromType(CurrencyType.usd);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -90,6 +85,7 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
created: DateTime.now(),
|
created: DateTime.now(),
|
||||||
ip: ipv4,
|
ip: ipv4,
|
||||||
name: server['name'],
|
name: server['name'],
|
||||||
|
location: server['region']['slug'],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).toList();
|
).toList();
|
||||||
|
@ -238,6 +234,7 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
databasePassword: StringGenerators.dbPassword(),
|
databasePassword: StringGenerators.dbPassword(),
|
||||||
serverApiToken: serverApiToken,
|
serverApiToken: serverApiToken,
|
||||||
customSshKey: installationData.customSshKey,
|
customSshKey: installationData.customSshKey,
|
||||||
|
region: installationData.location,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!serverResult.success || serverResult.data == null) {
|
if (!serverResult.success || serverResult.data == null) {
|
||||||
|
@ -264,12 +261,15 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final int dropletId = serverResult.data!;
|
final int dropletId = serverResult.data!;
|
||||||
final newVolume =
|
final newVolume = (await createVolume(
|
||||||
(await createVolume(installationData.storageSize.gibibyte.toInt()))
|
installationData.storageSize.gibibyte.toInt(),
|
||||||
|
installationData.location,
|
||||||
|
))
|
||||||
.data;
|
.data;
|
||||||
final bool attachedVolume = (await _adapter.api().attachVolume(
|
final bool attachedVolume = (await _adapter.api().attachVolume(
|
||||||
newVolume!.name,
|
name: newVolume!.name,
|
||||||
dropletId,
|
serverId: dropletId,
|
||||||
|
region: installationData.location,
|
||||||
))
|
))
|
||||||
.data;
|
.data;
|
||||||
|
|
||||||
|
@ -350,9 +350,14 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
(final el) => el.serverId == foundServer!.id,
|
(final el) => el.serverId == foundServer!.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (volumeToRemove.location == null) {
|
||||||
|
throw Exception('Volume location is null!');
|
||||||
|
}
|
||||||
|
|
||||||
await _adapter.api().detachVolume(
|
await _adapter.api().detachVolume(
|
||||||
volumeToRemove.name,
|
name: volumeToRemove.name,
|
||||||
volumeToRemove.serverId!,
|
serverId: volumeToRemove.serverId!,
|
||||||
|
region: volumeToRemove.location!,
|
||||||
);
|
);
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
@ -400,33 +405,9 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_adapter = ApiAdapter(region: api.region, isWithToken: true);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<bool>> trySetServerLocation(
|
|
||||||
final String location,
|
|
||||||
) async {
|
|
||||||
final bool apiInitialized = _adapter.api().isWithToken;
|
|
||||||
final String token = _adapter._api.token;
|
|
||||||
|
|
||||||
if (!apiInitialized || token.isEmpty) {
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: false,
|
|
||||||
message: 'Not authorized!',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_adapter = ApiAdapter(
|
|
||||||
isWithToken: true,
|
|
||||||
region: location,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerProviderLocation>>>
|
Future<GenericResult<List<ServerProviderLocation>>>
|
||||||
getAvailableLocations() async {
|
getAvailableLocations() async {
|
||||||
|
@ -544,7 +525,9 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing() async =>
|
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing(
|
||||||
|
final String location,
|
||||||
|
) async =>
|
||||||
GenericResult(
|
GenericResult(
|
||||||
success: true,
|
success: true,
|
||||||
data: AdditionalPricing(
|
data: AdditionalPricing(
|
||||||
|
@ -591,6 +574,7 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
: null,
|
: null,
|
||||||
linuxDevice: 'scsi-0DO_Volume_$volumeName',
|
linuxDevice: 'scsi-0DO_Volume_$volumeName',
|
||||||
uuid: rawVolume.id,
|
uuid: rawVolume.id,
|
||||||
|
location: rawVolume.region.slug,
|
||||||
);
|
);
|
||||||
volumes.add(volume);
|
volumes.add(volume);
|
||||||
}
|
}
|
||||||
|
@ -612,10 +596,11 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<ServerProviderVolume?>> createVolume(
|
Future<GenericResult<ServerProviderVolume?>> createVolume(
|
||||||
final int gb,
|
final int gb,
|
||||||
|
final String location,
|
||||||
) async {
|
) async {
|
||||||
ServerProviderVolume? volume;
|
ServerProviderVolume? volume;
|
||||||
|
|
||||||
final result = await _adapter.api().createVolume(gb);
|
final result = await _adapter.api().createVolume(gb: gb, region: location);
|
||||||
|
|
||||||
if (!result.success || result.data == null) {
|
if (!result.success || result.data == null) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
|
@ -687,8 +672,9 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
final int serverId,
|
final int serverId,
|
||||||
) async =>
|
) async =>
|
||||||
_adapter.api().attachVolume(
|
_adapter.api().attachVolume(
|
||||||
volume.name,
|
name: volume.name,
|
||||||
serverId,
|
serverId: serverId,
|
||||||
|
region: volume.location!,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -696,8 +682,9 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
final ServerProviderVolume volume,
|
final ServerProviderVolume volume,
|
||||||
) async =>
|
) async =>
|
||||||
_adapter.api().detachVolume(
|
_adapter.api().detachVolume(
|
||||||
volume.name,
|
name: volume.name,
|
||||||
volume.serverId!,
|
serverId: volume.serverId!,
|
||||||
|
region: volume.location!,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -714,13 +701,15 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
final DiskSize size,
|
final DiskSize size,
|
||||||
) async =>
|
) async =>
|
||||||
_adapter.api().resizeVolume(
|
_adapter.api().resizeVolume(
|
||||||
volume.uuid!,
|
uuid: volume.uuid!,
|
||||||
size.gibibyte.toInt(),
|
gb: size.gibibyte.toInt(),
|
||||||
|
region: volume.location!,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
||||||
final int serverId,
|
final int serverId,
|
||||||
|
final String location,
|
||||||
) async {
|
) async {
|
||||||
List<ServerMetadataEntity> metadata = [];
|
List<ServerMetadataEntity> metadata = [];
|
||||||
final result = await _adapter.api().getServers();
|
final result = await _adapter.api().getServers();
|
||||||
|
@ -741,7 +730,7 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
message: resultVolumes.message,
|
message: resultVolumes.message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final resultPricePerGb = await getAdditionalPricing();
|
final resultPricePerGb = await getAdditionalPricing(location);
|
||||||
if (resultPricePerGb.data == null || !resultPricePerGb.success) {
|
if (resultPricePerGb.data == null || !resultPricePerGb.success) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
success: false,
|
success: false,
|
||||||
|
|
|
@ -19,11 +19,9 @@ import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
class ApiAdapter {
|
class ApiAdapter {
|
||||||
ApiAdapter({
|
ApiAdapter({
|
||||||
final String? region,
|
|
||||||
final bool isWithToken = true,
|
final bool isWithToken = true,
|
||||||
final String? token,
|
final String? token,
|
||||||
}) : _api = HetznerApi(
|
}) : _api = HetznerApi(
|
||||||
region: region,
|
|
||||||
isWithToken: isWithToken,
|
isWithToken: isWithToken,
|
||||||
token: token ?? '',
|
token: token ?? '',
|
||||||
);
|
);
|
||||||
|
@ -31,7 +29,6 @@ class ApiAdapter {
|
||||||
HetznerApi api({final bool getInitialized = true}) => getInitialized
|
HetznerApi api({final bool getInitialized = true}) => getInitialized
|
||||||
? _api
|
? _api
|
||||||
: HetznerApi(
|
: HetznerApi(
|
||||||
region: _api.region,
|
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -41,16 +38,14 @@ class ApiAdapter {
|
||||||
class HetznerServerProvider extends ServerProvider {
|
class HetznerServerProvider extends ServerProvider {
|
||||||
HetznerServerProvider() : _adapter = ApiAdapter(isWithToken: false);
|
HetznerServerProvider() : _adapter = ApiAdapter(isWithToken: false);
|
||||||
HetznerServerProvider.load(
|
HetznerServerProvider.load(
|
||||||
final String? location,
|
|
||||||
final bool isAuthorized,
|
final bool isAuthorized,
|
||||||
final String? token,
|
final String? token,
|
||||||
) : _adapter = ApiAdapter(
|
) : _adapter = ApiAdapter(
|
||||||
isWithToken: isAuthorized,
|
isWithToken: isAuthorized,
|
||||||
region: location,
|
|
||||||
token: token,
|
token: token,
|
||||||
);
|
);
|
||||||
|
|
||||||
ApiAdapter _adapter;
|
final ApiAdapter _adapter;
|
||||||
final Currency currency = Currency.fromType(CurrencyType.eur);
|
final Currency currency = Currency.fromType(CurrencyType.eur);
|
||||||
int? cachedCoreAmount;
|
int? cachedCoreAmount;
|
||||||
|
|
||||||
|
@ -87,6 +82,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
ip: hetznerServer.publicNet.ipv4!.ip,
|
ip: hetznerServer.publicNet.ipv4!.ip,
|
||||||
reverseDns: hetznerServer.publicNet.ipv4!.reverseDns,
|
reverseDns: hetznerServer.publicNet.ipv4!.reverseDns,
|
||||||
created: hetznerServer.created,
|
created: hetznerServer.created,
|
||||||
|
location: hetznerServer.location.name,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -176,7 +172,8 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
final LaunchInstallationData installationData,
|
final LaunchInstallationData installationData,
|
||||||
) async {
|
) async {
|
||||||
final volumeResult = await _adapter.api().createVolume(
|
final volumeResult = await _adapter.api().createVolume(
|
||||||
installationData.storageSize.gibibyte.toInt(),
|
gb: installationData.storageSize.gibibyte.toInt(),
|
||||||
|
region: installationData.location,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!volumeResult.success || volumeResult.data == null) {
|
if (!volumeResult.success || volumeResult.data == null) {
|
||||||
|
@ -222,6 +219,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
databasePassword: StringGenerators.dbPassword(),
|
databasePassword: StringGenerators.dbPassword(),
|
||||||
serverApiToken: serverApiToken,
|
serverApiToken: serverApiToken,
|
||||||
customSshKey: installationData.customSshKey,
|
customSshKey: installationData.customSshKey,
|
||||||
|
region: installationData.location,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!serverResult.success || serverResult.data == null) {
|
if (!serverResult.success || serverResult.data == null) {
|
||||||
|
@ -419,33 +417,9 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// _adapter = ApiAdapter(region: api.region, isWithToken: true);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<GenericResult<bool>> trySetServerLocation(
|
|
||||||
final String location,
|
|
||||||
) async {
|
|
||||||
final bool apiInitialized = _adapter.api().isWithToken;
|
|
||||||
final String token = _adapter._api.token;
|
|
||||||
if (!apiInitialized || token.isEmpty) {
|
|
||||||
return GenericResult(
|
|
||||||
success: true,
|
|
||||||
data: false,
|
|
||||||
message: 'Not authorized!',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_adapter = ApiAdapter(
|
|
||||||
isWithToken: true,
|
|
||||||
region: location,
|
|
||||||
token: token,
|
|
||||||
);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerProviderLocation>>>
|
Future<GenericResult<List<ServerProviderLocation>>>
|
||||||
getAvailableLocations() async {
|
getAvailableLocations() async {
|
||||||
|
@ -566,8 +540,10 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing() async {
|
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing(
|
||||||
final result = await _adapter.api().getPricing();
|
final String location,
|
||||||
|
) async {
|
||||||
|
final result = await _adapter.api().getPricing(region: location);
|
||||||
|
|
||||||
if (!result.success || result.data == null) {
|
if (!result.success || result.data == null) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
|
@ -617,12 +593,14 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
final volumeServer = rawVolume.serverId;
|
final volumeServer = rawVolume.serverId;
|
||||||
final String volumeName = rawVolume.name;
|
final String volumeName = rawVolume.name;
|
||||||
final volumeDevice = rawVolume.linuxDevice;
|
final volumeDevice = rawVolume.linuxDevice;
|
||||||
|
final volumeLocation = rawVolume.location.name;
|
||||||
final volume = ServerProviderVolume(
|
final volume = ServerProviderVolume(
|
||||||
id: volumeId,
|
id: volumeId,
|
||||||
name: volumeName,
|
name: volumeName,
|
||||||
sizeByte: volumeSize,
|
sizeByte: volumeSize,
|
||||||
serverId: volumeServer,
|
serverId: volumeServer,
|
||||||
linuxDevice: volumeDevice,
|
linuxDevice: volumeDevice,
|
||||||
|
location: volumeLocation,
|
||||||
);
|
);
|
||||||
volumes.add(volume);
|
volumes.add(volume);
|
||||||
}
|
}
|
||||||
|
@ -645,10 +623,11 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<ServerProviderVolume?>> createVolume(
|
Future<GenericResult<ServerProviderVolume?>> createVolume(
|
||||||
final int gb,
|
final int gb,
|
||||||
|
final String location,
|
||||||
) async {
|
) async {
|
||||||
ServerProviderVolume? volume;
|
ServerProviderVolume? volume;
|
||||||
|
|
||||||
final result = await _adapter.api().createVolume(gb);
|
final result = await _adapter.api().createVolume(gb: gb, region: location);
|
||||||
|
|
||||||
if (!result.success || result.data == null) {
|
if (!result.success || result.data == null) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
|
@ -702,6 +681,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
volume.serverId,
|
volume.serverId,
|
||||||
volume.name,
|
volume.name,
|
||||||
volume.linuxDevice,
|
volume.linuxDevice,
|
||||||
|
HetznerLocation.empty(),
|
||||||
),
|
),
|
||||||
size,
|
size,
|
||||||
);
|
);
|
||||||
|
@ -718,6 +698,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
volume.serverId,
|
volume.serverId,
|
||||||
volume.name,
|
volume.name,
|
||||||
volume.linuxDevice,
|
volume.linuxDevice,
|
||||||
|
HetznerLocation.empty(),
|
||||||
),
|
),
|
||||||
serverId,
|
serverId,
|
||||||
);
|
);
|
||||||
|
@ -733,6 +714,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
@override
|
@override
|
||||||
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
||||||
final int serverId,
|
final int serverId,
|
||||||
|
final String location,
|
||||||
) async {
|
) async {
|
||||||
List<ServerMetadataEntity> metadata = [];
|
List<ServerMetadataEntity> metadata = [];
|
||||||
final resultServers = await _adapter.api().getServers();
|
final resultServers = await _adapter.api().getServers();
|
||||||
|
@ -753,7 +735,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
message: resultVolumes.message,
|
message: resultVolumes.message,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final resultPricePerGb = await getAdditionalPricing();
|
final resultPricePerGb = await getAdditionalPricing(location);
|
||||||
if (resultPricePerGb.data == null || !resultPricePerGb.success) {
|
if (resultPricePerGb.data == null || !resultPricePerGb.success) {
|
||||||
return GenericResult(
|
return GenericResult(
|
||||||
success: false,
|
success: false,
|
||||||
|
|
|
@ -60,12 +60,6 @@ abstract class ServerProvider {
|
||||||
/// server provider respectfully.
|
/// server provider respectfully.
|
||||||
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
|
/// Returns all available server locations
|
||||||
/// of the authorized user's server provider.
|
/// of the authorized user's server provider.
|
||||||
Future<GenericResult<List<ServerProviderLocation>>> getAvailableLocations();
|
Future<GenericResult<List<ServerProviderLocation>>> getAvailableLocations();
|
||||||
|
@ -92,7 +86,9 @@ abstract class ServerProvider {
|
||||||
|
|
||||||
/// Returns [Price] information map of all additional resources, excluding
|
/// Returns [Price] information map of all additional resources, excluding
|
||||||
/// main server type pricing
|
/// main server type pricing
|
||||||
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing();
|
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing(
|
||||||
|
final String location,
|
||||||
|
);
|
||||||
|
|
||||||
/// Returns [ServerProviderVolume] of all available volumes
|
/// Returns [ServerProviderVolume] of all available volumes
|
||||||
/// assigned to the authorized user and attached to active machine.
|
/// assigned to the authorized user and attached to active machine.
|
||||||
|
@ -103,7 +99,10 @@ abstract class ServerProvider {
|
||||||
/// Tries to create an empty unattached [ServerProviderVolume].
|
/// Tries to create an empty unattached [ServerProviderVolume].
|
||||||
///
|
///
|
||||||
/// If success, returns this volume information.
|
/// If success, returns this volume information.
|
||||||
Future<GenericResult<ServerProviderVolume?>> createVolume(final int gb);
|
Future<GenericResult<ServerProviderVolume?>> createVolume(
|
||||||
|
final int gb,
|
||||||
|
final String location,
|
||||||
|
);
|
||||||
|
|
||||||
/// Tries to delete the requested accessible [ServerProviderVolume].
|
/// Tries to delete the requested accessible [ServerProviderVolume].
|
||||||
Future<GenericResult<void>> deleteVolume(final ServerProviderVolume volume);
|
Future<GenericResult<void>> deleteVolume(final ServerProviderVolume volume);
|
||||||
|
@ -126,10 +125,11 @@ abstract class ServerProvider {
|
||||||
/// from any machine.
|
/// from any machine.
|
||||||
Future<GenericResult<bool>> detachVolume(final ServerProviderVolume volume);
|
Future<GenericResult<bool>> detachVolume(final ServerProviderVolume volume);
|
||||||
|
|
||||||
/// Returns metedata of an accessible machine by the provided identificator
|
/// Returns metadata of an accessible machine by the provided identificator
|
||||||
/// to show on ServerDetailsScreen.
|
/// to show on ServerDetailsScreen.
|
||||||
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
|
||||||
final int serverId,
|
final int serverId,
|
||||||
|
final String location,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Returns information about cpu and bandwidth load within the provided
|
/// Returns information about cpu and bandwidth load within the provided
|
||||||
|
|
|
@ -17,7 +17,6 @@ class ServerProviderFactory {
|
||||||
case ServerProviderType.hetzner:
|
case ServerProviderType.hetzner:
|
||||||
return settings.isAuthorized
|
return settings.isAuthorized
|
||||||
? HetznerServerProvider.load(
|
? HetznerServerProvider.load(
|
||||||
settings.location,
|
|
||||||
settings.isAuthorized,
|
settings.isAuthorized,
|
||||||
settings.token,
|
settings.token,
|
||||||
)
|
)
|
||||||
|
@ -25,7 +24,6 @@ class ServerProviderFactory {
|
||||||
case ServerProviderType.digitalOcean:
|
case ServerProviderType.digitalOcean:
|
||||||
return settings.isAuthorized
|
return settings.isAuthorized
|
||||||
? DigitalOceanServerProvider.load(
|
? DigitalOceanServerProvider.load(
|
||||||
settings.location,
|
|
||||||
settings.isAuthorized,
|
settings.isAuthorized,
|
||||||
settings.token,
|
settings.token,
|
||||||
)
|
)
|
||||||
|
|
|
@ -172,7 +172,7 @@ class SelectTypePage extends StatelessWidget {
|
||||||
final Future<List<ServerType>> serverTypes =
|
final Future<List<ServerType>> serverTypes =
|
||||||
serverInstallationCubit.fetchAvailableTypesByLocation(location);
|
serverInstallationCubit.fetchAvailableTypesByLocation(location);
|
||||||
final Future<AdditionalPricing?> prices =
|
final Future<AdditionalPricing?> prices =
|
||||||
serverInstallationCubit.fetchAvailableAdditionalPricing();
|
serverInstallationCubit.fetchAvailableAdditionalPricing(location);
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: Future.wait([
|
future: Future.wait([
|
||||||
serverTypes,
|
serverTypes,
|
||||||
|
|
Loading…
Reference in a new issue