From daf50e8c921ce8bc4cba97f5c5a9895547f06b01 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Sun, 23 Oct 2022 05:13:27 +0400 Subject: [PATCH 1/5] refactor(server-api): Replace Rest API interface with Graphql endpoints everywhere they are used With no implementations yet --- .../graphql_maps/server_api/server.dart | 67 +++++++++++++++++-- lib/logic/cubit/backups/backups_cubit.dart | 2 +- lib/logic/cubit/devices/devices_cubit.dart | 2 +- .../cubit/dns_records/dns_records_cubit.dart | 2 +- .../recovery_domain_form_cubit.dart | 2 +- .../recovery_key/recovery_key_cubit.dart | 2 +- .../server_installation_repository.dart | 2 +- 7 files changed, 69 insertions(+), 10 deletions(-) diff --git a/lib/logic/api_maps/graphql_maps/server_api/server.dart b/lib/logic/api_maps/graphql_maps/server_api/server.dart index 0e20fb02..3f903168 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server.dart @@ -8,8 +8,12 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.g import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart'; import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; +import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; +import 'package:selfprivacy/logic/models/json/backup.dart'; +import 'package:selfprivacy/logic/models/json/device_token.dart'; +import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/json/server_disk_volume.dart'; import 'package:selfprivacy/logic/models/json/server_job.dart'; import 'package:selfprivacy/logic/models/service.dart'; @@ -35,20 +39,34 @@ class GenericMutationResult { class GenericJobMutationReturn extends GenericMutationResult { GenericJobMutationReturn({ - required final super.success, - required final super.code, - final super.message, + required super.success, + required super.code, + super.message, this.job, }); final ServerJob? job; } +class ApiResponse { + ApiResponse({ + required this.statusCode, + required this.data, + this.errorMessage, + }); + final int statusCode; + final String? errorMessage; + final D data; + + bool get isSuccess => statusCode >= 200 && statusCode < 300; +} + class ServerApi extends ApiMap with VolumeApi, JobsApi, ServerActionsApi, ServicesApi, UsersApi { ServerApi({ this.hasLogger = false, this.isWithToken = true, this.customToken = '', + this.overrideDomain, }); @override @@ -58,7 +76,9 @@ class ServerApi extends ApiMap @override String customToken; @override - String? get rootAddress => getIt().serverDomain?.domainName; + String? get rootAddress => + overrideDomain ?? getIt().serverDomain?.domainName; + String? overrideDomain; Future getApiVersion() async { QueryResult response; @@ -193,4 +213,43 @@ class ServerApi extends ApiMap return settings; } + + Future> getRecoveryTokenStatus() async {} + + Future> generateRecoveryToken( + final DateTime? expirationDate, + final int? numberOfUses, + ) async {} + + Future getDkim() async {} + + Future>> getApiTokens() async {} + + Future> deleteApiToken(final String name) async {} + + Future> createDeviceToken() async {} + + Future getBackupStatus() async {} + + Future> getBackups() async {} + + Future uploadBackblazeConfig(final BackblazeBucket bucket) async {} + + Future forceBackupListReload() async {} + + Future startBackup() async {} + + Future restoreBackup(final String backupId) async {} + + Future isHttpServerWorking() async {} + + Future> authorizeDevice(final DeviceToken token) async {} + + Future> useRecoveryToken(final DeviceToken token) async {} + + Future> servicesPowerCheck() async {} + + Future>> getUsersList({ + required final bool withMainUser, + }) async {} } diff --git a/lib/logic/cubit/backups/backups_cubit.dart b/lib/logic/cubit/backups/backups_cubit.dart index 084a2b47..5f72f2e2 100644 --- a/lib/logic/cubit/backups/backups_cubit.dart +++ b/lib/logic/cubit/backups/backups_cubit.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart'; -import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/json/backup.dart'; diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index 6aa71400..1ab4ff11 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -1,5 +1,5 @@ import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index b6d503aa..29d19d81 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -6,7 +6,7 @@ import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_depe import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; -import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; part 'dns_records_state.dart'; diff --git a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart index 400fdff4..dbfc7259 100644 --- a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart index 9692ca4b..d20bb3c5 100644 --- a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart +++ b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart @@ -1,4 +1,4 @@ -import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index a83e9deb..93763ffd 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -12,7 +12,7 @@ import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart'; -import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; From 19b45ac14230682d89b853983c0d17a2d1d847e5 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 24 Oct 2022 13:47:47 +0400 Subject: [PATCH 2/5] fix(server-api): Adapt ApiResponse structure to GraphQL format --- .../graphql_maps/server_api/server.dart | 78 +++++++++++++++++-- lib/logic/cubit/devices/devices_cubit.dart | 10 +-- .../recovery_key/recovery_key_cubit.dart | 6 +- .../server_installation_repository.dart | 14 ++-- .../models/json/recovery_token_status.dart | 11 +++ 5 files changed, 96 insertions(+), 23 deletions(-) diff --git a/lib/logic/api_maps/graphql_maps/server_api/server.dart b/lib/logic/api_maps/graphql_maps/server_api/server.dart index 3f903168..686e3e29 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server.dart @@ -49,15 +49,13 @@ class GenericJobMutationReturn extends GenericMutationResult { class ApiResponse { ApiResponse({ - required this.statusCode, + required this.success, required this.data, - this.errorMessage, + this.message, }); - final int statusCode; - final String? errorMessage; + final bool success; + final String? message; final D data; - - bool get isSuccess => statusCode >= 200 && statusCode < 300; } class ServerApi extends ApiMap @@ -214,12 +212,76 @@ class ServerApi extends ApiMap return settings; } - Future> getRecoveryTokenStatus() async {} + Future> getRecoveryTokenStatus() async { + RecoveryKeyStatus? key; + QueryResult response; + String? error; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$RecoveryKey(); + if (response.hasException) { + print(response.exception.toString()); + error = response.exception.toString(); + } + key = RecoveryKeyStatus.fromGraphQL(response.parsedData!.api.recoveryKey); + } catch (e) { + print(e); + } + + return ApiResponse( + success: error == null, + data: key, + message: error, + ); + } Future> generateRecoveryToken( final DateTime? expirationDate, final int? numberOfUses, - ) async {} + ) async { + ApiResponse key; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final input = Input$RecoveryKeyLimitsInput( + expirationDate: expirationDate, + uses: numberOfUses, + ); + final variables = Variables$Mutation$GetNewRecoveryApiKey( + limits: input, + ); + final mutation = Options$Mutation$GetNewRecoveryApiKey( + variables: variables, + ); + response = await client.mutate$GetNewRecoveryApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + key = ApiResponse( + success: false, + data: '', + message: response.exception.toString(), + ); + } + key = ApiResponse( + success: true, + data: response.parsedData!.getNewRecoveryApiKey.key!, + ); + } catch (e) { + print(e); + key = ApiResponse( + success: false, + data: '', + message: e.toString(), + ); + } + + return key; + } Future getDkim() async {} diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index 1ab4ff11..3c552c23 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -36,7 +36,7 @@ class ApiDevicesCubit Future?> _getApiTokens() async { final ApiResponse> response = await api.getApiTokens(); - if (response.isSuccess) { + if (response.success) { return response.data; } else { return null; @@ -45,7 +45,7 @@ class ApiDevicesCubit Future deleteDevice(final ApiToken device) async { final ApiResponse response = await api.deleteApiToken(device.name); - if (response.isSuccess) { + if (response.success) { emit( ApiDevicesState( state.devices.where((final d) => d.name != device.name).toList(), @@ -54,17 +54,17 @@ class ApiDevicesCubit ); } else { getIt() - .showSnackBar(response.errorMessage ?? 'Error deleting device'); + .showSnackBar(response.message ?? 'Error deleting device'); } } Future getNewDeviceKey() async { final ApiResponse response = await api.createDeviceToken(); - if (response.isSuccess) { + if (response.success) { return response.data; } else { getIt().showSnackBar( - response.errorMessage ?? 'Error getting new device key', + response.message ?? 'Error getting new device key', ); return null; } diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart index d20bb3c5..90f5330d 100644 --- a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart +++ b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart @@ -34,7 +34,7 @@ class RecoveryKeyCubit Future _getRecoveryKeyStatus() async { final ApiResponse response = await api.getRecoveryTokenStatus(); - if (response.isSuccess) { + if (response.success) { return response.data; } else { return null; @@ -59,11 +59,11 @@ class RecoveryKeyCubit }) async { final ApiResponse response = await api.generateRecoveryToken(expirationDate, numberOfUses); - if (response.isSuccess) { + if (response.success) { refresh(); return response.data; } else { - throw GenerationError(response.errorMessage ?? 'Unknown error'); + throw GenerationError(response.message ?? 'Unknown error'); } } diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 93763ffd..2c08dda1 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -498,7 +498,7 @@ class ServerInstallationRepository { DeviceToken(device: await getDeviceName(), token: newDeviceKey), ); - if (apiResponse.isSuccess) { + if (apiResponse.success) { return ServerHostingDetails( apiToken: apiResponse.data, volume: ServerVolume( @@ -517,7 +517,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.errorMessage ?? apiResponse.data, + apiResponse.message ?? apiResponse.data, ); } @@ -535,7 +535,7 @@ class ServerInstallationRepository { DeviceToken(device: await getDeviceName(), token: recoveryKey), ); - if (apiResponse.isSuccess) { + if (apiResponse.success) { return ServerHostingDetails( apiToken: apiResponse.data, volume: ServerVolume( @@ -554,7 +554,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.errorMessage ?? apiResponse.data, + apiResponse.message ?? apiResponse.data, ); } @@ -600,7 +600,7 @@ class ServerInstallationRepository { DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data), ); - if (apiResponse.isSuccess) { + if (apiResponse.success) { return ServerHostingDetails( apiToken: apiResponse.data, volume: ServerVolume( @@ -619,7 +619,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.errorMessage ?? apiResponse.data, + apiResponse.message ?? apiResponse.data, ); } @@ -636,7 +636,7 @@ class ServerInstallationRepository { final String? serverApiVersion = await serverApi.getApiVersion(); final ApiResponse> users = await serverApi.getUsersList(withMainUser: true); - if (serverApiVersion == null || !users.isSuccess) { + if (serverApiVersion == null || !users.success) { return fallbackUser; } try { diff --git a/lib/logic/models/json/recovery_token_status.dart b/lib/logic/models/json/recovery_token_status.dart index 6e59b57d..b56ae4e6 100644 --- a/lib/logic/models/json/recovery_token_status.dart +++ b/lib/logic/models/json/recovery_token_status.dart @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart'; part 'recovery_token_status.g.dart'; @@ -15,6 +16,16 @@ class RecoveryKeyStatus extends Equatable { this.usesLeft, }); + RecoveryKeyStatus.fromGraphQL( + final Query$RecoveryKey$api$recoveryKey recoveryKey, + ) : this( + exists: recoveryKey.exists, + date: recoveryKey.creationDate, + expiration: recoveryKey.expirationDate, + usesLeft: recoveryKey.usesLeft, + valid: recoveryKey.valid, + ); + final bool exists; final DateTime? date; final DateTime? expiration; From 1b94e14727a8a2dd89e5e476c3561c9c045b6597 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Mon, 24 Oct 2022 15:39:53 +0400 Subject: [PATCH 3/5] feat(server-api): Migrate all server endpoints to GraphQL --- .../graphql_maps/server_api/server.dart | 279 +++++- lib/logic/api_maps/rest_maps/server.dart | 870 ------------------ .../server_installation_repository.dart | 15 +- lib/logic/models/json/api_token.dart | 9 + 4 files changed, 256 insertions(+), 917 deletions(-) delete mode 100644 lib/logic/api_maps/rest_maps/server.dart diff --git a/lib/logic/api_maps/graphql_maps/server_api/server.dart b/lib/logic/api_maps/graphql_maps/server_api/server.dart index 686e3e29..78e352c4 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server.dart @@ -112,25 +112,6 @@ class ServerApi extends ApiMap return usesBinds; } - Future> getApiTokens() async { - QueryResult response; - List tokens = []; - - try { - final GraphQLClient client = await getClient(); - response = await client.query$GetApiTokens(); - if (response.hasException) { - print(response.exception.toString()); - } - tokens = response.data!['api']['devices'] - .map((final e) => ApiToken.fromJson(e)) - .toList(); - } catch (e) { - print(e); - } - return tokens; - } - Future switchService(final String uid, final bool needTurnOn) async { try { final GraphQLClient client = await getClient(); @@ -283,17 +264,253 @@ class ServerApi extends ApiMap return key; } - Future getDkim() async {} + Future getDkim() async { + String? dkim; + QueryResult response; - Future>> getApiTokens() async {} + try { + final GraphQLClient client = await getClient(); + response = await client.query$DomainInfo(); + if (response.hasException) { + print(response.exception.toString()); + } + dkim = response.parsedData!.system.domainInfo.requiredDnsRecords + .firstWhere( + ( + final Query$DomainInfo$system$domainInfo$requiredDnsRecords + dnsRecord, + ) => + dnsRecord.name == 'selector._domainkey' && + dnsRecord.recordType == 'TXT', + ) + .content; + } catch (e) { + print(e); + } - Future> deleteApiToken(final String name) async {} + return dkim; + } - Future> createDeviceToken() async {} + Future>> getApiTokens() async { + ApiResponse> tokens; + QueryResult response; - Future getBackupStatus() async {} + try { + final GraphQLClient client = await getClient(); + response = await client.query$GetApiTokens(); + if (response.hasException) { + final message = response.exception.toString(); + print(message); + tokens = ApiResponse>( + success: false, + data: [], + message: message, + ); + } + final List parsed = response.parsedData!.api.devices + .map( + ( + final Query$GetApiTokens$api$devices device, + ) => + ApiToken.fromGraphQL(device), + ) + .toList(); + tokens = ApiResponse>( + success: true, + data: parsed, + ); + } catch (e) { + print(e); + tokens = ApiResponse>( + success: false, + data: [], + message: e.toString(), + ); + } - Future> getBackups() async {} + return tokens; + } + + Future> deleteApiToken(final String name) async { + ApiResponse returnable; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final variables = Variables$Mutation$DeleteDeviceApiToken( + device: name, + ); + final mutation = Options$Mutation$DeleteDeviceApiToken( + variables: variables, + ); + response = await client.mutate$DeleteDeviceApiToken( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + returnable = ApiResponse( + success: false, + data: null, + message: response.exception.toString(), + ); + } + returnable = ApiResponse( + success: true, + data: null, + ); + } catch (e) { + print(e); + returnable = ApiResponse( + success: false, + data: null, + message: e.toString(), + ); + } + + return returnable; + } + + Future> createDeviceToken() async { + ApiResponse token; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final mutation = Options$Mutation$GetNewDeviceApiKey(); + response = await client.mutate$GetNewDeviceApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + token = ApiResponse( + success: false, + data: '', + message: response.exception.toString(), + ); + } + token = ApiResponse( + success: true, + data: response.parsedData!.getNewDeviceApiKey.key!, + ); + } catch (e) { + print(e); + token = ApiResponse( + success: false, + data: '', + message: e.toString(), + ); + } + + return token; + } + + Future isHttpServerWorking() async => (await getApiVersion()) != null; + + Future> authorizeDevice( + final DeviceToken deviceToken, + ) async { + ApiResponse token; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final input = Input$UseNewDeviceKeyInput( + deviceName: deviceToken.device, + key: deviceToken.token, + ); + + final variables = Variables$Mutation$AuthorizeWithNewDeviceApiKey( + input: input, + ); + final mutation = Options$Mutation$AuthorizeWithNewDeviceApiKey( + variables: variables, + ); + response = await client.mutate$AuthorizeWithNewDeviceApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + token = ApiResponse( + success: false, + data: '', + message: response.exception.toString(), + ); + } + token = ApiResponse( + success: true, + data: response.parsedData!.authorizeWithNewDeviceApiKey.token!, + ); + } catch (e) { + print(e); + token = ApiResponse( + success: false, + data: '', + message: e.toString(), + ); + } + + return token; + } + + Future> useRecoveryToken( + final DeviceToken deviceToken, + ) async { + ApiResponse token; + QueryResult response; + + try { + final GraphQLClient client = await getClient(); + + final input = Input$UseRecoveryKeyInput( + deviceName: deviceToken.device, + key: deviceToken.token, + ); + + final variables = Variables$Mutation$UseRecoveryApiKey( + input: input, + ); + final mutation = Options$Mutation$UseRecoveryApiKey( + variables: variables, + ); + response = await client.mutate$UseRecoveryApiKey( + mutation, + ); + if (response.hasException) { + print(response.exception.toString()); + token = ApiResponse( + success: false, + data: '', + message: response.exception.toString(), + ); + } + token = ApiResponse( + success: true, + data: response.parsedData!.useRecoveryApiKey.token!, + ); + } catch (e) { + print(e); + token = ApiResponse( + success: false, + data: '', + message: e.toString(), + ); + } + + return token; + } + + /// TODO: backups're not implemented on server side + + Future getBackupStatus() async => BackupStatus( + progress: 0.0, + status: BackupStatusEnum.error, + errorMessage: null, + ); + + Future> getBackups() async => []; Future uploadBackblazeConfig(final BackblazeBucket bucket) async {} @@ -302,16 +519,4 @@ class ServerApi extends ApiMap Future startBackup() async {} Future restoreBackup(final String backupId) async {} - - Future isHttpServerWorking() async {} - - Future> authorizeDevice(final DeviceToken token) async {} - - Future> useRecoveryToken(final DeviceToken token) async {} - - Future> servicesPowerCheck() async {} - - Future>> getUsersList({ - required final bool withMainUser, - }) async {} } diff --git a/lib/logic/api_maps/rest_maps/server.dart b/lib/logic/api_maps/rest_maps/server.dart deleted file mode 100644 index 1cef3dfc..00000000 --- a/lib/logic/api_maps/rest_maps/server.dart +++ /dev/null @@ -1,870 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; -import 'package:selfprivacy/logic/common_enum/common_enum.dart'; -import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; -import 'package:selfprivacy/logic/models/hive/server_domain.dart'; -import 'package:selfprivacy/logic/models/hive/user.dart'; -import 'package:selfprivacy/logic/models/json/api_token.dart'; -import 'package:selfprivacy/logic/models/json/backup.dart'; -import 'package:selfprivacy/logic/models/json/device_token.dart'; -import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; -import 'package:selfprivacy/logic/models/service.dart'; -import 'package:selfprivacy/logic/models/timezone_settings.dart'; - -class ApiResponse { - ApiResponse({ - required this.statusCode, - required this.data, - this.errorMessage, - }); - final int statusCode; - final String? errorMessage; - final D data; - - bool get isSuccess => statusCode >= 200 && statusCode < 300; -} - -class ServerApi extends ApiMap { - ServerApi({ - this.hasLogger = false, - this.isWithToken = true, - this.overrideDomain, - this.customToken, - }); - @override - bool hasLogger; - @override - bool isWithToken; - String? overrideDomain; - String? customToken; - - @override - BaseOptions get options { - BaseOptions options = BaseOptions( - connectTimeout: 10000, - receiveTimeout: 10000, - ); - - if (isWithToken) { - final ServerDomain? serverDomain = getIt().serverDomain; - final String domainName = serverDomain!.domainName; - final String? apiToken = getIt().serverDetails?.apiToken; - - options = BaseOptions( - baseUrl: 'https://api.$domainName', - connectTimeout: 10000, - receiveTimeout: 10000, - headers: { - 'Authorization': 'Bearer $apiToken', - }, - ); - } - - if (overrideDomain != null) { - options = BaseOptions( - baseUrl: 'https://api.$overrideDomain', - connectTimeout: 10000, - receiveTimeout: 10000, - headers: customToken != null - ? {'Authorization': 'Bearer $customToken'} - : null, - ); - } - - return options; - } - - Future getApiVersion() async { - Response response; - - final Dio client = await getClient(); - String? apiVersion; - - try { - response = await client.get('/api/version'); - apiVersion = response.data['version']; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return apiVersion; - } - - Future isHttpServerWorking() async { - bool res = false; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/services/status'); - res = response.statusCode == HttpStatus.ok; - } catch (e) { - print(e); - } finally { - close(client); - } - return res; - } - - Future> createUser(final User user) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/users', - data: { - 'username': user.login, - 'password': user.password, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.error.toString(), - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: User( - login: user.login, - type: UserType.normal, - password: user.password, - isFoundOnServer: false, - ), - ); - } finally { - close(client); - } - - bool isFoundOnServer = false; - int code = 0; - - final bool isUserCreated = (response.statusCode != null) && - (response.statusCode == HttpStatus.created); - - if (isUserCreated) { - isFoundOnServer = true; - code = response.statusCode!; - } else { - isFoundOnServer = false; - code = HttpStatus.notAcceptable; - } - - return ApiResponse( - statusCode: code, - data: User( - login: user.login, - type: UserType.normal, - password: user.password, - isFoundOnServer: isFoundOnServer, - ), - ); - } - - Future>> getUsersList({ - final withMainUser = false, - }) async { - final List res = []; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get( - '/users', - queryParameters: withMainUser ? {'withMainUser': 'true'} : null, - ); - for (final user in response.data) { - res.add(user.toString()); - } - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: [], - ); - } catch (e) { - print(e); - return ApiResponse( - errorMessage: e.toString(), - statusCode: HttpStatus.internalServerError, - data: [], - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: res, - ); - } - - Future> addUserSshKey( - final User user, - final String sshKey, - ) async { - late Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/services/ssh/keys/${user.login}', - data: { - 'public_key': sshKey, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: null, - ); - } - - Future> addRootSshKey(final String ssh) async { - late Response response; - - final Dio client = await getClient(); - try { - response = await client.put( - '/services/ssh/key/send', - data: {'public_key': ssh}, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: null, - ); - } - - Future>> getUserSshKeys(final User user) async { - List res; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/services/ssh/keys/${user.login}'); - res = (response.data as List) - .map((final e) => e as String) - .toList(); - } on DioError catch (e) { - print(e.message); - return ApiResponse>( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: [], - ); - } catch (e) { - return ApiResponse>( - errorMessage: e.toString(), - statusCode: HttpStatus.internalServerError, - data: [], - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse>( - statusCode: code, - data: res, - errorMessage: response.data is List - ? null - : response.data?.containsKey('error') ?? false - ? response.data['error'] - : null, - ); - } - - Future> deleteUserSshKey( - final User user, - final String sshKey, - ) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.delete( - '/services/ssh/keys/${user.login}', - data: {'public_key': sshKey}, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: null, - errorMessage: response.data?.containsKey('error') ?? false - ? response.data['error'] - : null, - ); - } - - Future deleteUser(final User user) async { - bool res = false; - Response response; - - final Dio client = await getClient(); - try { - response = await client.delete('/users/${user.login}'); - res = response.statusCode == HttpStatus.ok || - response.statusCode == HttpStatus.notFound; - } on DioError catch (e) { - print(e.message); - res = false; - } finally { - close(client); - } - return res; - } - - @override - String get rootAddress => - throw UnimplementedError('not used in with implementation'); - - Future apply() async { - bool res = false; - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/system/configuration/apply'); - res = response.statusCode == HttpStatus.ok; - } on DioError catch (e) { - print(e.message); - res = false; - } finally { - close(client); - } - return res; - } - - Future switchService( - final Service service, - final bool needToTurnOn, - ) async { - final Dio client = await getClient(); - try { - client.post( - '/services/${service.id}/${needToTurnOn ? 'enable' : 'disable'}', - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future> servicesPowerCheck() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/services/status'); - } on DioError catch (e) { - print(e.message); - return {}; - } finally { - close(client); - } - - return { - 'bitwarden': response.data['bitwarden'] == 0, - 'gitea': response.data['gitea'] == 0, - 'nextcloud': response.data['nextcloud'] == 0, - 'ocserv': response.data['ocserv'] == 0, - 'pleroma': response.data['pleroma'] == 0, - }; - } - - Future uploadBackblazeConfig(final BackblazeBucket bucket) async { - final Dio client = await getClient(); - try { - client.put( - '/services/restic/backblaze/config', - data: { - 'accountId': bucket.applicationKeyId, - 'accountKey': bucket.applicationKey, - 'bucket': bucket.bucketName, - }, - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future startBackup() async { - final Dio client = await getClient(); - try { - client.put('/services/restic/backup/create'); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future> getBackups() async { - Response response; - List backups = []; - - final Dio client = await getClient(); - try { - response = await client.get('/services/restic/backup/list'); - backups = - response.data.map((final e) => Backup.fromJson(e)).toList(); - } on DioError catch (e) { - print(e.message); - } catch (e) { - print(e); - } finally { - close(client); - } - return backups; - } - - Future getBackupStatus() async { - Response response; - BackupStatus status = BackupStatus( - status: BackupStatusEnum.error, - errorMessage: 'Network error', - progress: 0, - ); - - final Dio client = await getClient(); - try { - response = await client.get('/services/restic/backup/status'); - status = BackupStatus.fromJson(response.data); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return status; - } - - Future forceBackupListReload() async { - final Dio client = await getClient(); - try { - client.get('/services/restic/backup/reload'); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future restoreBackup(final String backupId) async { - final Dio client = await getClient(); - try { - client.put( - '/services/restic/backup/restore', - data: {'backupId': backupId}, - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future pullConfigurationUpdate() async { - Response response; - bool result = false; - - final Dio client = await getClient(); - try { - response = await client.get('/system/configuration/pull'); - result = (response.statusCode != null) - ? (response.statusCode == HttpStatus.ok) - : false; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return result; - } - - Future reboot() async { - Response response; - bool result = false; - - final Dio client = await getClient(); - try { - response = await client.get('/system/reboot'); - result = (response.statusCode != null) - ? (response.statusCode == HttpStatus.ok) - : false; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return result; - } - - Future upgrade() async { - Response response; - bool result = false; - - final Dio client = await getClient(); - try { - response = await client.get('/system/configuration/upgrade'); - result = (response.statusCode != null) - ? (response.statusCode == HttpStatus.ok) - : false; - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - return result; - } - - Future getServerTimezone() async { - TimeZoneSettings settings = TimeZoneSettings(); - final Dio client = await getClient(); - try { - final Response response = await client.get( - '/system/configuration/timezone', - ); - settings = TimeZoneSettings.fromString(response.data); - } catch (e) { - print(e); - } finally { - close(client); - } - - return settings; - } - - Future updateServerTimezone(final TimeZoneSettings settings) async { - final Dio client = await getClient(); - try { - await client.put( - '/system/configuration/timezone', - data: settings.toString(), - ); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - } - - Future getDkim() async { - Response response; - String? dkim; - final Dio client = await getClient(); - try { - response = await client.get('/services/mailserver/dkim'); - final Codec base64toString = utf8.fuse(base64); - dkim = base64toString - .decode(response.data) - .split('(')[1] - .split(')')[0] - .replaceAll('"', ''); - } on DioError catch (e) { - print(e.message); - } finally { - close(client); - } - - return dkim; - } - - Future> getRecoveryTokenStatus() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/auth/recovery_token'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: const RecoveryKeyStatus(exists: false, valid: false), - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null - ? RecoveryKeyStatus.fromJson(response.data) - : null, - ); - } - - Future> generateRecoveryToken( - final DateTime? expiration, - final int? uses, - ) async { - Response response; - - final Dio client = await getClient(); - final Map data = {}; - if (expiration != null) { - data['expiration'] = '${expiration.toIso8601String()}Z'; - print(data['expiration']); - } - if (uses != null) { - data['uses'] = uses; - } - try { - response = await client.post( - '/auth/recovery_token', - data: data, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - close(client); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> useRecoveryToken(final DeviceToken token) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/auth/recovery_token/use', - data: { - 'token': token.token, - 'device': token.device, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> authorizeDevice(final DeviceToken token) async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post( - '/auth/new_device/authorize', - data: { - 'token': token.token, - 'device': token.device, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse(statusCode: code, data: response.data['token'] ?? ''); - } - - Future> createDeviceToken() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post('/auth/new_device'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> deleteDeviceToken() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.delete('/auth/new_device'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse(statusCode: code, data: response.data ?? ''); - } - - Future>> getApiTokens() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.get('/auth/tokens'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: [], - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: (response.data != null) - ? response.data - .map((final e) => ApiToken.fromJson(e)) - .toList() - : [], - ); - } - - Future> refreshCurrentApiToken() async { - Response response; - - final Dio client = await getClient(); - try { - response = await client.post('/auth/tokens'); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: '', - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - - return ApiResponse( - statusCode: code, - data: response.data != null ? response.data['token'] : '', - ); - } - - Future> deleteApiToken(final String device) async { - Response response; - final Dio client = await getClient(); - try { - response = await client.delete( - '/auth/tokens', - data: { - 'token_name': device, - }, - ); - } on DioError catch (e) { - print(e.message); - return ApiResponse( - errorMessage: e.message, - statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, - data: null, - ); - } finally { - client.close(); - } - - final int code = response.statusCode ?? HttpStatus.internalServerError; - return ApiResponse(statusCode: code, data: null); - } -} diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 2c08dda1..fdbb32e1 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -570,9 +570,7 @@ class ServerInstallationRepository { ); final String serverIp = await getServerIpFromDomain(serverDomain); if (recoveryCapabilities == ServerRecoveryCapabilities.legacy) { - final Map apiResponse = - await serverApi.servicesPowerCheck(); - if (apiResponse.isNotEmpty) { + if (await serverApi.isHttpServerWorking()) { return ServerHostingDetails( apiToken: apiToken, volume: ServerVolume( @@ -634,9 +632,8 @@ class ServerInstallationRepository { ); final String? serverApiVersion = await serverApi.getApiVersion(); - final ApiResponse> users = - await serverApi.getUsersList(withMainUser: true); - if (serverApiVersion == null || !users.success) { + final users = await serverApi.getAllUsers(); + if (serverApiVersion == null || users.isEmpty) { return fallbackUser; } try { @@ -644,10 +641,8 @@ class ServerInstallationRepository { if (!VersionConstraint.parse('>=1.2.5').allows(parsedVersion)) { return fallbackUser; } - return User( - isFoundOnServer: true, - login: users.data[0], - type: UserType.primary, + return users.firstWhere( + (final User user) => user.type == UserType.primary, ); } on FormatException { return fallbackUser; diff --git a/lib/logic/models/json/api_token.dart b/lib/logic/models/json/api_token.dart index 980d5132..f53f7f02 100644 --- a/lib/logic/models/json/api_token.dart +++ b/lib/logic/models/json/api_token.dart @@ -1,4 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphql.dart'; part 'api_token.g.dart'; @@ -12,6 +13,14 @@ class ApiToken { required this.isCaller, }); + ApiToken.fromGraphQL( + final Query$GetApiTokens$api$devices device, + ) : this( + name: device.name, + date: device.creationDate, + isCaller: device.isCaller, + ); + final String name; final DateTime date; @JsonKey(name: 'is_caller') From 1845bea36131e9ecb478b0d557a7ebc4bd5695e9 Mon Sep 17 00:00:00 2001 From: inexcode Date: Wed, 26 Oct 2022 19:53:25 +0300 Subject: [PATCH 4/5] chore(server-api): Deprecate ApiResponse ApiResponse is a wrapper for REST API responses, which is removed. Our code, however, depends on this interface. This dependency must be removed. --- lib/logic/api_maps/graphql_maps/server_api/server.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/logic/api_maps/graphql_maps/server_api/server.dart b/lib/logic/api_maps/graphql_maps/server_api/server.dart index 78e352c4..842d278a 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server.dart @@ -47,6 +47,8 @@ class GenericJobMutationReturn extends GenericMutationResult { final ServerJob? job; } +@Deprecated( + 'Extend GenericMutationResult for mutations, return data for queries') class ApiResponse { ApiResponse({ required this.success, From 88bcdcff39328ba9b1a2a5804172d8e28c6a5b1b Mon Sep 17 00:00:00 2001 From: inexcode Date: Wed, 26 Oct 2022 20:24:00 +0300 Subject: [PATCH 5/5] chore(server-api): Deprecate getDkim Server API now aware of all required DNS records. More general approach to handle DNS records is required in the future. --- lib/logic/api_maps/graphql_maps/server_api/server.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/logic/api_maps/graphql_maps/server_api/server.dart b/lib/logic/api_maps/graphql_maps/server_api/server.dart index 842d278a..31ee89fe 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server.dart @@ -48,7 +48,8 @@ class GenericJobMutationReturn extends GenericMutationResult { } @Deprecated( - 'Extend GenericMutationResult for mutations, return data for queries') + 'Extend GenericMutationResult for mutations, return data for queries', +) class ApiResponse { ApiResponse({ required this.success, @@ -266,6 +267,9 @@ class ServerApi extends ApiMap return key; } + @Deprecated( + 'Server now aware of all required DNS records. More general approach has to be implemented', + ) Future getDkim() async { String? dkim; QueryResult response;