From 8fe0de0c9e059bf2f42b5d7fb8bdaf7348c6c44c Mon Sep 17 00:00:00 2001 From: Inex Code Date: Tue, 30 Jul 2024 04:15:17 +0300 Subject: [PATCH] feat: Allow skipping the server provider token when recovering --- assets/translations/en.json | 6 +- lib/logic/bloc/volumes/volumes_bloc.dart | 31 ++++--- lib/logic/cubit/metrics/metrics_cubit.dart | 2 + .../cubit/metrics/metrics_repository.dart | 9 +- lib/logic/cubit/metrics/metrics_state.dart | 9 ++ .../server_detailed_info_cubit.dart | 65 ++++++++------ .../server_installation_cubit.dart | 85 +++++++++++-------- .../server_installation_repository.dart | 56 +++++++++--- .../server_installation_state.dart | 12 +-- .../get_it/api_connection_repository.dart | 12 +-- lib/logic/models/disk_status.dart | 4 +- .../backups_providers/backblaze.dart | 3 + .../backups_providers/backups_provider.dart | 2 + .../providers/dns_providers/cloudflare.dart | 5 +- lib/logic/providers/dns_providers/desec.dart | 5 +- .../dns_providers/digital_ocean_dns.dart | 5 +- .../providers/dns_providers/dns_provider.dart | 2 + .../server_providers/digital_ocean.dart | 5 +- .../providers/server_providers/hetzner.dart | 5 +- .../server_providers/server_provider.dart | 2 + lib/ui/pages/server_details/charts/chart.dart | 71 ++++++++++------ .../server_details/logs/logs_screen.dart | 16 ++-- .../server_details/server_details_screen.dart | 69 +++++++-------- lib/ui/pages/server_details/text_details.dart | 2 +- .../recovery_server_provider_connected.dart | 34 +++++++- 25 files changed, 341 insertions(+), 176 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 9cc5b883..9f5fa0bb 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -138,7 +138,8 @@ "cpu_title": "CPU Usage", "network_title": "Network Usage", "in": "In", - "out": "Out" + "out": "Out", + "unsupported": "You can't view resource usage charts without the server provider token." }, "server": { "card_title": "Server", @@ -511,6 +512,9 @@ "provider_connected": "Connect to {}", "provider_connected_description": "Enter your token with access to {}:", "provider_connected_placeholder": "{} token", + "login_later": "Log in later", + "server_provider_unknown": "Unknown server provider", + "server_provider_unknown_description": "Your server provider is not recognized. You can continue without entering its API token.", "confirm_server": "Confirm server", "confirm_server_description": "Found your server! Confirm it is the right one:", "confirm_server_accept": "Yes! That's it", diff --git a/lib/logic/bloc/volumes/volumes_bloc.dart b/lib/logic/bloc/volumes/volumes_bloc.dart index 95e753a9..eb654040 100644 --- a/lib/logic/bloc/volumes/volumes_bloc.dart +++ b/lib/logic/bloc/volumes/volumes_bloc.dart @@ -5,6 +5,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/disk_status.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; @@ -74,7 +75,7 @@ class VolumesBloc extends Bloc { bool isLoaded = false; Future getPricePerGb() async { - if (ProvidersController.currentServerProvider == null) { + if (!(ProvidersController.currentServerProvider?.isAuthorized ?? false)) { return null; } Price? price; @@ -92,33 +93,36 @@ class VolumesBloc extends Bloc { final VolumesServerLoaded event, final Emitter emit, ) async { - if (ProvidersController.currentServerProvider == null) { + if (getIt().currentConnectionStatus == + ConnectionStatus.nonexistent) { return; } - emit(VolumesLoading()); - final volumesResult = - await ProvidersController.currentServerProvider!.getVolumes(); + late final GenericResult>? volumesResult; - if (!volumesResult.success || volumesResult.data.isEmpty) { - emit(VolumesInitial()); - return; + if (ProvidersController.currentServerProvider?.isAuthorized ?? false) { + volumesResult = + await ProvidersController.currentServerProvider?.getVolumes(); + } else { + volumesResult = null; } final serverVolumes = getIt().apiData.volumes.data; - if (serverVolumes == null) { + if (serverVolumes == null && + volumesResult != null && + volumesResult.data.isNotEmpty) { emit(VolumesLoading(providerVolumes: volumesResult.data)); return; - } else { + } else if (serverVolumes != null) { emit( VolumesLoaded( diskStatus: DiskStatus.fromVolumes( serverVolumes, - volumesResult.data, + volumesResult?.data ?? [], ), - providerVolumes: volumesResult.data, + providerVolumes: volumesResult?.data ?? [], serverVolumesHashCode: Object.hashAll(serverVolumes), ), ); @@ -186,6 +190,9 @@ class VolumesBloc extends Bloc { if (state is! VolumesLoaded) { return; } + if (ProvidersController.currentServerProvider?.isAuthorized ?? false) { + return; + } getIt().showSnackBar( 'storage.extending_volume_started'.tr(), ); diff --git a/lib/logic/cubit/metrics/metrics_cubit.dart b/lib/logic/cubit/metrics/metrics_cubit.dart index 290c9b55..7d6394c1 100644 --- a/lib/logic/cubit/metrics/metrics_cubit.dart +++ b/lib/logic/cubit/metrics/metrics_cubit.dart @@ -52,6 +52,8 @@ class MetricsCubit extends Cubit { Duration(seconds: state.period.stepPeriodInSeconds), () => load(state.period), ); + } on MetricsUnsupportedException { + emit(MetricsUnsupported(period)); } } } diff --git a/lib/logic/cubit/metrics/metrics_repository.dart b/lib/logic/cubit/metrics/metrics_repository.dart index 561d75ef..53a43ab3 100644 --- a/lib/logic/cubit/metrics/metrics_repository.dart +++ b/lib/logic/cubit/metrics/metrics_repository.dart @@ -10,10 +10,15 @@ class MetricsLoadException implements Exception { final String message; } +class MetricsUnsupportedException implements Exception { + MetricsUnsupportedException(this.message); + final String message; +} + class MetricsRepository { Future getMetrics(final Period period) async { - if (ProvidersController.currentServerProvider == null) { - throw MetricsLoadException('Server Provider data is null'); + if (!(ProvidersController.currentServerProvider?.isAuthorized ?? false)) { + throw MetricsUnsupportedException('Server Provider data is null'); } final DateTime end = DateTime.now(); diff --git a/lib/logic/cubit/metrics/metrics_state.dart b/lib/logic/cubit/metrics/metrics_state.dart index b27546ce..f1bd78dd 100644 --- a/lib/logic/cubit/metrics/metrics_state.dart +++ b/lib/logic/cubit/metrics/metrics_state.dart @@ -29,3 +29,12 @@ class MetricsLoaded extends MetricsState { @override List get props => [period, metrics]; } + +class MetricsUnsupported extends MetricsState { + const MetricsUnsupported(this.period); + @override + final Period period; + + @override + List get props => [period]; +} 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 09b9e845..9a2b98db 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 @@ -4,6 +4,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart'; import 'package:selfprivacy/logic/get_it/resources_model.dart'; import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; +import 'package:selfprivacy/logic/models/hive/server.dart'; import 'package:selfprivacy/logic/models/server_metadata.dart'; import 'package:selfprivacy/logic/models/ssh_settings.dart'; import 'package:selfprivacy/logic/models/system_settings.dart'; @@ -36,47 +37,63 @@ class ServerDetailsCubit sshSettings: settings.sshSettings, ), ); + if (state.metadata.isEmpty) { + check(); + } } Future> get _metadata async { - List data = []; + final List data = []; final serverProviderApi = ProvidersController.currentServerProvider; final dnsProviderApi = ProvidersController.currentDnsProvider; - if (serverProviderApi != null && dnsProviderApi != null) { + if (serverProviderApi?.isAuthorized ?? false) { final serverId = getIt().serverDetails?.id ?? 0; - final metadataResult = await serverProviderApi.getMetadata(serverId); - metadataResult.data.add( + final metadataResult = await serverProviderApi?.getMetadata(serverId); + + data.addAll(metadataResult?.data ?? []); + } + + if (serverProviderApi == null || !serverProviderApi.isAuthorized) { + final Server server = getIt().servers.first; + data.add( + ServerMetadataEntity( + type: MetadataType.other, + trId: 'server.server_provider', + value: server.hostingDetails.provider.displayName, + ), + ); + } + + if (dnsProviderApi != null && dnsProviderApi.isAuthorized) { + data.add( ServerMetadataEntity( trId: 'server.dns_provider', value: dnsProviderApi.type.displayName, type: MetadataType.other, ), ); - - data = metadataResult.data; + } else { + final Server server = getIt().servers.first; + data.add( + ServerMetadataEntity( + trId: 'server.dns_provider', + value: server.domain.provider.displayName, + type: MetadataType.other, + ), + ); } return data; } void check() async { - final bool isReadyToCheck = getIt().serverDetails != null; - try { - if (isReadyToCheck) { - emit(const ServerDetailsLoading()); - final List metadata = await _metadata; - emit( - state.copyWith( - metadata: metadata, - ), - ); - } else { - emit(const ServerDetailsNotReady()); - } - } on StateError { - print('Tried to emit server info state when cubit is closed'); - } + final List metadata = await _metadata; + emit( + state.copyWith( + metadata: metadata, + ), + ); } @override @@ -85,9 +102,7 @@ class ServerDetailsCubit } @override - void load() async { - check(); - } + void load() async {} @override Future close() { diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index cd4e14ba..ae67c395 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -552,6 +552,34 @@ class ServerInstallationCubit extends Cubit { ); } + void selectRecoveryMethod(final ServerRecoveryMethods method) { + final ServerInstallationRecovery dataState = + state as ServerInstallationRecovery; + switch (method) { + case ServerRecoveryMethods.newDeviceKey: + emit( + dataState.copyWith( + currentStep: RecoveryStep.newDeviceKey, + ), + ); + break; + case ServerRecoveryMethods.recoveryKey: + emit( + dataState.copyWith( + currentStep: RecoveryStep.recoveryKey, + ), + ); + break; + case ServerRecoveryMethods.oldToken: + emit( + dataState.copyWith( + currentStep: RecoveryStep.oldToken, + ), + ); + break; + } + } + void tryToRecover( final String token, final ServerRecoveryMethods method, @@ -596,8 +624,7 @@ class ServerInstallationCubit extends Cubit { isWithToken: true, overrideDomain: serverDomain.domainName, ).getDnsProviderType(); - if (serverProvider == ServerProviderType.unknown || - dnsProvider == DnsProviderType.unknown) { + if (dnsProvider == DnsProviderType.unknown) { getIt() .showSnackBar('recovering.generic_error'.tr()); return; @@ -672,32 +699,13 @@ class ServerInstallationCubit extends Cubit { } } - void selectRecoveryMethod(final ServerRecoveryMethods method) { - final ServerInstallationRecovery dataState = - state as ServerInstallationRecovery; - switch (method) { - case ServerRecoveryMethods.newDeviceKey: - emit( - dataState.copyWith( - currentStep: RecoveryStep.newDeviceKey, - ), - ); - break; - case ServerRecoveryMethods.recoveryKey: - emit( - dataState.copyWith( - currentStep: RecoveryStep.recoveryKey, - ), - ); - break; - case ServerRecoveryMethods.oldToken: - emit( - dataState.copyWith( - currentStep: RecoveryStep.oldToken, - ), - ); - break; - } + Future skipSettingServerProviderKey() async { + emit( + (state as ServerInstallationRecovery).copyWith( + providerApiToken: null, + currentStep: RecoveryStep.dnsProviderToken, + ), + ); } Future> getAvailableServers() async { @@ -747,7 +755,7 @@ class ServerInstallationCubit extends Cubit { linuxDevice: '', ), apiToken: dataState.serverDetails!.apiToken, - provider: ServerProviderType.hetzner, + provider: dataState.serverDetails!.provider, ); await repository.saveDomain(serverDomain); await repository.saveServerDetails(serverDetails); @@ -804,16 +812,23 @@ class ServerInstallationCubit extends Cubit { await repository.saveIsServerRebootedFirstTime(true); await repository.saveIsServerRebootedSecondTime(true); await repository.saveIsRecoveringServer(false); - final serverType = await ProvidersController.currentServerProvider! - .getServerType(state.serverDetails!.id); - await repository.saveServerType(serverType.data!); - await ProvidersController.currentServerProvider! - .trySetServerLocation(serverType.data!.location.identifier); + late final GenericResult? serverType; + if (ProvidersController.currentServerProvider?.isAuthorized ?? false) { + serverType = await ProvidersController.currentServerProvider + ?.getServerType(state.serverDetails!.id); + if (serverType != null) { + await repository.saveServerType(serverType.data!); + await ProvidersController.currentServerProvider! + .trySetServerLocation(serverType.data!.location.identifier); + } + } else { + serverType = null; + } await repository.saveHasFinalChecked(true); final ServerInstallationRecovery updatedState = (state as ServerInstallationRecovery).copyWith( backblazeCredential: backblazeCredential, - serverTypeIdentificator: serverType.data!.identifier, + serverTypeIdentificator: serverType?.data?.identifier, ); emit(updatedState.finish()); getIt().init(); diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index c823c13a..3bb52ea5 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -95,8 +95,8 @@ class ServerInstallationRepository { // We have a server set up, so we load it TlsOptions.verifyCertificate = true; return ServerInstallationFinished( - providerApiToken: providerApiToken!, - serverTypeIdentificator: serverTypeIdentificator!, + providerApiToken: providerApiToken, + serverTypeIdentificator: serverTypeIdentificator, dnsApiToken: dnsApiToken!, serverDomain: serverDomain!, backblazeCredential: backblazeCredential!, @@ -105,6 +105,30 @@ class ServerInstallationRepository { } } + // If wizard data has info on the providers, init them + if (wizardData.serverProviderType != null && + wizardData.serverProviderType != ServerProviderType.unknown) { + ProvidersController.initServerProvider( + ServerProviderSettings( + provider: wizardData.serverProviderType!, + isAuthorized: wizardData.serverProviderKey != null, + location: wizardData.serverLocation, + token: wizardData.serverProviderKey, + ), + ); + } + + if (wizardData.dnsProviderType != null && + wizardData.dnsProviderType != DnsProviderType.unknown) { + ProvidersController.initDnsProvider( + DnsProviderSettings( + provider: wizardData.dnsProviderType!, + isAuthorized: wizardData.dnsProviderKey != null, + token: wizardData.dnsProviderKey, + ), + ); + } + if (wizardData.isRecoveringServer && wizardData.serverDomain != null) { return ServerInstallationRecovery( providerApiToken: wizardData.serverProviderKey, @@ -548,17 +572,23 @@ class ServerInstallationRepository { domain: wizardData.serverDomain!, ), ); - await getIt().associateServerWithToken( - wizardData.serverDetails!.id, - wizardData.serverProviderKey!, - ); - await getIt().associateDomainWithToken( - wizardData.serverDomain!.domainName, - wizardData.dnsProviderKey!, - ); - await getIt().addBackupsCredential( - wizardData.backupsCredential!, - ); + if (wizardData.serverProviderKey != null) { + await getIt().associateServerWithToken( + wizardData.serverDetails!.id, + wizardData.serverProviderKey!, + ); + } + if (wizardData.dnsProviderKey != null) { + await getIt().associateDomainWithToken( + wizardData.serverDomain!.domainName, + wizardData.dnsProviderKey!, + ); + } + if (wizardData.backupsCredential != null) { + await getIt().addBackupsCredential( + wizardData.backupsCredential!, + ); + } await getIt().clearServerInstallation(); } } diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index f19ba930..ec4a7aa1 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -226,8 +226,8 @@ class ServerInstallationNotFinished extends ServerInstallationState { ); ServerInstallationFinished finish() => ServerInstallationFinished( - providerApiToken: providerApiToken!, - serverTypeIdentificator: serverTypeIdentificator!, + providerApiToken: providerApiToken, + serverTypeIdentificator: serverTypeIdentificator, dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, @@ -257,12 +257,12 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished { class ServerInstallationFinished extends ServerInstallationState { const ServerInstallationFinished({ - required String super.providerApiToken, - required String super.serverTypeIdentificator, required String super.dnsApiToken, required BackupsCredential super.backblazeCredential, required ServerDomain super.serverDomain, required ServerHostingDetails super.serverDetails, + super.providerApiToken, + super.serverTypeIdentificator, }) : super( rootUser: null, isServerStarted: true, @@ -367,8 +367,8 @@ class ServerInstallationRecovery extends ServerInstallationState { ); ServerInstallationFinished finish() => ServerInstallationFinished( - providerApiToken: providerApiToken!, - serverTypeIdentificator: serverTypeIdentificator!, + providerApiToken: providerApiToken, + serverTypeIdentificator: serverTypeIdentificator, dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, diff --git a/lib/logic/get_it/api_connection_repository.dart b/lib/logic/get_it/api_connection_repository.dart index 7cce96ae..99f5d9b8 100644 --- a/lib/logic/get_it/api_connection_repository.dart +++ b/lib/logic/get_it/api_connection_repository.dart @@ -320,20 +320,20 @@ class ApiConnectionRepository { await _apiData.serverJobs .refetchData(version, () => _dataStream.add(_apiData)); } - await _apiData.backups - .refetchData(version, () => _dataStream.add(_apiData)); - await _apiData.backupConfig - .refetchData(version, () => _dataStream.add(_apiData)); await _apiData.services .refetchData(version, () => _dataStream.add(_apiData)); + await _apiData.users.refetchData(version, () => _dataStream.add(_apiData)); await _apiData.volumes .refetchData(version, () => _dataStream.add(_apiData)); + await _apiData.settings + .refetchData(version, () => _dataStream.add(_apiData)); await _apiData.recoveryKeyStatus .refetchData(version, () => _dataStream.add(_apiData)); await _apiData.devices .refetchData(version, () => _dataStream.add(_apiData)); - await _apiData.users.refetchData(version, () => _dataStream.add(_apiData)); - await _apiData.settings + await _apiData.backupConfig + .refetchData(version, () => _dataStream.add(_apiData)); + await _apiData.backups .refetchData(version, () => _dataStream.add(_apiData)); } diff --git a/lib/logic/models/disk_status.dart b/lib/logic/models/disk_status.dart index 6708cf12..5adc80b3 100644 --- a/lib/logic/models/disk_status.dart +++ b/lib/logic/models/disk_status.dart @@ -12,7 +12,7 @@ class DiskVolume { this.providerVolume, }); - DiskVolume.fromServerDiscVolume( + DiskVolume.fromServerAndDiskVolume( final ServerDiskVolume volume, final ServerProviderVolume? providerVolume, ) : this( @@ -104,7 +104,7 @@ class DiskStatus { } final DiskVolume diskVolume = - DiskVolume.fromServerDiscVolume(volume, providerVolume); + DiskVolume.fromServerAndDiskVolume(volume, providerVolume); return diskVolume; }).toList(); diff --git a/lib/logic/providers/backups_providers/backblaze.dart b/lib/logic/providers/backups_providers/backblaze.dart index b640baf3..14ea427b 100644 --- a/lib/logic/providers/backups_providers/backblaze.dart +++ b/lib/logic/providers/backups_providers/backblaze.dart @@ -36,6 +36,9 @@ class BackblazeBackupsProvider extends BackupsProvider { final ApiAdapter _adapter; + @override + bool get isAuthorized => _adapter.api().isWithToken; + @override BackupsProviderType get type => BackupsProviderType.backblaze; diff --git a/lib/logic/providers/backups_providers/backups_provider.dart b/lib/logic/providers/backups_providers/backups_provider.dart index 0503428e..08a3bc7a 100644 --- a/lib/logic/providers/backups_providers/backups_provider.dart +++ b/lib/logic/providers/backups_providers/backups_provider.dart @@ -19,6 +19,8 @@ abstract class BackupsProvider { /// provider implements [BackupsProvider] interface. BackupsProviderType get type; + bool get isAuthorized; + /// Returns a full url to a guide on how to setup /// backups provider String get howToRegister; diff --git a/lib/logic/providers/dns_providers/cloudflare.dart b/lib/logic/providers/dns_providers/cloudflare.dart index 68695ca3..c8379493 100644 --- a/lib/logic/providers/dns_providers/cloudflare.dart +++ b/lib/logic/providers/dns_providers/cloudflare.dart @@ -27,7 +27,7 @@ class ApiAdapter { } class CloudflareDnsProvider extends DnsProvider { - CloudflareDnsProvider() : _adapter = ApiAdapter(); + CloudflareDnsProvider() : _adapter = ApiAdapter(isWithToken: false); CloudflareDnsProvider.load( final bool isAuthorized, final String? token, @@ -38,6 +38,9 @@ class CloudflareDnsProvider extends DnsProvider { ApiAdapter _adapter; + @override + bool get isAuthorized => _adapter.api().isWithToken; + @override DnsProviderType get type => DnsProviderType.cloudflare; diff --git a/lib/logic/providers/dns_providers/desec.dart b/lib/logic/providers/dns_providers/desec.dart index e619eaf1..7a93ad40 100644 --- a/lib/logic/providers/dns_providers/desec.dart +++ b/lib/logic/providers/dns_providers/desec.dart @@ -21,7 +21,7 @@ class ApiAdapter { } class DesecDnsProvider extends DnsProvider { - DesecDnsProvider() : _adapter = ApiAdapter(); + DesecDnsProvider() : _adapter = ApiAdapter(isWithToken: false); DesecDnsProvider.load( final bool isAuthorized, final String? token, @@ -32,6 +32,9 @@ class DesecDnsProvider extends DnsProvider { final ApiAdapter _adapter; + @override + bool get isAuthorized => _adapter.api().isWithToken; + @override DnsProviderType get type => DnsProviderType.desec; diff --git a/lib/logic/providers/dns_providers/digital_ocean_dns.dart b/lib/logic/providers/dns_providers/digital_ocean_dns.dart index c34f86e7..3f2b2346 100644 --- a/lib/logic/providers/dns_providers/digital_ocean_dns.dart +++ b/lib/logic/providers/dns_providers/digital_ocean_dns.dart @@ -21,7 +21,7 @@ class ApiAdapter { } class DigitalOceanDnsProvider extends DnsProvider { - DigitalOceanDnsProvider() : _adapter = ApiAdapter(); + DigitalOceanDnsProvider() : _adapter = ApiAdapter(isWithToken: false); DigitalOceanDnsProvider.load( final bool isAuthorized, final String? token, @@ -32,6 +32,9 @@ class DigitalOceanDnsProvider extends DnsProvider { final ApiAdapter _adapter; + @override + bool get isAuthorized => _adapter.api().isWithToken; + @override DnsProviderType get type => DnsProviderType.digitalOcean; diff --git a/lib/logic/providers/dns_providers/dns_provider.dart b/lib/logic/providers/dns_providers/dns_provider.dart index 03e01657..17053eb9 100644 --- a/lib/logic/providers/dns_providers/dns_provider.dart +++ b/lib/logic/providers/dns_providers/dns_provider.dart @@ -8,6 +8,8 @@ abstract class DnsProvider { /// provider implements [DnsProvider] interface. DnsProviderType get type; + bool get isAuthorized; + /// Returns a full url to a guide on how to setup /// DNS provider nameservers String get howToRegister; diff --git a/lib/logic/providers/server_providers/digital_ocean.dart b/lib/logic/providers/server_providers/digital_ocean.dart index 99123750..356b8365 100644 --- a/lib/logic/providers/server_providers/digital_ocean.dart +++ b/lib/logic/providers/server_providers/digital_ocean.dart @@ -39,7 +39,7 @@ class ApiAdapter { } class DigitalOceanServerProvider extends ServerProvider { - DigitalOceanServerProvider() : _adapter = ApiAdapter(); + DigitalOceanServerProvider() : _adapter = ApiAdapter(isWithToken: false); DigitalOceanServerProvider.load( final String? location, final bool isAuthorized, @@ -53,6 +53,9 @@ class DigitalOceanServerProvider extends ServerProvider { ApiAdapter _adapter; final Currency currency = Currency.fromType(CurrencyType.usd); + @override + bool get isAuthorized => _adapter.api().isWithToken; + @override ServerProviderType get type => ServerProviderType.digitalOcean; diff --git a/lib/logic/providers/server_providers/hetzner.dart b/lib/logic/providers/server_providers/hetzner.dart index 26e960cc..10f2c8e0 100644 --- a/lib/logic/providers/server_providers/hetzner.dart +++ b/lib/logic/providers/server_providers/hetzner.dart @@ -39,7 +39,7 @@ class ApiAdapter { } class HetznerServerProvider extends ServerProvider { - HetznerServerProvider() : _adapter = ApiAdapter(); + HetznerServerProvider() : _adapter = ApiAdapter(isWithToken: false); HetznerServerProvider.load( final String? location, final bool isAuthorized, @@ -54,6 +54,9 @@ class HetznerServerProvider extends ServerProvider { final Currency currency = Currency.fromType(CurrencyType.eur); int? cachedCoreAmount; + @override + bool get isAuthorized => _adapter.api().isWithToken; + @override ServerProviderType get type => ServerProviderType.hetzner; diff --git a/lib/logic/providers/server_providers/server_provider.dart b/lib/logic/providers/server_providers/server_provider.dart index ab52fe47..b7258d16 100644 --- a/lib/logic/providers/server_providers/server_provider.dart +++ b/lib/logic/providers/server_providers/server_provider.dart @@ -18,6 +18,8 @@ abstract class ServerProvider { /// provider implements [ServerProvider] interface. ServerProviderType get type; + bool get isAuthorized; + /// Returns [ServerBasicInfo] of all available machines /// assigned to the authorized user. /// diff --git a/lib/ui/pages/server_details/charts/chart.dart b/lib/ui/pages/server_details/charts/chart.dart index ffa2e367..6066aa32 100644 --- a/lib/ui/pages/server_details/charts/chart.dart +++ b/lib/ui/pages/server_details/charts/chart.dart @@ -100,37 +100,58 @@ class _Chart extends StatelessWidget { ), ), ]; + } else if (state is MetricsUnsupported) { + charts = [ + FilledCard( + clipped: false, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'resource_chart.unsupported'.tr(), + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + ), + ]; } else { throw 'wrong state'; } return Column( children: [ - SegmentedButtons( - isSelected: [ - period == Period.month, - period == Period.day, - period == Period.hour, - ], - onPressed: (final index) { - switch (index) { - case 0: - cubit.changePeriod(Period.month); - break; - case 1: - cubit.changePeriod(Period.day); - break; - case 2: - cubit.changePeriod(Period.hour); - break; - } - }, - titles: [ - 'resource_chart.month'.tr(), - 'resource_chart.day'.tr(), - 'resource_chart.hour'.tr(), - ], - ), + if (state is! MetricsUnsupported) + SegmentedButtons( + isSelected: [ + period == Period.month, + period == Period.day, + period == Period.hour, + ], + onPressed: (final index) { + switch (index) { + case 0: + cubit.changePeriod(Period.month); + break; + case 1: + cubit.changePeriod(Period.day); + break; + case 2: + cubit.changePeriod(Period.hour); + break; + } + }, + titles: [ + 'resource_chart.month'.tr(), + 'resource_chart.day'.tr(), + 'resource_chart.hour'.tr(), + ], + ), const SizedBox(height: 8), ...charts, ], diff --git a/lib/ui/pages/server_details/logs/logs_screen.dart b/lib/ui/pages/server_details/logs/logs_screen.dart index 41323ef4..94db160e 100644 --- a/lib/ui/pages/server_details/logs/logs_screen.dart +++ b/lib/ui/pages/server_details/logs/logs_screen.dart @@ -84,7 +84,11 @@ class _ServerLogsScreenState extends State { const Key centerKey = ValueKey('server-logs-center-key'); return Scaffold( appBar: AppBar( - title: Text(widget.serviceId == null ? 'server.logs'.tr() : 'service_page.logs'.tr()), + title: Text( + widget.serviceId == null + ? 'server.logs'.tr() + : 'service_page.logs'.tr(), + ), ), endDrawer: BlocBuilder( builder: (final context, final state) { @@ -160,10 +164,12 @@ class _ServerLogsScreenState extends State { ], ); } else if (state is ServerLogsError) { - return EmptyPagePlaceholder( - title: 'basis.error'.tr(), - iconData: Icons.error_outline, - description: state.error.toString(), + return Center( + child: EmptyPagePlaceholder( + title: 'basis.error'.tr(), + iconData: Icons.error_outline, + description: state.error.toString(), + ), ); } return Center(child: Text('server.no_logs'.tr())); diff --git a/lib/ui/pages/server_details/server_details_screen.dart b/lib/ui/pages/server_details/server_details_screen.dart index a0d28ad7..064f0740 100644 --- a/lib/ui/pages/server_details/server_details_screen.dart +++ b/lib/ui/pages/server_details/server_details_screen.dart @@ -63,42 +63,39 @@ class _ServerDetailsScreenState extends State ); } - return BlocProvider( - create: (final context) => context.read()..check(), - child: BrandHeroScreen( - hasFlashButton: true, - heroIcon: BrandIcons.server, - heroTitle: 'server.card_title'.tr(), - heroSubtitle: 'server.description'.tr(), - children: [ - StorageCard( - diskStatus: context.watch().state.diskStatus, - ), - const SizedBox(height: 16), - ListTile( - title: Text('server.settings'.tr()), - leading: const Icon(BrandIcons.settings), - onTap: () => context.pushRoute(const ServerSettingsRoute()), - ), - ListTile( - title: Text('server.logs'.tr()), - leading: const Icon(Icons.manage_search_outlined), - onTap: () => context.pushRoute(ServerLogsRoute()), - ), - const Divider(height: 32), - Text( - 'server.resource_usage'.tr(), - style: Theme.of(context).textTheme.titleLarge, - ), - const SizedBox(height: 8), - BlocProvider( - create: (final context) => MetricsCubit()..restart(), - child: _Chart(), - ), - const SizedBox(height: 8), - _TextDetails(), - ], - ), + return BrandHeroScreen( + hasFlashButton: true, + heroIcon: BrandIcons.server, + heroTitle: 'server.card_title'.tr(), + heroSubtitle: 'server.description'.tr(), + children: [ + StorageCard( + diskStatus: context.watch().state.diskStatus, + ), + const SizedBox(height: 16), + ListTile( + title: Text('server.settings'.tr()), + leading: const Icon(BrandIcons.settings), + onTap: () => context.pushRoute(const ServerSettingsRoute()), + ), + ListTile( + title: Text('server.logs'.tr()), + leading: const Icon(Icons.manage_search_outlined), + onTap: () => context.pushRoute(ServerLogsRoute()), + ), + const Divider(height: 32), + Text( + 'server.resource_usage'.tr(), + style: Theme.of(context).textTheme.titleLarge, + ), + const SizedBox(height: 8), + BlocProvider( + create: (final context) => MetricsCubit()..restart(), + child: _Chart(), + ), + const SizedBox(height: 8), + _TextDetails(), + ], ); } } diff --git a/lib/ui/pages/server_details/text_details.dart b/lib/ui/pages/server_details/text_details.dart index f94a353b..95569356 100644 --- a/lib/ui/pages/server_details/text_details.dart +++ b/lib/ui/pages/server_details/text_details.dart @@ -47,7 +47,7 @@ class _TempMessage extends StatelessWidget { final String message; @override Widget build(final BuildContext context) => SizedBox( - height: MediaQuery.of(context).size.height - 100, + height: 200, child: Center( child: Text( message, diff --git a/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart b/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart index cb58997d..59fd685f 100644 --- a/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart +++ b/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart @@ -1,9 +1,11 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:gap/gap.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; +import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; @@ -15,6 +17,27 @@ class RecoveryServerProviderConnected extends StatelessWidget { final ServerInstallationCubit appConfig = context.watch(); + if (appConfig.state.serverDetails?.provider == ServerProviderType.unknown) { + return BrandHeroScreen( + heroTitle: 'recovering.server_provider_unknown'.tr(), + heroSubtitle: 'recovering.server_provider_unknown_description'.tr(), + hasBackButton: true, + hasFlashButton: false, + ignoreBreakpoints: true, + onBackButtonPressed: () { + Navigator.of(context).popUntil((final route) => route.isFirst); + }, + children: [ + BrandButton.filled( + text: 'basis.continue'.tr(), + onPressed: () => context + .read() + .skipSettingServerProviderKey(), + ), + ], + ); + } + return BlocProvider( create: (final BuildContext context) => ServerProviderFormCubit(appConfig), @@ -50,13 +73,13 @@ class RecoveryServerProviderConnected extends StatelessWidget { ), ), ), - const SizedBox(height: 16), + const Gap(16), BrandButton.filled( onPressed: () => context.read().trySubmit(), child: Text('basis.continue'.tr()), ), - const SizedBox(height: 16), + const Gap(16), Builder( builder: (final context) => BrandButton.text( title: 'initializing.how'.tr(), @@ -66,6 +89,13 @@ class RecoveryServerProviderConnected extends StatelessWidget { ), ), ), + const Gap(16), + BrandButton.text( + title: 'recovering.login_later'.tr(), + onPressed: () => context + .read() + .skipSettingServerProviderKey(), + ), ], ), ),