refactor: Server location is no longer a part of server provider controller.

This commit is contained in:
Inex Code 2024-08-14 06:12:47 +03:00
parent 55616c3e69
commit e13b324afa
23 changed files with 187 additions and 140 deletions

View file

@ -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<GenericResult<DigitalOceanVolume?>> createVolume(final int gb) async {
Future<GenericResult<DigitalOceanVolume?>> 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<GenericResult<bool>> attachVolume(
final String name,
final int serverId,
) async {
Future<GenericResult<bool>> 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<GenericResult<bool>> detachVolume(
final String name,
final int serverId,
) async {
Future<GenericResult<bool>> 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<GenericResult<bool>> resizeVolume(
final String uuid,
final int gb,
) async {
Future<GenericResult<bool>> resizeVolume({
required final String uuid,
required final int gb,
required final String region,
}) async {
bool success = false;
final Response resizeVolumeResponse;

View file

@ -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<GenericResult<HetznerPricing?>> getPricing() async {
Future<GenericResult<HetznerPricing?>> 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<GenericResult<HetznerVolume?>> createVolume(final int gb) async {
Future<GenericResult<HetznerVolume?>> createVolume({
required final int gb,
required final String region,
}) async {
Response? createVolumeResponse;
HetznerVolume? volume;
final Dio client = await getClient();

View file

@ -201,7 +201,7 @@ class TokensBloc extends Bloc<TokensEvent, TokensState> {
),
apiToken: event.server.hostingDetails.apiToken,
provider: event.serverProviderCredential.provider,
serverLocation: event.server.hostingDetails.serverLocation,
serverLocation: event.providerServer.location,
serverType: event.server.hostingDetails.serverType,
),
);

View file

@ -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<VolumesEvent, VolumesState> {
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<NavigationService>().showSnackBar('server.pricing_error'.tr());
return price;
}
price = pricingResult.data!.perVolumeGb;
return price;
} else {
await Future.delayed(Duration.zero);
getIt<NavigationService>().showSnackBar('server.pricing_error'.tr());
return price;
}
price = pricingResult.data!.perVolumeGb;
return price;
}
Future<void> _loadState(

View file

@ -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({

View file

@ -45,17 +45,26 @@ class ServerDetailsCubit
Future<List<ServerMetadataEntity>> get _metadata async {
final List<ServerMetadataEntity> data = [];
final Server? server = getIt<ResourcesModel>().servers.firstOrNull;
if (server == null) {
return data;
}
final serverProviderApi = ProvidersController.currentServerProvider;
final dnsProviderApi = ProvidersController.currentDnsProvider;
if (serverProviderApi?.isAuthorized ?? false) {
final serverId = getIt<ResourcesModel>().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<ResourcesModel>().servers.first;
data.add(
ServerMetadataEntity(
type: MetadataType.other,
@ -74,7 +83,6 @@ class ServerDetailsCubit
),
);
} else {
final Server server = getIt<ResourcesModel>().servers.first;
data.add(
ServerMetadataEntity(
trId: 'server.dns_provider',

View file

@ -168,10 +168,12 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
return apiResult.data;
}
Future<AdditionalPricing?> fetchAvailableAdditionalPricing() async {
Future<AdditionalPricing?> 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<NavigationService>().showSnackBar('server.pricing_error'.tr());
return prices;
@ -202,8 +204,11 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
}
Future<void> 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<ServerInstallationState> {
emit(
(state as ServerInstallationNotFinished).copyWith(
serverTypeIdentificator: serverType.identifier,
serverLocation: serverType.location.identifier,
),
);
}
@ -335,6 +341,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
successCallback: onCreateServerSuccess,
storageSize: initialStorage,
customSshKey: (state as ServerInstallationNotFinished).customSshKey,
location: state.serverLocation!,
);
final result =
@ -794,12 +801,14 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
),
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<ServerInstallationState> {
?.getServerType(state.serverDetails!.id);
if (serverType != null) {
await repository.saveServerType(serverType.data!);
await ProvidersController.currentServerProvider!
.trySetServerLocation(serverType.data!.location.identifier);
}
} else {
serverType = null;

View file

@ -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(

View file

@ -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<Object?> 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<Object?> 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<Object?> 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!,

View file

@ -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)

View file

@ -83,13 +83,14 @@ class ServerProviderVolumeAdapter extends TypeAdapter<ServerProviderVolume> {
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<ServerProviderVolume> {
..writeByte(5)
..write(obj.linuxDevice)
..writeByte(6)
..write(obj.uuid);
..write(obj.uuid)
..writeByte(7)
..write(obj.location);
}
@override

View file

@ -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<String, dynamic> json) =>
_$DigitalOceanVolumeFromJson(json);
}

View file

@ -14,6 +14,7 @@ DigitalOceanVolume _$DigitalOceanVolumeFromJson(Map<String, dynamic> json) =>
(json['droplet_ids'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList(),
DigitalOceanLocation.fromJson(json['region'] as Map<String, dynamic>),
);
Map<String, dynamic> _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) =>
@ -22,6 +23,7 @@ Map<String, dynamic> _$DigitalOceanVolumeToJson(DigitalOceanVolume instance) =>
'name': instance.name,
'droplet_ids': instance.dropletIds,
'size_gigabytes': instance.sizeGigabytes,
'region': instance.region,
};
DigitalOceanLocation _$DigitalOceanLocationFromJson(

View file

@ -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<String, dynamic> json) =>
_$HetznerVolumeFromJson(json);
}

View file

@ -139,6 +139,7 @@ HetznerVolume _$HetznerVolumeFromJson(Map<String, dynamic> json) =>
(json['serverId'] as num?)?.toInt(),
json['name'] as String,
json['linux_device'] as String?,
HetznerLocation.fromJson(json['location'] as Map<String, dynamic>),
);
Map<String, dynamic> _$HetznerVolumeToJson(HetznerVolume instance) =>
@ -148,4 +149,5 @@ Map<String, dynamic> _$HetznerVolumeToJson(HetznerVolume instance) =>
'serverId': instance.serverId,
'name': instance.name,
'linux_device': instance.linuxDevice,
'location': instance.location,
};

View file

@ -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;
}

View file

@ -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,
});

View file

@ -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 {

View file

@ -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<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
Future<GenericResult<List<ServerProviderLocation>>>
getAvailableLocations() async {
@ -544,7 +525,9 @@ class DigitalOceanServerProvider extends ServerProvider {
}
@override
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing() async =>
Future<GenericResult<AdditionalPricing?>> 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<GenericResult<ServerProviderVolume?>> 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<GenericResult<List<ServerMetadataEntity>>> getMetadata(
final int serverId,
final String location,
) async {
List<ServerMetadataEntity> 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,

View file

@ -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<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
Future<GenericResult<List<ServerProviderLocation>>>
getAvailableLocations() async {
@ -566,8 +540,10 @@ class HetznerServerProvider extends ServerProvider {
}
@override
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing() async {
final result = await _adapter.api().getPricing();
Future<GenericResult<AdditionalPricing?>> 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<GenericResult<ServerProviderVolume?>> 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<GenericResult<List<ServerMetadataEntity>>> getMetadata(
final int serverId,
final String location,
) async {
List<ServerMetadataEntity> 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,

View file

@ -60,12 +60,6 @@ abstract class ServerProvider {
/// server provider respectfully.
Future<GenericResult<bool>> tryInitApiByToken(final String token);
/// Tries to assign the location shortcode for future usage.
///
/// If API wasn't initialized with token by [tryInitApiByToken] beforehand,
/// returns 'Not authorized!' error.
Future<GenericResult<bool>> trySetServerLocation(final String location);
/// Returns all available server locations
/// of the authorized user's server provider.
Future<GenericResult<List<ServerProviderLocation>>> getAvailableLocations();
@ -92,7 +86,9 @@ abstract class ServerProvider {
/// Returns [Price] information map of all additional resources, excluding
/// main server type pricing
Future<GenericResult<AdditionalPricing?>> getAdditionalPricing();
Future<GenericResult<AdditionalPricing?>> 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<GenericResult<ServerProviderVolume?>> createVolume(final int gb);
Future<GenericResult<ServerProviderVolume?>> createVolume(
final int gb,
final String location,
);
/// Tries to delete the requested accessible [ServerProviderVolume].
Future<GenericResult<void>> deleteVolume(final ServerProviderVolume volume);
@ -126,10 +125,11 @@ abstract class ServerProvider {
/// from any machine.
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.
Future<GenericResult<List<ServerMetadataEntity>>> getMetadata(
final int serverId,
final String location,
);
/// Returns information about cpu and bandwidth load within the provided

View file

@ -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,
)

View file

@ -172,7 +172,7 @@ class SelectTypePage extends StatelessWidget {
final Future<List<ServerType>> serverTypes =
serverInstallationCubit.fetchAvailableTypesByLocation(location);
final Future<AdditionalPricing?> prices =
serverInstallationCubit.fetchAvailableAdditionalPricing();
serverInstallationCubit.fetchAvailableAdditionalPricing(location);
return FutureBuilder(
future: Future.wait([
serverTypes,