diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart index 4fdbbb08..3dee8313 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart @@ -10,7 +10,6 @@ import 'package:selfprivacy/utils/password_generator.dart'; class DigitalOceanApi extends RestApiMap { DigitalOceanApi({ - required this.region, this.token = '', this.hasLogger = true, this.isWithToken = true, @@ -21,7 +20,6 @@ class DigitalOceanApi extends RestApiMap { @override bool isWithToken; - final String? region; final String token; @override @@ -79,6 +77,7 @@ class DigitalOceanApi extends RestApiMap { required final String hostName, required final String serverType, required final String? customSshKey, + required final String region, }) async { 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' " "${customSshKey != null ? "SSH_AUTHORIZED_KEY='$customSshKey'" : ""} " 'bash 2>&1 | tee /root/nixos-infect.log', - 'region': region!, + 'region': region, }; print('Decoded data: $data'); @@ -326,7 +325,10 @@ class DigitalOceanApi extends RestApiMap { ); } - Future> createVolume(final int gb) async { + Future> createVolume({ + required final int gb, + required final String region, + }) async { DigitalOceanVolume? volume; Response? createVolumeResponse; final Dio client = await getClient(); @@ -363,10 +365,11 @@ class DigitalOceanApi extends RestApiMap { ); } - Future> attachVolume( - final String name, - final int serverId, - ) async { + Future> attachVolume({ + required final String name, + required final int serverId, + required final String region, + }) async { bool success = false; Response? attachVolumeResponse; @@ -402,10 +405,11 @@ class DigitalOceanApi extends RestApiMap { ); } - Future> detachVolume( - final String name, - final int serverId, - ) async { + Future> detachVolume({ + required final String name, + required final int serverId, + required final String region, + }) async { bool success = false; final Response detachVolumeResponse; @@ -460,10 +464,11 @@ class DigitalOceanApi extends RestApiMap { ); } - Future> resizeVolume( - final String uuid, - final int gb, - ) async { + Future> resizeVolume({ + required final String uuid, + required final int gb, + required final String region, + }) async { bool success = false; final Response resizeVolumeResponse; diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart index 514e8402..60bfaa75 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_api.dart @@ -11,7 +11,6 @@ import 'package:selfprivacy/utils/password_generator.dart'; class HetznerApi extends RestApiMap { HetznerApi({ - this.region, this.token = '', this.hasLogger = true, this.isWithToken = true, @@ -22,7 +21,6 @@ class HetznerApi extends RestApiMap { @override bool isWithToken; - final String? region; final String token; @override @@ -85,6 +83,7 @@ class HetznerApi extends RestApiMap { required final int volumeId, required final String serverType, required final String? customSshKey, + required final String region, }) async { final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false'; Response? serverCreateResponse; @@ -111,7 +110,7 @@ class HetznerApi extends RestApiMap { 'bash 2>&1 | tee /root/nixos-infect.log', 'labels': {}, 'automount': true, - 'location': region!, + 'location': region, }; print('Decoded data: $data'); @@ -326,7 +325,9 @@ class HetznerApi extends RestApiMap { return GenericResult(success: true, data: null); } - Future> getPricing() async { + Future> getPricing({ + required final String region, + }) async { HetznerPricing? pricing; final Response pricingResponse; @@ -341,14 +342,14 @@ class HetznerApi extends RestApiMap { for (final primaryIp in primaryIps) { if (primaryIp['type'] == 'ipv4') { for (final primaryIpPrice in primaryIp['prices']) { - if (primaryIpPrice['location'] == region!) { + if (primaryIpPrice['location'] == region) { ipPrice = primaryIpPrice['price_monthly']['gross']; } } } } pricing = HetznerPricing( - region!, + region, double.parse(volumePrice), double.parse(ipPrice!), ); @@ -395,7 +396,10 @@ class HetznerApi extends RestApiMap { ); } - Future> createVolume(final int gb) async { + Future> createVolume({ + required final int gb, + required final String region, + }) async { Response? createVolumeResponse; HetznerVolume? volume; final Dio client = await getClient(); diff --git a/lib/logic/bloc/tokens/tokens_bloc.dart b/lib/logic/bloc/tokens/tokens_bloc.dart index d825d502..89a1a294 100644 --- a/lib/logic/bloc/tokens/tokens_bloc.dart +++ b/lib/logic/bloc/tokens/tokens_bloc.dart @@ -201,7 +201,7 @@ class TokensBloc extends Bloc { ), apiToken: event.server.hostingDetails.apiToken, provider: event.serverProviderCredential.provider, - serverLocation: event.server.hostingDetails.serverLocation, + serverLocation: event.providerServer.location, serverType: event.server.hostingDetails.serverType, ), ); diff --git a/lib/logic/bloc/volumes/volumes_bloc.dart b/lib/logic/bloc/volumes/volumes_bloc.dart index e2bfec0f..ad2a7b3c 100644 --- a/lib/logic/bloc/volumes/volumes_bloc.dart +++ b/lib/logic/bloc/volumes/volumes_bloc.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:bloc_concurrency/bloc_concurrency.dart'; +import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -88,14 +89,21 @@ class VolumesBloc extends Bloc { return null; } Price? price; - final pricingResult = - await ProvidersController.currentServerProvider!.getAdditionalPricing(); - if (pricingResult.data == null || !pricingResult.success) { + final location = state.location; + if (location != null) { + final pricingResult = await ProvidersController.currentServerProvider! + .getAdditionalPricing(location); + if (pricingResult.data == null || !pricingResult.success) { + getIt().showSnackBar('server.pricing_error'.tr()); + return price; + } + price = pricingResult.data!.perVolumeGb; + return price; + } else { + await Future.delayed(Duration.zero); getIt().showSnackBar('server.pricing_error'.tr()); return price; } - price = pricingResult.data!.perVolumeGb; - return price; } Future _loadState( diff --git a/lib/logic/bloc/volumes/volumes_state.dart b/lib/logic/bloc/volumes/volumes_state.dart index 04c745e4..b4cf019c 100644 --- a/lib/logic/bloc/volumes/volumes_state.dart +++ b/lib/logic/bloc/volumes/volumes_state.dart @@ -17,6 +17,11 @@ sealed class VolumesState extends Equatable { orElse: () => DiskVolume(), ); + String? get location => volumes + .firstWhereOrNull((final volume) => volume.isResizable) + ?.providerVolume + ?.location; + bool get isProviderVolumesLoaded => providerVolumes.isNotEmpty; VolumesState copyWith({ diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart index 9a2b98db..1a2bf9fd 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart @@ -45,17 +45,26 @@ class ServerDetailsCubit Future> get _metadata async { final List data = []; + final Server? server = getIt().servers.firstOrNull; + + if (server == null) { + return data; + } + final serverProviderApi = ProvidersController.currentServerProvider; final dnsProviderApi = ProvidersController.currentDnsProvider; - if (serverProviderApi?.isAuthorized ?? false) { - final serverId = getIt().serverDetails?.id ?? 0; - final metadataResult = await serverProviderApi?.getMetadata(serverId); + if (server.hostingDetails.serverLocation != null && + (serverProviderApi?.isAuthorized ?? false)) { + final serverId = server.hostingDetails.id; + final metadataResult = await serverProviderApi?.getMetadata( + serverId, + server.hostingDetails.serverLocation!, + ); data.addAll(metadataResult?.data ?? []); } if (serverProviderApi == null || !serverProviderApi.isAuthorized) { - final Server server = getIt().servers.first; data.add( ServerMetadataEntity( type: MetadataType.other, @@ -74,7 +83,6 @@ class ServerDetailsCubit ), ); } else { - final Server server = getIt().servers.first; data.add( ServerMetadataEntity( trId: 'server.dns_provider', diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 1b3da96c..b123ac30 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -168,10 +168,12 @@ class ServerInstallationCubit extends Cubit { return apiResult.data; } - Future fetchAvailableAdditionalPricing() async { + Future fetchAvailableAdditionalPricing( + final ServerProviderLocation location, + ) async { AdditionalPricing? prices; - final pricingResult = - await ProvidersController.currentServerProvider!.getAdditionalPricing(); + final pricingResult = await ProvidersController.currentServerProvider! + .getAdditionalPricing(location.identifier); if (pricingResult.data == null || !pricingResult.success) { getIt().showSnackBar('server.pricing_error'.tr()); return prices; @@ -202,8 +204,11 @@ class ServerInstallationCubit extends Cubit { } Future setLocationIdentifier(final String locationId) async { - await ProvidersController.currentServerProvider! - .trySetServerLocation(locationId); + emit( + (state as ServerInstallationNotFinished).copyWith( + serverLocation: locationId, + ), + ); } void setServerType(final ServerType serverType) async { @@ -212,6 +217,7 @@ class ServerInstallationCubit extends Cubit { emit( (state as ServerInstallationNotFinished).copyWith( serverTypeIdentificator: serverType.identifier, + serverLocation: serverType.location.identifier, ), ); } @@ -335,6 +341,7 @@ class ServerInstallationCubit extends Cubit { successCallback: onCreateServerSuccess, storageSize: initialStorage, customSshKey: (state as ServerInstallationNotFinished).customSshKey, + location: state.serverLocation!, ); final result = @@ -794,12 +801,14 @@ class ServerInstallationCubit extends Cubit { ), apiToken: dataState.serverDetails!.apiToken, provider: dataState.serverDetails!.provider, + serverLocation: server.location, ); await repository.saveDomain(serverDomain); await repository.saveServerDetails(serverDetails); emit( dataState.copyWith( serverDetails: serverDetails, + serverLocation: server.location, currentStep: RecoveryStep.dnsProviderToken, ), ); @@ -858,8 +867,6 @@ class ServerInstallationCubit extends Cubit { ?.getServerType(state.serverDetails!.id); if (serverType != null) { await repository.saveServerType(serverType.data!); - await ProvidersController.currentServerProvider! - .trySetServerLocation(serverType.data!.location.identifier); } } else { serverType = null; diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index e0fdecfa..ec99e636 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -64,7 +64,6 @@ class ServerInstallationRepository { ServerProviderSettings( provider: serverProvider ?? serverDetails!.provider, isAuthorized: providerApiToken != null, - location: location, token: providerApiToken, ), ); @@ -100,6 +99,7 @@ class ServerInstallationRepository { serverDomain: serverDomain!, backblazeCredential: backblazeCredential!, serverDetails: serverDetails!, + serverLocation: location, ); } } @@ -111,7 +111,6 @@ class ServerInstallationRepository { ServerProviderSettings( provider: wizardData.serverProviderType!, isAuthorized: wizardData.serverProviderKey != null, - location: wizardData.serverLocation, token: wizardData.serverProviderKey, ), ); @@ -134,6 +133,7 @@ class ServerInstallationRepository { dnsApiToken: wizardData.dnsProviderKey, serverDomain: wizardData.serverDomain, serverTypeIdentificator: wizardData.serverTypeIdentifier, + serverLocation: wizardData.serverLocation, backblazeCredential: wizardData.backupsCredential, serverDetails: wizardData.serverDetails, currentStep: _getCurrentRecoveryStep( diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index ec4a7aa1..3d4fe6e2 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -13,6 +13,7 @@ abstract class ServerInstallationState extends Equatable { required this.isServerResetedFirstTime, required this.isServerResetedSecondTime, required this.installationDialoguePopUp, + required this.serverLocation, }); @override @@ -27,11 +28,13 @@ abstract class ServerInstallationState extends Equatable { isServerStarted, isServerResetedFirstTime, installationDialoguePopUp, + serverLocation, ]; final String? providerApiToken; final String? dnsApiToken; final String? serverTypeIdentificator; + final String? serverLocation; final BackupsCredential? backblazeCredential; final ServerDomain? serverDomain; final User? rootUser; @@ -91,6 +94,7 @@ class TimerState extends ServerInstallationNotFinished { }) : super( providerApiToken: dataState.providerApiToken, serverTypeIdentificator: dataState.serverTypeIdentificator, + serverLocation: dataState.serverLocation, dnsApiToken: dataState.dnsApiToken, backblazeCredential: dataState.backblazeCredential, serverDomain: dataState.serverDomain, @@ -139,6 +143,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { required this.dnsMatches, required this.customSshKey, super.providerApiToken, + super.serverLocation, super.serverTypeIdentificator, super.dnsApiToken, super.backblazeCredential, @@ -155,6 +160,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { dnsApiToken: data.dnsProviderKey, serverDomain: data.serverDomain, serverTypeIdentificator: data.serverTypeIdentifier, + serverLocation: data.serverLocation, backblazeCredential: data.backupsCredential, serverDetails: data.serverDetails, rootUser: data.rootUser, @@ -175,6 +181,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { List get props => [ providerApiToken, serverTypeIdentificator, + serverLocation, dnsApiToken, backblazeCredential, serverDomain, @@ -190,6 +197,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { ServerInstallationNotFinished copyWith({ final String? providerApiToken, + final String? serverLocation, final String? serverTypeIdentificator, final String? dnsApiToken, final BackupsCredential? backblazeCredential, @@ -206,6 +214,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { }) => ServerInstallationNotFinished( providerApiToken: providerApiToken ?? this.providerApiToken, + serverLocation: serverLocation ?? this.serverLocation, serverTypeIdentificator: serverTypeIdentificator ?? this.serverTypeIdentificator, dnsApiToken: dnsApiToken ?? this.dnsApiToken, @@ -227,6 +236,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { ServerInstallationFinished finish() => ServerInstallationFinished( providerApiToken: providerApiToken, + serverLocation: serverLocation, serverTypeIdentificator: serverTypeIdentificator, dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, @@ -239,6 +249,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished { const ServerInstallationEmpty() : super( providerApiToken: null, + serverLocation: null, serverTypeIdentificator: null, dnsApiToken: null, backblazeCredential: null, @@ -263,6 +274,7 @@ class ServerInstallationFinished extends ServerInstallationState { required ServerHostingDetails super.serverDetails, super.providerApiToken, super.serverTypeIdentificator, + super.serverLocation, }) : super( rootUser: null, isServerStarted: true, @@ -275,6 +287,7 @@ class ServerInstallationFinished extends ServerInstallationState { List get props => [ providerApiToken, serverTypeIdentificator, + serverLocation, dnsApiToken, backblazeCredential, serverDomain, @@ -315,6 +328,7 @@ class ServerInstallationRecovery extends ServerInstallationState { required this.recoveryCapabilities, super.providerApiToken, super.serverTypeIdentificator, + super.serverLocation, super.dnsApiToken, super.backblazeCredential, super.serverDomain, @@ -333,6 +347,7 @@ class ServerInstallationRecovery extends ServerInstallationState { List get props => [ providerApiToken, serverTypeIdentificator, + serverLocation, dnsApiToken, backblazeCredential, serverDomain, @@ -346,6 +361,7 @@ class ServerInstallationRecovery extends ServerInstallationState { ServerInstallationRecovery copyWith({ final String? providerApiToken, + final String? serverLocation, final String? serverTypeIdentificator, final String? dnsApiToken, final BackupsCredential? backblazeCredential, @@ -356,6 +372,7 @@ class ServerInstallationRecovery extends ServerInstallationState { }) => ServerInstallationRecovery( providerApiToken: providerApiToken ?? this.providerApiToken, + serverLocation: serverLocation ?? this.serverLocation, serverTypeIdentificator: serverTypeIdentificator ?? this.serverTypeIdentificator, dnsApiToken: dnsApiToken ?? this.dnsApiToken, @@ -368,6 +385,7 @@ class ServerInstallationRecovery extends ServerInstallationState { ServerInstallationFinished finish() => ServerInstallationFinished( providerApiToken: providerApiToken, + serverLocation: serverLocation, serverTypeIdentificator: serverTypeIdentificator, dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, diff --git a/lib/logic/models/hive/server_details.dart b/lib/logic/models/hive/server_details.dart index da248c93..b24ee30f 100644 --- a/lib/logic/models/hive/server_details.dart +++ b/lib/logic/models/hive/server_details.dart @@ -76,6 +76,7 @@ class ServerProviderVolume { required this.serverId, required this.linuxDevice, this.uuid, + this.location, }); @HiveField(1) @@ -90,6 +91,8 @@ class ServerProviderVolume { String? linuxDevice; @HiveField(6, defaultValue: null) String? uuid; + @HiveField(7, defaultValue: null) + String? location; } @HiveType(typeId: 101) diff --git a/lib/logic/models/hive/server_details.g.dart b/lib/logic/models/hive/server_details.g.dart index 1ace42fc..2138788c 100644 --- a/lib/logic/models/hive/server_details.g.dart +++ b/lib/logic/models/hive/server_details.g.dart @@ -83,13 +83,14 @@ class ServerProviderVolumeAdapter extends TypeAdapter { serverId: fields[4] as int?, linuxDevice: fields[5] as String?, uuid: fields[6] as String?, + location: fields[7] as String?, ); } @override void write(BinaryWriter writer, ServerProviderVolume obj) { writer - ..writeByte(6) + ..writeByte(7) ..writeByte(1) ..write(obj.id) ..writeByte(2) @@ -101,7 +102,9 @@ class ServerProviderVolumeAdapter extends TypeAdapter { ..writeByte(5) ..write(obj.linuxDevice) ..writeByte(6) - ..write(obj.uuid); + ..write(obj.uuid) + ..writeByte(7) + ..write(obj.location); } @override diff --git a/lib/logic/models/json/digital_ocean_server_info.dart b/lib/logic/models/json/digital_ocean_server_info.dart index 5e92de29..b26b341d 100644 --- a/lib/logic/models/json/digital_ocean_server_info.dart +++ b/lib/logic/models/json/digital_ocean_server_info.dart @@ -9,6 +9,7 @@ class DigitalOceanVolume { this.name, this.sizeGigabytes, this.dropletIds, + this.region, ); final String id; @@ -20,6 +21,8 @@ class DigitalOceanVolume { @JsonKey(name: 'size_gigabytes') final int sizeGigabytes; + final DigitalOceanLocation region; + static DigitalOceanVolume fromJson(final Map json) => _$DigitalOceanVolumeFromJson(json); } diff --git a/lib/logic/models/json/digital_ocean_server_info.g.dart b/lib/logic/models/json/digital_ocean_server_info.g.dart index ea3d73d9..e55e889f 100644 --- a/lib/logic/models/json/digital_ocean_server_info.g.dart +++ b/lib/logic/models/json/digital_ocean_server_info.g.dart @@ -14,6 +14,7 @@ DigitalOceanVolume _$DigitalOceanVolumeFromJson(Map json) => (json['droplet_ids'] as List?) ?.map((e) => (e as num).toInt()) .toList(), + DigitalOceanLocation.fromJson(json['region'] as Map), ); Map _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) => @@ -22,6 +23,7 @@ Map _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) => 'name': instance.name, 'droplet_ids': instance.dropletIds, 'size_gigabytes': instance.sizeGigabytes, + 'region': instance.region, }; DigitalOceanLocation _$DigitalOceanLocationFromJson( diff --git a/lib/logic/models/json/hetzner_server_info.dart b/lib/logic/models/json/hetzner_server_info.dart index a0559de6..262a8aeb 100644 --- a/lib/logic/models/json/hetzner_server_info.dart +++ b/lib/logic/models/json/hetzner_server_info.dart @@ -127,6 +127,9 @@ class HetznerLocation { this.zone, this.name, ); + + HetznerLocation.empty() : this('', '', '', '', ''); + final String name; final String country; final String city; @@ -192,6 +195,7 @@ class HetznerVolume { this.serverId, this.name, this.linuxDevice, + this.location, ); /// ID of the Resource @@ -210,6 +214,8 @@ class HetznerVolume { @JsonKey(name: 'linux_device') final String? linuxDevice; + final HetznerLocation location; + static HetznerVolume fromJson(final Map json) => _$HetznerVolumeFromJson(json); } diff --git a/lib/logic/models/json/hetzner_server_info.g.dart b/lib/logic/models/json/hetzner_server_info.g.dart index fb877295..87dc5262 100644 --- a/lib/logic/models/json/hetzner_server_info.g.dart +++ b/lib/logic/models/json/hetzner_server_info.g.dart @@ -139,6 +139,7 @@ HetznerVolume _$HetznerVolumeFromJson(Map json) => (json['serverId'] as num?)?.toInt(), json['name'] as String, json['linux_device'] as String?, + HetznerLocation.fromJson(json['location'] as Map), ); Map _$HetznerVolumeToJson(HetznerVolume instance) => @@ -148,4 +149,5 @@ Map _$HetznerVolumeToJson(HetznerVolume instance) => 'serverId': instance.serverId, 'name': instance.name, 'linux_device': instance.linuxDevice, + 'location': instance.location, }; diff --git a/lib/logic/models/launch_installation_data.dart b/lib/logic/models/launch_installation_data.dart index eea963a0..247652ee 100644 --- a/lib/logic/models/launch_installation_data.dart +++ b/lib/logic/models/launch_installation_data.dart @@ -14,6 +14,7 @@ class LaunchInstallationData { required this.successCallback, required this.storageSize, required this.customSshKey, + required this.location, }); final User rootUser; @@ -25,4 +26,5 @@ class LaunchInstallationData { final Function(ServerHostingDetails details) successCallback; final DiskSize storageSize; final String? customSshKey; + final String location; } diff --git a/lib/logic/models/server_basic_info.dart b/lib/logic/models/server_basic_info.dart index 3037a2d5..65069d16 100644 --- a/lib/logic/models/server_basic_info.dart +++ b/lib/logic/models/server_basic_info.dart @@ -5,12 +5,14 @@ class ServerBasicInfo { required this.reverseDns, required this.ip, required this.created, + required this.location, }); final int id; final String name; final String reverseDns; final String ip; final DateTime created; + final String location; } class ServerBasicInfoWithValidators extends ServerBasicInfo { @@ -24,6 +26,7 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo { reverseDns: serverBasicInfo.reverseDns, ip: serverBasicInfo.ip, created: serverBasicInfo.created, + location: serverBasicInfo.location, isIpValid: isIpValid, isReverseDnsValid: isReverseDnsValid, ); @@ -34,6 +37,7 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo { required super.reverseDns, required super.ip, required super.created, + required super.location, required this.isIpValid, required this.isReverseDnsValid, }); diff --git a/lib/logic/providers/provider_settings.dart b/lib/logic/providers/provider_settings.dart index 0029eb80..0c8eeed8 100644 --- a/lib/logic/providers/provider_settings.dart +++ b/lib/logic/providers/provider_settings.dart @@ -7,13 +7,11 @@ class ServerProviderSettings { required this.provider, this.token, this.isAuthorized = false, - this.location, }); final bool isAuthorized; final ServerProviderType provider; final String? token; - final String? location; } class DnsProviderSettings { diff --git a/lib/logic/providers/server_providers/digital_ocean.dart b/lib/logic/providers/server_providers/digital_ocean.dart index b199bc3c..ac293e3c 100644 --- a/lib/logic/providers/server_providers/digital_ocean.dart +++ b/lib/logic/providers/server_providers/digital_ocean.dart @@ -19,11 +19,9 @@ import 'package:selfprivacy/utils/password_generator.dart'; class ApiAdapter { ApiAdapter({ - final String? region, final bool isWithToken = true, final String? token, }) : _api = DigitalOceanApi( - region: region, isWithToken: isWithToken, token: token ?? '', ); @@ -31,7 +29,6 @@ class ApiAdapter { DigitalOceanApi api({final bool getInitialized = true}) => getInitialized ? _api : DigitalOceanApi( - region: _api.region, isWithToken: false, ); @@ -41,16 +38,14 @@ class ApiAdapter { class DigitalOceanServerProvider extends ServerProvider { DigitalOceanServerProvider() : _adapter = ApiAdapter(isWithToken: false); DigitalOceanServerProvider.load( - final String? location, final bool isAuthorized, final String? token, ) : _adapter = ApiAdapter( isWithToken: isAuthorized, - region: location, token: token, ); - ApiAdapter _adapter; + final ApiAdapter _adapter; final Currency currency = Currency.fromType(CurrencyType.usd); @override @@ -90,6 +85,7 @@ class DigitalOceanServerProvider extends ServerProvider { created: DateTime.now(), ip: ipv4, name: server['name'], + location: server['region']['slug'], ); }, ).toList(); @@ -238,6 +234,7 @@ class DigitalOceanServerProvider extends ServerProvider { databasePassword: StringGenerators.dbPassword(), serverApiToken: serverApiToken, customSshKey: installationData.customSshKey, + region: installationData.location, ); if (!serverResult.success || serverResult.data == null) { @@ -264,12 +261,15 @@ class DigitalOceanServerProvider extends ServerProvider { try { final int dropletId = serverResult.data!; - final newVolume = - (await createVolume(installationData.storageSize.gibibyte.toInt())) - .data; + final newVolume = (await createVolume( + installationData.storageSize.gibibyte.toInt(), + installationData.location, + )) + .data; final bool attachedVolume = (await _adapter.api().attachVolume( - newVolume!.name, - dropletId, + name: newVolume!.name, + serverId: dropletId, + region: installationData.location, )) .data; @@ -350,9 +350,14 @@ class DigitalOceanServerProvider extends ServerProvider { (final el) => el.serverId == foundServer!.id, ); + if (volumeToRemove.location == null) { + throw Exception('Volume location is null!'); + } + await _adapter.api().detachVolume( - volumeToRemove.name, - volumeToRemove.serverId!, + name: volumeToRemove.name, + serverId: volumeToRemove.serverId!, + region: volumeToRemove.location!, ); await Future.delayed(const Duration(seconds: 10)); @@ -400,33 +405,9 @@ class DigitalOceanServerProvider extends ServerProvider { return result; } - _adapter = ApiAdapter(region: api.region, isWithToken: true); return result; } - @override - Future> 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 Future>> getAvailableLocations() async { @@ -544,7 +525,9 @@ class DigitalOceanServerProvider extends ServerProvider { } @override - Future> getAdditionalPricing() async => + Future> getAdditionalPricing( + final String location, + ) async => GenericResult( success: true, data: AdditionalPricing( @@ -591,6 +574,7 @@ class DigitalOceanServerProvider extends ServerProvider { : null, linuxDevice: 'scsi-0DO_Volume_$volumeName', uuid: rawVolume.id, + location: rawVolume.region.slug, ); volumes.add(volume); } @@ -612,10 +596,11 @@ class DigitalOceanServerProvider extends ServerProvider { @override Future> createVolume( final int gb, + final String location, ) async { 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) { return GenericResult( @@ -687,8 +672,9 @@ class DigitalOceanServerProvider extends ServerProvider { final int serverId, ) async => _adapter.api().attachVolume( - volume.name, - serverId, + name: volume.name, + serverId: serverId, + region: volume.location!, ); @override @@ -696,8 +682,9 @@ class DigitalOceanServerProvider extends ServerProvider { final ServerProviderVolume volume, ) async => _adapter.api().detachVolume( - volume.name, - volume.serverId!, + name: volume.name, + serverId: volume.serverId!, + region: volume.location!, ); @override @@ -714,13 +701,15 @@ class DigitalOceanServerProvider extends ServerProvider { final DiskSize size, ) async => _adapter.api().resizeVolume( - volume.uuid!, - size.gibibyte.toInt(), + uuid: volume.uuid!, + gb: size.gibibyte.toInt(), + region: volume.location!, ); @override Future>> getMetadata( final int serverId, + final String location, ) async { List metadata = []; final result = await _adapter.api().getServers(); @@ -741,7 +730,7 @@ class DigitalOceanServerProvider extends ServerProvider { message: resultVolumes.message, ); } - final resultPricePerGb = await getAdditionalPricing(); + final resultPricePerGb = await getAdditionalPricing(location); if (resultPricePerGb.data == null || !resultPricePerGb.success) { return GenericResult( success: false, diff --git a/lib/logic/providers/server_providers/hetzner.dart b/lib/logic/providers/server_providers/hetzner.dart index 3c5a7232..6d74e421 100644 --- a/lib/logic/providers/server_providers/hetzner.dart +++ b/lib/logic/providers/server_providers/hetzner.dart @@ -19,11 +19,9 @@ import 'package:selfprivacy/utils/password_generator.dart'; class ApiAdapter { ApiAdapter({ - final String? region, final bool isWithToken = true, final String? token, }) : _api = HetznerApi( - region: region, isWithToken: isWithToken, token: token ?? '', ); @@ -31,7 +29,6 @@ class ApiAdapter { HetznerApi api({final bool getInitialized = true}) => getInitialized ? _api : HetznerApi( - region: _api.region, isWithToken: false, ); @@ -41,16 +38,14 @@ class ApiAdapter { class HetznerServerProvider extends ServerProvider { HetznerServerProvider() : _adapter = ApiAdapter(isWithToken: false); HetznerServerProvider.load( - final String? location, final bool isAuthorized, final String? token, ) : _adapter = ApiAdapter( isWithToken: isAuthorized, - region: location, token: token, ); - ApiAdapter _adapter; + final ApiAdapter _adapter; final Currency currency = Currency.fromType(CurrencyType.eur); int? cachedCoreAmount; @@ -87,6 +82,7 @@ class HetznerServerProvider extends ServerProvider { ip: hetznerServer.publicNet.ipv4!.ip, reverseDns: hetznerServer.publicNet.ipv4!.reverseDns, created: hetznerServer.created, + location: hetznerServer.location.name, ); } catch (e) { continue; @@ -176,7 +172,8 @@ class HetznerServerProvider extends ServerProvider { final LaunchInstallationData installationData, ) async { final volumeResult = await _adapter.api().createVolume( - installationData.storageSize.gibibyte.toInt(), + gb: installationData.storageSize.gibibyte.toInt(), + region: installationData.location, ); if (!volumeResult.success || volumeResult.data == null) { @@ -222,6 +219,7 @@ class HetznerServerProvider extends ServerProvider { databasePassword: StringGenerators.dbPassword(), serverApiToken: serverApiToken, customSshKey: installationData.customSshKey, + region: installationData.location, ); if (!serverResult.success || serverResult.data == null) { @@ -419,33 +417,9 @@ class HetznerServerProvider extends ServerProvider { return result; } - // _adapter = ApiAdapter(region: api.region, isWithToken: true); return result; } - @override - Future> 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 Future>> getAvailableLocations() async { @@ -566,8 +540,10 @@ class HetznerServerProvider extends ServerProvider { } @override - Future> getAdditionalPricing() async { - final result = await _adapter.api().getPricing(); + Future> getAdditionalPricing( + final String location, + ) async { + final result = await _adapter.api().getPricing(region: location); if (!result.success || result.data == null) { return GenericResult( @@ -617,12 +593,14 @@ class HetznerServerProvider extends ServerProvider { final volumeServer = rawVolume.serverId; final String volumeName = rawVolume.name; final volumeDevice = rawVolume.linuxDevice; + final volumeLocation = rawVolume.location.name; final volume = ServerProviderVolume( id: volumeId, name: volumeName, sizeByte: volumeSize, serverId: volumeServer, linuxDevice: volumeDevice, + location: volumeLocation, ); volumes.add(volume); } @@ -645,10 +623,11 @@ class HetznerServerProvider extends ServerProvider { @override Future> createVolume( final int gb, + final String location, ) async { 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) { return GenericResult( @@ -702,6 +681,7 @@ class HetznerServerProvider extends ServerProvider { volume.serverId, volume.name, volume.linuxDevice, + HetznerLocation.empty(), ), size, ); @@ -718,6 +698,7 @@ class HetznerServerProvider extends ServerProvider { volume.serverId, volume.name, volume.linuxDevice, + HetznerLocation.empty(), ), serverId, ); @@ -733,6 +714,7 @@ class HetznerServerProvider extends ServerProvider { @override Future>> getMetadata( final int serverId, + final String location, ) async { List metadata = []; final resultServers = await _adapter.api().getServers(); @@ -753,7 +735,7 @@ class HetznerServerProvider extends ServerProvider { message: resultVolumes.message, ); } - final resultPricePerGb = await getAdditionalPricing(); + final resultPricePerGb = await getAdditionalPricing(location); if (resultPricePerGb.data == null || !resultPricePerGb.success) { return GenericResult( success: false, diff --git a/lib/logic/providers/server_providers/server_provider.dart b/lib/logic/providers/server_providers/server_provider.dart index b7258d16..23d4411a 100644 --- a/lib/logic/providers/server_providers/server_provider.dart +++ b/lib/logic/providers/server_providers/server_provider.dart @@ -60,12 +60,6 @@ abstract class ServerProvider { /// server provider respectfully. Future> 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> trySetServerLocation(final String location); - /// Returns all available server locations /// of the authorized user's server provider. Future>> getAvailableLocations(); @@ -92,7 +86,9 @@ abstract class ServerProvider { /// Returns [Price] information map of all additional resources, excluding /// main server type pricing - Future> getAdditionalPricing(); + Future> getAdditionalPricing( + final String location, + ); /// Returns [ServerProviderVolume] of all available volumes /// assigned to the authorized user and attached to active machine. @@ -103,7 +99,10 @@ abstract class ServerProvider { /// Tries to create an empty unattached [ServerProviderVolume]. /// /// If success, returns this volume information. - Future> createVolume(final int gb); + Future> createVolume( + final int gb, + final String location, + ); /// Tries to delete the requested accessible [ServerProviderVolume]. Future> deleteVolume(final ServerProviderVolume volume); @@ -126,10 +125,11 @@ abstract class ServerProvider { /// from any machine. Future> 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. Future>> getMetadata( final int serverId, + final String location, ); /// Returns information about cpu and bandwidth load within the provided diff --git a/lib/logic/providers/server_providers/server_provider_factory.dart b/lib/logic/providers/server_providers/server_provider_factory.dart index 39d6a357..abec5f1f 100644 --- a/lib/logic/providers/server_providers/server_provider_factory.dart +++ b/lib/logic/providers/server_providers/server_provider_factory.dart @@ -17,7 +17,6 @@ class ServerProviderFactory { case ServerProviderType.hetzner: return settings.isAuthorized ? HetznerServerProvider.load( - settings.location, settings.isAuthorized, settings.token, ) @@ -25,7 +24,6 @@ class ServerProviderFactory { case ServerProviderType.digitalOcean: return settings.isAuthorized ? DigitalOceanServerProvider.load( - settings.location, settings.isAuthorized, settings.token, ) diff --git a/lib/ui/pages/setup/initializing/server_type_picker.dart b/lib/ui/pages/setup/initializing/server_type_picker.dart index 25f559b2..53a25619 100644 --- a/lib/ui/pages/setup/initializing/server_type_picker.dart +++ b/lib/ui/pages/setup/initializing/server_type_picker.dart @@ -172,7 +172,7 @@ class SelectTypePage extends StatelessWidget { final Future> serverTypes = serverInstallationCubit.fetchAvailableTypesByLocation(location); final Future prices = - serverInstallationCubit.fetchAvailableAdditionalPricing(); + serverInstallationCubit.fetchAvailableAdditionalPricing(location); return FutureBuilder( future: Future.wait([ serverTypes,