From 68811efc1e53630db014bbe2a079991ee36b8339 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 28 Oct 2022 12:17:08 +0400 Subject: [PATCH 1/2] refactor(server-api): Generalize and unify api response objects - Separate response success from business logic operation success - Remove ApiResponse, replace with GenericResult - Make GenericMutationResult inherit GenericResult - Add generic error message for when a response couldn't be sent or received --- assets/translations/en.json | 3 +- assets/translations/ru.json | 3 +- .../graphql_maps/server_api/jobs_api.dart | 6 +- .../graphql_maps/server_api/server.dart | 107 ++++++++---------- .../graphql_maps/server_api/services_api.dart | 44 ++++--- .../graphql_maps/server_api/users_api.dart | 59 +++++----- .../graphql_maps/server_api/volume_api.dart | 24 ++-- lib/logic/cubit/devices/devices_cubit.dart | 6 +- .../recovery_key/recovery_key_cubit.dart | 4 +- .../server_installation_repository.dart | 26 ++--- .../cubit/server_jobs/server_jobs_cubit.dart | 14 ++- lib/logic/cubit/services/services_cubit.dart | 14 ++- lib/logic/cubit/users/users_cubit.dart | 35 +++--- 13 files changed, 182 insertions(+), 163 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index c74e33e8..ddb5c541 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -419,7 +419,8 @@ "create_ssh_key": "Create SSH key for {}", "delete_ssh_key": "Delete SSH key for {}", "server_jobs": "Jobs on the server", - "reset_user_password": "Reset password of user" + "reset_user_password": "Reset password of user", + "generic_error": "Couldn't connect to the server!" }, "validations": { "required": "Required", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 704e5991..d2e298c2 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -420,7 +420,8 @@ "create_ssh_key": "Создать SSH ключ для {}", "delete_ssh_key": "Удалить SSH ключ для {}", "server_jobs": "Задачи на сервере", - "reset_user_password": "Сбросить пароль пользователя" + "reset_user_password": "Сбросить пароль пользователя", + "generic_error": "Не удалось подключиться к серверу!" }, "validations": { "required": "Обязательное поле", diff --git a/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart b/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart index 8d731e3e..67e8bcb3 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/jobs_api.dart @@ -22,20 +22,22 @@ mixin JobsApi on ApiMap { return jobsList; } - Future removeApiJob(final String uid) async { + Future> removeApiJob(final String uid) async { try { final GraphQLClient client = await getClient(); final variables = Variables$Mutation$RemoveJob(jobId: uid); final mutation = Options$Mutation$RemoveJob(variables: variables); final response = await client.mutate$RemoveJob(mutation); return GenericMutationResult( - success: response.parsedData?.removeJob.success ?? false, + data: response.parsedData?.removeJob.success ?? false, + success: true, code: response.parsedData?.removeJob.code ?? 0, message: response.parsedData?.removeJob.message, ); } catch (e) { print(e); return GenericMutationResult( + data: false, success: false, code: 0, message: e.toString(), 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 31ee89fe..82aa24be 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server.dart @@ -26,39 +26,28 @@ part 'services_api.dart'; part 'users_api.dart'; part 'volume_api.dart'; -class GenericMutationResult { - GenericMutationResult({ - required this.success, - required this.code, - this.message, - }); - final bool success; - final int code; - final String? message; -} - -class GenericJobMutationReturn extends GenericMutationResult { - GenericJobMutationReturn({ - required super.success, - required super.code, - super.message, - this.job, - }); - final ServerJob? job; -} - -@Deprecated( - 'Extend GenericMutationResult for mutations, return data for queries', -) -class ApiResponse { - ApiResponse({ +class GenericResult { + GenericResult({ required this.success, required this.data, this.message, }); + + /// Whether was a response successfully received, + /// doesn't represent success of the request if `data` is `bool` final bool success; final String? message; - final D data; + final T data; +} + +class GenericMutationResult extends GenericResult { + GenericMutationResult({ + required super.success, + required this.code, + required super.data, + super.message, + }); + final int code; } class ServerApi extends ApiMap @@ -196,7 +185,7 @@ class ServerApi extends ApiMap return settings; } - Future> getRecoveryTokenStatus() async { + Future> getRecoveryTokenStatus() async { RecoveryKeyStatus? key; QueryResult response; String? error; @@ -213,18 +202,18 @@ class ServerApi extends ApiMap print(e); } - return ApiResponse( + return GenericResult( success: error == null, data: key, message: error, ); } - Future> generateRecoveryToken( + Future> generateRecoveryToken( final DateTime? expirationDate, final int? numberOfUses, ) async { - ApiResponse key; + GenericResult key; QueryResult response; try { @@ -245,19 +234,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - key = ApiResponse( + key = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - key = ApiResponse( + key = GenericResult( success: true, data: response.parsedData!.getNewRecoveryApiKey.key!, ); } catch (e) { print(e); - key = ApiResponse( + key = GenericResult( success: false, data: '', message: e.toString(), @@ -297,8 +286,8 @@ class ServerApi extends ApiMap return dkim; } - Future>> getApiTokens() async { - ApiResponse> tokens; + Future>> getApiTokens() async { + GenericResult> tokens; QueryResult response; try { @@ -307,7 +296,7 @@ class ServerApi extends ApiMap if (response.hasException) { final message = response.exception.toString(); print(message); - tokens = ApiResponse>( + tokens = GenericResult>( success: false, data: [], message: message, @@ -321,13 +310,13 @@ class ServerApi extends ApiMap ApiToken.fromGraphQL(device), ) .toList(); - tokens = ApiResponse>( + tokens = GenericResult>( success: true, data: parsed, ); } catch (e) { print(e); - tokens = ApiResponse>( + tokens = GenericResult>( success: false, data: [], message: e.toString(), @@ -337,8 +326,8 @@ class ServerApi extends ApiMap return tokens; } - Future> deleteApiToken(final String name) async { - ApiResponse returnable; + Future> deleteApiToken(final String name) async { + GenericResult returnable; QueryResult response; try { @@ -355,19 +344,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - returnable = ApiResponse( + returnable = GenericResult( success: false, data: null, message: response.exception.toString(), ); } - returnable = ApiResponse( + returnable = GenericResult( success: true, data: null, ); } catch (e) { print(e); - returnable = ApiResponse( + returnable = GenericResult( success: false, data: null, message: e.toString(), @@ -377,8 +366,8 @@ class ServerApi extends ApiMap return returnable; } - Future> createDeviceToken() async { - ApiResponse token; + Future> createDeviceToken() async { + GenericResult token; QueryResult response; try { @@ -390,19 +379,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = ApiResponse( + token = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = ApiResponse( + token = GenericResult( success: true, data: response.parsedData!.getNewDeviceApiKey.key!, ); } catch (e) { print(e); - token = ApiResponse( + token = GenericResult( success: false, data: '', message: e.toString(), @@ -414,10 +403,10 @@ class ServerApi extends ApiMap Future isHttpServerWorking() async => (await getApiVersion()) != null; - Future> authorizeDevice( + Future> authorizeDevice( final DeviceToken deviceToken, ) async { - ApiResponse token; + GenericResult token; QueryResult response; try { @@ -439,19 +428,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = ApiResponse( + token = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = ApiResponse( + token = GenericResult( success: true, data: response.parsedData!.authorizeWithNewDeviceApiKey.token!, ); } catch (e) { print(e); - token = ApiResponse( + token = GenericResult( success: false, data: '', message: e.toString(), @@ -461,10 +450,10 @@ class ServerApi extends ApiMap return token; } - Future> useRecoveryToken( + Future> useRecoveryToken( final DeviceToken deviceToken, ) async { - ApiResponse token; + GenericResult token; QueryResult response; try { @@ -486,19 +475,19 @@ class ServerApi extends ApiMap ); if (response.hasException) { print(response.exception.toString()); - token = ApiResponse( + token = GenericResult( success: false, data: '', message: response.exception.toString(), ); } - token = ApiResponse( + token = GenericResult( success: true, data: response.parsedData!.useRecoveryApiKey.token!, ); } catch (e) { print(e); - token = ApiResponse( + token = GenericResult( success: false, data: '', message: e.toString(), diff --git a/lib/logic/api_maps/graphql_maps/server_api/services_api.dart b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart index 62a55e15..2ef0e2ca 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/services_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/services_api.dart @@ -20,20 +20,24 @@ mixin ServicesApi on ApiMap { return services; } - Future enableService(final String serviceId) async { + Future> enableService( + final String serviceId, + ) async { try { final GraphQLClient client = await getClient(); final variables = Variables$Mutation$EnableService(serviceId: serviceId); final mutation = Options$Mutation$EnableService(variables: variables); final response = await client.mutate$EnableService(mutation); return GenericMutationResult( - success: response.parsedData?.enableService.success ?? false, + data: response.parsedData?.enableService.success ?? false, + success: true, code: response.parsedData?.enableService.code ?? 0, message: response.parsedData?.enableService.message, ); } catch (e) { print(e); return GenericMutationResult( + data: false, success: false, code: 0, message: e.toString(), @@ -41,13 +45,16 @@ mixin ServicesApi on ApiMap { } } - Future disableService(final String serviceId) async { + Future> disableService( + final String serviceId, + ) async { try { final GraphQLClient client = await getClient(); final variables = Variables$Mutation$DisableService(serviceId: serviceId); final mutation = Options$Mutation$DisableService(variables: variables); final response = await client.mutate$DisableService(mutation); return GenericMutationResult( + data: null, success: response.parsedData?.disableService.success ?? false, code: response.parsedData?.disableService.code ?? 0, message: response.parsedData?.disableService.message, @@ -55,6 +62,7 @@ mixin ServicesApi on ApiMap { } catch (e) { print(e); return GenericMutationResult( + data: null, success: false, code: 0, message: e.toString(), @@ -62,20 +70,24 @@ mixin ServicesApi on ApiMap { } } - Future stopService(final String serviceId) async { + Future> stopService( + final String serviceId, + ) async { try { final GraphQLClient client = await getClient(); final variables = Variables$Mutation$StopService(serviceId: serviceId); final mutation = Options$Mutation$StopService(variables: variables); final response = await client.mutate$StopService(mutation); return GenericMutationResult( - success: response.parsedData?.stopService.success ?? false, + data: response.parsedData?.stopService.success ?? false, + success: true, code: response.parsedData?.stopService.code ?? 0, message: response.parsedData?.stopService.message, ); } catch (e) { print(e); return GenericMutationResult( + data: false, success: false, code: 0, message: e.toString(), @@ -90,6 +102,7 @@ mixin ServicesApi on ApiMap { final mutation = Options$Mutation$StartService(variables: variables); final response = await client.mutate$StartService(mutation); return GenericMutationResult( + data: null, success: response.parsedData?.startService.success ?? false, code: response.parsedData?.startService.code ?? 0, message: response.parsedData?.startService.message, @@ -97,6 +110,7 @@ mixin ServicesApi on ApiMap { } catch (e) { print(e); return GenericMutationResult( + data: null, success: false, code: 0, message: e.toString(), @@ -104,20 +118,24 @@ mixin ServicesApi on ApiMap { } } - Future restartService(final String serviceId) async { + Future> restartService( + final String serviceId, + ) async { try { final GraphQLClient client = await getClient(); final variables = Variables$Mutation$RestartService(serviceId: serviceId); final mutation = Options$Mutation$RestartService(variables: variables); final response = await client.mutate$RestartService(mutation); return GenericMutationResult( - success: response.parsedData?.restartService.success ?? false, + data: response.parsedData?.restartService.success ?? false, + success: true, code: response.parsedData?.restartService.code ?? 0, message: response.parsedData?.restartService.message, ); } catch (e) { print(e); return GenericMutationResult( + data: false, success: false, code: 0, message: e.toString(), @@ -125,7 +143,7 @@ mixin ServicesApi on ApiMap { } } - Future moveService( + Future> moveService( final String serviceId, final String destination, ) async { @@ -140,19 +158,19 @@ mixin ServicesApi on ApiMap { final mutation = Options$Mutation$MoveService(variables: variables); final response = await client.mutate$MoveService(mutation); final jobJson = response.parsedData?.moveService.job?.toJson(); - return GenericJobMutationReturn( - success: response.parsedData?.moveService.success ?? false, + return GenericMutationResult( + success: true, code: response.parsedData?.moveService.code ?? 0, message: response.parsedData?.moveService.message, - job: jobJson != null ? ServerJob.fromJson(jobJson) : null, + data: jobJson != null ? ServerJob.fromJson(jobJson) : null, ); } catch (e) { print(e); - return GenericJobMutationReturn( + return GenericMutationResult( success: false, code: 0, message: e.toString(), - job: null, + data: null, ); } } diff --git a/lib/logic/api_maps/graphql_maps/server_api/users_api.dart b/lib/logic/api_maps/graphql_maps/server_api/users_api.dart index 07da1083..bb46bfef 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/users_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/users_api.dart @@ -1,16 +1,5 @@ part of 'server.dart'; -class UserMutationResult extends GenericMutationResult { - UserMutationResult({ - required super.success, - required super.code, - super.message, - this.user, - }); - - final User? user; -} - mixin UsersApi on ApiMap { Future> getAllUsers() async { QueryResult response; @@ -56,7 +45,7 @@ mixin UsersApi on ApiMap { return user; } - Future createUser( + Future> createUser( final String username, final String password, ) async { @@ -67,25 +56,26 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$CreateUser(variables: variables); final response = await client.mutate$CreateUser(mutation); - return UserMutationResult( - success: response.parsedData?.createUser.success ?? false, + return GenericMutationResult( + success: true, code: response.parsedData?.createUser.code ?? 500, message: response.parsedData?.createUser.message, - user: response.parsedData?.createUser.user != null + data: response.parsedData?.createUser.user != null ? User.fromGraphQL(response.parsedData!.createUser.user!) : null, ); } catch (e) { print(e); - return UserMutationResult( + return GenericMutationResult( success: false, code: 0, message: e.toString(), + data: null, ); } } - Future deleteUser( + Future> deleteUser( final String username, ) async { try { @@ -94,13 +84,15 @@ mixin UsersApi on ApiMap { final mutation = Options$Mutation$DeleteUser(variables: variables); final response = await client.mutate$DeleteUser(mutation); return GenericMutationResult( - success: response.parsedData?.deleteUser.success ?? false, + data: response.parsedData?.deleteUser.success ?? false, + success: true, code: response.parsedData?.deleteUser.code ?? 500, message: response.parsedData?.deleteUser.message, ); } catch (e) { print(e); return GenericMutationResult( + data: false, success: false, code: 500, message: e.toString(), @@ -108,7 +100,7 @@ mixin UsersApi on ApiMap { } } - Future updateUser( + Future> updateUser( final String username, final String password, ) async { @@ -119,17 +111,18 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$UpdateUser(variables: variables); final response = await client.mutate$UpdateUser(mutation); - return UserMutationResult( - success: response.parsedData?.updateUser.success ?? false, + return GenericMutationResult( + success: true, code: response.parsedData?.updateUser.code ?? 500, message: response.parsedData?.updateUser.message, - user: response.parsedData?.updateUser.user != null + data: response.parsedData?.updateUser.user != null ? User.fromGraphQL(response.parsedData!.updateUser.user!) : null, ); } catch (e) { print(e); - return UserMutationResult( + return GenericMutationResult( + data: null, success: false, code: 0, message: e.toString(), @@ -137,7 +130,7 @@ mixin UsersApi on ApiMap { } } - Future addSshKey( + Future> addSshKey( final String username, final String sshKey, ) async { @@ -151,17 +144,18 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$AddSshKey(variables: variables); final response = await client.mutate$AddSshKey(mutation); - return UserMutationResult( - success: response.parsedData?.addSshKey.success ?? false, + return GenericMutationResult( + success: true, code: response.parsedData?.addSshKey.code ?? 500, message: response.parsedData?.addSshKey.message, - user: response.parsedData?.addSshKey.user != null + data: response.parsedData?.addSshKey.user != null ? User.fromGraphQL(response.parsedData!.addSshKey.user!) : null, ); } catch (e) { print(e); - return UserMutationResult( + return GenericMutationResult( + data: null, success: false, code: 0, message: e.toString(), @@ -169,7 +163,7 @@ mixin UsersApi on ApiMap { } } - Future removeSshKey( + Future> removeSshKey( final String username, final String sshKey, ) async { @@ -183,17 +177,18 @@ mixin UsersApi on ApiMap { ); final mutation = Options$Mutation$RemoveSshKey(variables: variables); final response = await client.mutate$RemoveSshKey(mutation); - return UserMutationResult( + return GenericMutationResult( success: response.parsedData?.removeSshKey.success ?? false, code: response.parsedData?.removeSshKey.code ?? 500, message: response.parsedData?.removeSshKey.message, - user: response.parsedData?.removeSshKey.user != null + data: response.parsedData?.removeSshKey.user != null ? User.fromGraphQL(response.parsedData!.removeSshKey.user!) : null, ); } catch (e) { print(e); - return UserMutationResult( + return GenericMutationResult( + data: null, success: false, code: 0, message: e.toString(), diff --git a/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart b/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart index 160a78ca..70119f28 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/volume_api.dart @@ -1,15 +1,5 @@ part of 'server.dart'; -class MigrateToBindsMutationReturn extends GenericMutationResult { - MigrateToBindsMutationReturn({ - required super.success, - required super.code, - super.message, - this.jobUid, - }); - final String? jobUid; -} - mixin VolumeApi on ApiMap { Future> getServerDiskVolumes() async { QueryResult response; @@ -67,10 +57,10 @@ mixin VolumeApi on ApiMap { } } - Future migrateToBinds( + Future> migrateToBinds( final Map serviceToDisk, ) async { - MigrateToBindsMutationReturn? mutation; + GenericMutationResult? mutation; try { final GraphQLClient client = await getClient(); @@ -88,19 +78,19 @@ mixin VolumeApi on ApiMap { await client.mutate$MigrateToBinds( migrateMutation, ); - mutation = mutation = MigrateToBindsMutationReturn( - success: result.parsedData!.migrateToBinds.success, + mutation = mutation = GenericMutationResult( + success: true, code: result.parsedData!.migrateToBinds.code, message: result.parsedData!.migrateToBinds.message, - jobUid: result.parsedData!.migrateToBinds.job?.uid, + data: result.parsedData!.migrateToBinds.job?.uid, ); } catch (e) { print(e); - mutation = MigrateToBindsMutationReturn( + mutation = GenericMutationResult( success: false, code: 0, message: e.toString(), - jobUid: null, + data: null, ); } diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index 3c552c23..065113f1 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -35,7 +35,7 @@ class ApiDevicesCubit } Future?> _getApiTokens() async { - final ApiResponse> response = await api.getApiTokens(); + final GenericResult> response = await api.getApiTokens(); if (response.success) { return response.data; } else { @@ -44,7 +44,7 @@ class ApiDevicesCubit } Future deleteDevice(final ApiToken device) async { - final ApiResponse response = await api.deleteApiToken(device.name); + final GenericResult response = await api.deleteApiToken(device.name); if (response.success) { emit( ApiDevicesState( @@ -59,7 +59,7 @@ class ApiDevicesCubit } Future getNewDeviceKey() async { - final ApiResponse response = await api.createDeviceToken(); + final GenericResult response = await api.createDeviceToken(); if (response.success) { return response.data; } else { diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart index 90f5330d..875d419a 100644 --- a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart +++ b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart @@ -32,7 +32,7 @@ class RecoveryKeyCubit } Future _getRecoveryKeyStatus() async { - final ApiResponse response = + final GenericResult response = await api.getRecoveryTokenStatus(); if (response.success) { return response.data; @@ -57,7 +57,7 @@ class RecoveryKeyCubit final DateTime? expirationDate, final int? numberOfUses, }) async { - final ApiResponse response = + final GenericResult response = await api.generateRecoveryToken(expirationDate, numberOfUses); if (response.success) { refresh(); diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index e8a69988..4bd500fd 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -494,13 +494,13 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final ApiResponse apiResponse = await serverApi.authorizeDevice( + final GenericResult result = await serverApi.authorizeDevice( DeviceToken(device: await getDeviceName(), token: newDeviceKey), ); - if (apiResponse.success) { + if (result.success) { return ServerHostingDetails( - apiToken: apiResponse.data, + apiToken: result.data, volume: ServerVolume( id: 0, name: '', @@ -517,7 +517,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.message ?? apiResponse.data, + result.message ?? result.data, ); } @@ -531,13 +531,13 @@ class ServerInstallationRepository { overrideDomain: serverDomain.domainName, ); final String serverIp = await getServerIpFromDomain(serverDomain); - final ApiResponse apiResponse = await serverApi.useRecoveryToken( + final GenericResult result = await serverApi.useRecoveryToken( DeviceToken(device: await getDeviceName(), token: recoveryKey), ); - if (apiResponse.success) { + if (result.success) { return ServerHostingDetails( - apiToken: apiResponse.data, + apiToken: result.data, volume: ServerVolume( id: 0, name: '', @@ -554,7 +554,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.message ?? apiResponse.data, + result.message ?? result.data, ); } @@ -592,15 +592,15 @@ class ServerInstallationRepository { ); } } - final ApiResponse deviceAuthKey = + final GenericResult deviceAuthKey = await serverApi.createDeviceToken(); - final ApiResponse apiResponse = await serverApi.authorizeDevice( + final GenericResult result = await serverApi.authorizeDevice( DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data), ); - if (apiResponse.success) { + if (result.success) { return ServerHostingDetails( - apiToken: apiResponse.data, + apiToken: result.data, volume: ServerVolume( id: 0, name: '', @@ -617,7 +617,7 @@ class ServerInstallationRepository { } throw ServerAuthorizationException( - apiResponse.message ?? apiResponse.data, + result.message ?? result.data, ); } diff --git a/lib/logic/cubit/server_jobs/server_jobs_cubit.dart b/lib/logic/cubit/server_jobs/server_jobs_cubit.dart index 1e808956..fc102115 100644 --- a/lib/logic/cubit/server_jobs/server_jobs_cubit.dart +++ b/lib/logic/cubit/server_jobs/server_jobs_cubit.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/config/get_it_config.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'; @@ -46,14 +47,14 @@ class ServerJobsCubit Future migrateToBinds(final Map serviceToDisk) async { final result = await api.migrateToBinds(serviceToDisk); - if (!result.success || result.jobUid == null) { + if (result.data == null) { getIt().showSnackBar(result.message!); return; } emit( ServerJobsState( - migrationJobUid: result.jobUid, + migrationJobUid: result.data, ), ); } @@ -75,7 +76,13 @@ class ServerJobsCubit Future removeServerJob(final String uid) async { final result = await api.removeApiJob(uid); if (!result.success) { - getIt().showSnackBar(result.message!); + getIt().showSnackBar('jobs.generic_error'.tr()); + return; + } + + if (!result.data) { + getIt() + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); return; } @@ -87,7 +94,6 @@ class ServerJobsCubit ], ), ); - print('removed job $uid'); } Future removeAllFinishedJobs() async { diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart index 13d3e84d..f76c15a7 100644 --- a/lib/logic/cubit/services/services_cubit.dart +++ b/lib/logic/cubit/services/services_cubit.dart @@ -1,5 +1,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/graphql_maps/server_api/server.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/service.dart'; @@ -39,7 +41,17 @@ class ServicesCubit extends ServerInstallationDependendCubit { Future restart(final String serviceId) async { emit(state.copyWith(lockedServices: [...state.lockedServices, serviceId])); - await api.restartService(serviceId); + final result = await api.restartService(serviceId); + if (!result.success) { + getIt().showSnackBar('jobs.generic_error'.tr()); + return; + } + if (!result.data) { + getIt() + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); + return; + } + await Future.delayed(const Duration(seconds: 2)); reload(); await Future.delayed(const Duration(seconds: 10)); diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 87abcffd..f31580fb 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -78,17 +78,16 @@ class UsersCubit extends ServerInstallationDependendCubit { return; } // If API returned error, do nothing - final UserMutationResult result = + final GenericMutationResult result = await api.createUser(user.login, password); - final User? createdUser = result.user; - if (!result.success || createdUser == null) { + if (result.data == null) { getIt() .showSnackBar(result.message ?? 'users.could_not_create_user'.tr()); return; } final List loadedUsers = List.from(state.users); - loadedUsers.add(createdUser); + loadedUsers.add(result.data!); await box.clear(); await box.addAll(loadedUsers); emit(state.copyWith(users: loadedUsers)); @@ -103,14 +102,20 @@ class UsersCubit extends ServerInstallationDependendCubit { } final List loadedUsers = List.from(state.users); final GenericMutationResult result = await api.deleteUser(user.login); - if (result.success) { + if (result.success && result.data) { loadedUsers.removeWhere((final User u) => u.login == user.login); await box.clear(); await box.addAll(loadedUsers); emit(state.copyWith(users: loadedUsers)); - } else { + } + + if (!result.success) { + getIt().showSnackBar('jobs.generic_error'.tr()); + } + + if (!result.data) { getIt() - .showSnackBar(result.message ?? 'users.could_not_delete_user'.tr()); + .showSnackBar(result.message ?? 'jobs.generic_error'.tr()); } } @@ -123,9 +128,9 @@ class UsersCubit extends ServerInstallationDependendCubit { .showSnackBar('users.could_not_change_password'.tr()); return; } - final UserMutationResult result = + final GenericMutationResult result = await api.updateUser(user.login, newPassword); - if (!result.success) { + if (result.data == null) { getIt().showSnackBar( result.message ?? 'users.could_not_change_password'.tr(), ); @@ -133,10 +138,10 @@ class UsersCubit extends ServerInstallationDependendCubit { } Future addSshKey(final User user, final String publicKey) async { - final UserMutationResult result = + final GenericMutationResult result = await api.addSshKey(user.login, publicKey); - if (result.success) { - final User updatedUser = result.user!; + if (result.data != null) { + final User updatedUser = result.data!; final int index = state.users.indexWhere((final User u) => u.login == user.login); await box.putAt(index, updatedUser); @@ -152,10 +157,10 @@ class UsersCubit extends ServerInstallationDependendCubit { } Future deleteSshKey(final User user, final String publicKey) async { - final UserMutationResult result = + final GenericMutationResult result = await api.removeSshKey(user.login, publicKey); - if (result.success) { - final User updatedUser = result.user!; + if (result.data != null) { + final User updatedUser = result.data!; final int index = state.users.indexWhere((final User u) => u.login == user.login); await box.putAt(index, updatedUser); From 86b80156fd8085a79532773055985e2110341ec3 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Sun, 30 Oct 2022 18:21:38 +0400 Subject: [PATCH 2/2] refactor: Generalize DKIM usage into DnsRecords - Replace raw DKIM String object with a general DnsRecord structure - Implement network utils for common operations with networking concepts and structures - Implement initializing page pop up to re-try server deployment in case of a random networking error --- .../graphql_maps/schema/schema.graphql | 8 + .../graphql_maps/schema/schema.graphql.dart | 216 +++++++++++++++++ .../graphql_maps/schema/schema.graphql.g.dart | 22 ++ .../schema/server_settings.graphql | 6 +- .../schema/server_settings.graphql.dart | 218 ++---------------- .../schema/server_settings.graphql.g.dart | 27 +-- .../graphql_maps/schema/services.graphql | 6 +- .../graphql_maps/schema/services.graphql.dart | 213 ++--------------- .../schema/services.graphql.g.dart | 26 +-- .../graphql_maps/server_api/server.dart | 24 +- .../dns_providers/cloudflare/cloudflare.dart | 13 +- .../rest_maps/dns_providers/dns_provider.dart | 4 +- .../cubit/dns_records/dns_records_cubit.dart | 98 +------- .../cubit/dns_records/dns_records_state.dart | 41 ---- .../server_installation_repository.dart | 10 +- lib/logic/models/json/dns_records.dart | 4 +- lib/logic/models/service.dart | 5 +- lib/ui/pages/dns_details/dns_details.dart | 1 + lib/utils/network_utils.dart | 135 +++++++++++ 19 files changed, 456 insertions(+), 621 deletions(-) create mode 100644 lib/utils/network_utils.dart diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql b/lib/logic/api_maps/graphql_maps/schema/schema.graphql index 2f60c969..5da67b2c 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql @@ -348,4 +348,12 @@ enum UserType { type Users { allUsers: [User!]! getUser(username: String!): User +} + +fragment dnsRecordFields on DnsRecord { + content + name + priority + recordType + ttl } \ No newline at end of file diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart index aa2d2c1e..7187e0e2 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart @@ -1,3 +1,5 @@ +import 'package:gql/ast.dart'; +import 'package:graphql/client.dart' as graphql; import 'package:json_annotation/json_annotation.dart'; import 'package:selfprivacy/utils/scalars.dart'; part 'schema.graphql.g.dart'; @@ -736,6 +738,220 @@ enum Enum$UserType { $unknown } +@JsonSerializable(explicitToJson: true) +class Fragment$dnsRecordFields { + Fragment$dnsRecordFields( + {required this.content, + required this.name, + this.priority, + required this.recordType, + required this.ttl, + required this.$__typename}); + + @override + factory Fragment$dnsRecordFields.fromJson(Map json) => + _$Fragment$dnsRecordFieldsFromJson(json); + + final String content; + + final String name; + + final int? priority; + + final String recordType; + + final int ttl; + + @JsonKey(name: '__typename') + final String $__typename; + + Map toJson() => _$Fragment$dnsRecordFieldsToJson(this); + int get hashCode { + final l$content = content; + final l$name = name; + final l$priority = priority; + final l$recordType = recordType; + final l$ttl = ttl; + final l$$__typename = $__typename; + return Object.hashAll( + [l$content, l$name, l$priority, l$recordType, l$ttl, l$$__typename]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (!(other is Fragment$dnsRecordFields) || + runtimeType != other.runtimeType) return false; + final l$content = content; + final lOther$content = other.content; + if (l$content != lOther$content) return false; + final l$name = name; + final lOther$name = other.name; + if (l$name != lOther$name) return false; + final l$priority = priority; + final lOther$priority = other.priority; + if (l$priority != lOther$priority) return false; + final l$recordType = recordType; + final lOther$recordType = other.recordType; + if (l$recordType != lOther$recordType) return false; + final l$ttl = ttl; + final lOther$ttl = other.ttl; + if (l$ttl != lOther$ttl) return false; + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) return false; + return true; + } +} + +extension UtilityExtension$Fragment$dnsRecordFields + on Fragment$dnsRecordFields { + CopyWith$Fragment$dnsRecordFields get copyWith => + CopyWith$Fragment$dnsRecordFields(this, (i) => i); +} + +abstract class CopyWith$Fragment$dnsRecordFields { + factory CopyWith$Fragment$dnsRecordFields(Fragment$dnsRecordFields instance, + TRes Function(Fragment$dnsRecordFields) then) = + _CopyWithImpl$Fragment$dnsRecordFields; + + factory CopyWith$Fragment$dnsRecordFields.stub(TRes res) = + _CopyWithStubImpl$Fragment$dnsRecordFields; + + TRes call( + {String? content, + String? name, + int? priority, + String? recordType, + int? ttl, + String? $__typename}); +} + +class _CopyWithImpl$Fragment$dnsRecordFields + implements CopyWith$Fragment$dnsRecordFields { + _CopyWithImpl$Fragment$dnsRecordFields(this._instance, this._then); + + final Fragment$dnsRecordFields _instance; + + final TRes Function(Fragment$dnsRecordFields) _then; + + static const _undefined = {}; + + TRes call( + {Object? content = _undefined, + Object? name = _undefined, + Object? priority = _undefined, + Object? recordType = _undefined, + Object? ttl = _undefined, + Object? $__typename = _undefined}) => + _then(Fragment$dnsRecordFields( + content: content == _undefined || content == null + ? _instance.content + : (content as String), + name: name == _undefined || name == null + ? _instance.name + : (name as String), + priority: + priority == _undefined ? _instance.priority : (priority as int?), + recordType: recordType == _undefined || recordType == null + ? _instance.recordType + : (recordType as String), + ttl: ttl == _undefined || ttl == null ? _instance.ttl : (ttl as int), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String))); +} + +class _CopyWithStubImpl$Fragment$dnsRecordFields + implements CopyWith$Fragment$dnsRecordFields { + _CopyWithStubImpl$Fragment$dnsRecordFields(this._res); + + TRes _res; + + call( + {String? content, + String? name, + int? priority, + String? recordType, + int? ttl, + String? $__typename}) => + _res; +} + +const fragmentDefinitiondnsRecordFields = FragmentDefinitionNode( + name: NameNode(value: 'dnsRecordFields'), + typeCondition: TypeConditionNode( + on: NamedTypeNode( + name: NameNode(value: 'DnsRecord'), isNonNull: false)), + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'content'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'name'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'priority'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'recordType'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: 'ttl'), + alias: null, + arguments: [], + directives: [], + selectionSet: null), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null) + ])); +const documentNodeFragmentdnsRecordFields = DocumentNode(definitions: [ + fragmentDefinitiondnsRecordFields, +]); + +extension ClientExtension$Fragment$dnsRecordFields on graphql.GraphQLClient { + void writeFragment$dnsRecordFields( + {required Fragment$dnsRecordFields data, + required Map idFields, + bool broadcast = true}) => + this.writeFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'dnsRecordFields', + document: documentNodeFragmentdnsRecordFields)), + data: data.toJson(), + broadcast: broadcast); + Fragment$dnsRecordFields? readFragment$dnsRecordFields( + {required Map idFields, bool optimistic = true}) { + final result = this.readFragment( + graphql.FragmentRequest( + idFields: idFields, + fragment: const graphql.Fragment( + fragmentName: 'dnsRecordFields', + document: documentNodeFragmentdnsRecordFields)), + optimistic: optimistic); + return result == null ? null : Fragment$dnsRecordFields.fromJson(result); + } +} + const possibleTypesMap = { 'MutationReturnInterface': { 'ApiKeyMutationReturn', diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.g.dart b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.g.dart index d3008d30..7d1280c8 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.g.dart +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.g.dart @@ -123,3 +123,25 @@ Map _$Input$UserMutationInputToJson( 'username': instance.username, 'password': instance.password, }; + +Fragment$dnsRecordFields _$Fragment$dnsRecordFieldsFromJson( + Map json) => + Fragment$dnsRecordFields( + content: json['content'] as String, + name: json['name'] as String, + priority: json['priority'] as int?, + recordType: json['recordType'] as String, + ttl: json['ttl'] as int, + $__typename: json['__typename'] as String, + ); + +Map _$Fragment$dnsRecordFieldsToJson( + Fragment$dnsRecordFields instance) => + { + 'content': instance.content, + 'name': instance.name, + 'priority': instance.priority, + 'recordType': instance.recordType, + 'ttl': instance.ttl, + '__typename': instance.$__typename, + }; diff --git a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql index d8a1d62d..83e5f06e 100644 --- a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql @@ -35,11 +35,7 @@ query DomainInfo { hostname provider requiredDnsRecords { - content - name - priority - recordType - ttl + ...dnsRecordFields } } } diff --git a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart index c0a70151..5d036afa 100644 --- a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart @@ -1444,36 +1444,9 @@ const documentNodeQueryDomainInfo = DocumentNode(definitions: [ arguments: [], directives: [], selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'content'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'name'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'priority'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'recordType'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'ttl'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), + FragmentSpreadNode( + name: NameNode(value: 'dnsRecordFields'), + directives: []), FieldNode( name: NameNode(value: '__typename'), alias: null, @@ -1502,6 +1475,7 @@ const documentNodeQueryDomainInfo = DocumentNode(definitions: [ directives: [], selectionSet: null) ])), + fragmentDefinitiondnsRecordFields, ]); Query$DomainInfo _parserFn$Query$DomainInfo(Map data) => Query$DomainInfo.fromJson(data); @@ -1699,8 +1673,7 @@ class Query$DomainInfo$system$domainInfo { @JsonKey(unknownEnumValue: Enum$DnsProvider.$unknown) final Enum$DnsProvider provider; - final List - requiredDnsRecords; + final List requiredDnsRecords; @JsonKey(name: '__typename') final String $__typename; @@ -1775,14 +1748,12 @@ abstract class CopyWith$Query$DomainInfo$system$domainInfo { {String? domain, String? hostname, Enum$DnsProvider? provider, - List? - requiredDnsRecords, + List? requiredDnsRecords, String? $__typename}); TRes requiredDnsRecords( - Iterable Function( + Iterable Function( Iterable< - CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords< - Query$DomainInfo$system$domainInfo$requiredDnsRecords>>) + CopyWith$Fragment$dnsRecordFields>) _fn); } @@ -1815,21 +1786,20 @@ class _CopyWithImpl$Query$DomainInfo$system$domainInfo requiredDnsRecords: requiredDnsRecords == _undefined || requiredDnsRecords == null ? _instance.requiredDnsRecords - : (requiredDnsRecords as List< - Query$DomainInfo$system$domainInfo$requiredDnsRecords>), + : (requiredDnsRecords as List), $__typename: $__typename == _undefined || $__typename == null ? _instance.$__typename : ($__typename as String))); TRes requiredDnsRecords( - Iterable Function( + Iterable Function( Iterable< - CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords< - Query$DomainInfo$system$domainInfo$requiredDnsRecords>>) + CopyWith$Fragment$dnsRecordFields< + Fragment$dnsRecordFields>>) _fn) => call( - requiredDnsRecords: _fn(_instance.requiredDnsRecords.map((e) => - CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords( - e, (i) => i))).toList()); + requiredDnsRecords: _fn(_instance.requiredDnsRecords + .map((e) => CopyWith$Fragment$dnsRecordFields(e, (i) => i))) + .toList()); } class _CopyWithStubImpl$Query$DomainInfo$system$domainInfo @@ -1842,168 +1812,12 @@ class _CopyWithStubImpl$Query$DomainInfo$system$domainInfo {String? domain, String? hostname, Enum$DnsProvider? provider, - List? - requiredDnsRecords, + List? requiredDnsRecords, String? $__typename}) => _res; requiredDnsRecords(_fn) => _res; } -@JsonSerializable(explicitToJson: true) -class Query$DomainInfo$system$domainInfo$requiredDnsRecords { - Query$DomainInfo$system$domainInfo$requiredDnsRecords( - {required this.content, - required this.name, - this.priority, - required this.recordType, - required this.ttl, - required this.$__typename}); - - @override - factory Query$DomainInfo$system$domainInfo$requiredDnsRecords.fromJson( - Map json) => - _$Query$DomainInfo$system$domainInfo$requiredDnsRecordsFromJson(json); - - final String content; - - final String name; - - final int? priority; - - final String recordType; - - final int ttl; - - @JsonKey(name: '__typename') - final String $__typename; - - Map toJson() => - _$Query$DomainInfo$system$domainInfo$requiredDnsRecordsToJson(this); - int get hashCode { - final l$content = content; - final l$name = name; - final l$priority = priority; - final l$recordType = recordType; - final l$ttl = ttl; - final l$$__typename = $__typename; - return Object.hashAll( - [l$content, l$name, l$priority, l$recordType, l$ttl, l$$__typename]); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (!(other is Query$DomainInfo$system$domainInfo$requiredDnsRecords) || - runtimeType != other.runtimeType) return false; - final l$content = content; - final lOther$content = other.content; - if (l$content != lOther$content) return false; - final l$name = name; - final lOther$name = other.name; - if (l$name != lOther$name) return false; - final l$priority = priority; - final lOther$priority = other.priority; - if (l$priority != lOther$priority) return false; - final l$recordType = recordType; - final lOther$recordType = other.recordType; - if (l$recordType != lOther$recordType) return false; - final l$ttl = ttl; - final lOther$ttl = other.ttl; - if (l$ttl != lOther$ttl) return false; - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) return false; - return true; - } -} - -extension UtilityExtension$Query$DomainInfo$system$domainInfo$requiredDnsRecords - on Query$DomainInfo$system$domainInfo$requiredDnsRecords { - CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords< - Query$DomainInfo$system$domainInfo$requiredDnsRecords> - get copyWith => - CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords( - this, (i) => i); -} - -abstract class CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords< - TRes> { - factory CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords( - Query$DomainInfo$system$domainInfo$requiredDnsRecords instance, - TRes Function(Query$DomainInfo$system$domainInfo$requiredDnsRecords) - then) = - _CopyWithImpl$Query$DomainInfo$system$domainInfo$requiredDnsRecords; - - factory CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords.stub( - TRes res) = - _CopyWithStubImpl$Query$DomainInfo$system$domainInfo$requiredDnsRecords; - - TRes call( - {String? content, - String? name, - int? priority, - String? recordType, - int? ttl, - String? $__typename}); -} - -class _CopyWithImpl$Query$DomainInfo$system$domainInfo$requiredDnsRecords - implements - CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords { - _CopyWithImpl$Query$DomainInfo$system$domainInfo$requiredDnsRecords( - this._instance, this._then); - - final Query$DomainInfo$system$domainInfo$requiredDnsRecords _instance; - - final TRes Function(Query$DomainInfo$system$domainInfo$requiredDnsRecords) - _then; - - static const _undefined = {}; - - TRes call( - {Object? content = _undefined, - Object? name = _undefined, - Object? priority = _undefined, - Object? recordType = _undefined, - Object? ttl = _undefined, - Object? $__typename = _undefined}) => - _then(Query$DomainInfo$system$domainInfo$requiredDnsRecords( - content: content == _undefined || content == null - ? _instance.content - : (content as String), - name: name == _undefined || name == null - ? _instance.name - : (name as String), - priority: - priority == _undefined ? _instance.priority : (priority as int?), - recordType: recordType == _undefined || recordType == null - ? _instance.recordType - : (recordType as String), - ttl: ttl == _undefined || ttl == null ? _instance.ttl : (ttl as int), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String))); -} - -class _CopyWithStubImpl$Query$DomainInfo$system$domainInfo$requiredDnsRecords< - TRes> - implements - CopyWith$Query$DomainInfo$system$domainInfo$requiredDnsRecords { - _CopyWithStubImpl$Query$DomainInfo$system$domainInfo$requiredDnsRecords( - this._res); - - TRes _res; - - call( - {String? content, - String? name, - int? priority, - String? recordType, - int? ttl, - String? $__typename}) => - _res; -} - @JsonSerializable(explicitToJson: true) class Variables$Mutation$ChangeTimezone { Variables$Mutation$ChangeTimezone({required this.timezone}); diff --git a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.g.dart b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.g.dart index fc66954b..c928b177 100644 --- a/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.g.dart +++ b/lib/logic/api_maps/graphql_maps/schema/server_settings.graphql.g.dart @@ -190,8 +190,7 @@ Query$DomainInfo$system$domainInfo _$Query$DomainInfo$system$domainInfoFromJson( unknownValue: Enum$DnsProvider.$unknown), requiredDnsRecords: (json['requiredDnsRecords'] as List) .map((e) => - Query$DomainInfo$system$domainInfo$requiredDnsRecords.fromJson( - e as Map)) + Fragment$dnsRecordFields.fromJson(e as Map)) .toList(), $__typename: json['__typename'] as String, ); @@ -212,30 +211,6 @@ const _$Enum$DnsProviderEnumMap = { Enum$DnsProvider.$unknown: r'$unknown', }; -Query$DomainInfo$system$domainInfo$requiredDnsRecords - _$Query$DomainInfo$system$domainInfo$requiredDnsRecordsFromJson( - Map json) => - Query$DomainInfo$system$domainInfo$requiredDnsRecords( - content: json['content'] as String, - name: json['name'] as String, - priority: json['priority'] as int?, - recordType: json['recordType'] as String, - ttl: json['ttl'] as int, - $__typename: json['__typename'] as String, - ); - -Map - _$Query$DomainInfo$system$domainInfo$requiredDnsRecordsToJson( - Query$DomainInfo$system$domainInfo$requiredDnsRecords instance) => - { - 'content': instance.content, - 'name': instance.name, - 'priority': instance.priority, - 'recordType': instance.recordType, - 'ttl': instance.ttl, - '__typename': instance.$__typename, - }; - Variables$Mutation$ChangeTimezone _$Variables$Mutation$ChangeTimezoneFromJson( Map json) => Variables$Mutation$ChangeTimezone( diff --git a/lib/logic/api_maps/graphql_maps/schema/services.graphql b/lib/logic/api_maps/graphql_maps/schema/services.graphql index ce464426..7386c362 100644 --- a/lib/logic/api_maps/graphql_maps/schema/services.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/services.graphql @@ -10,11 +10,7 @@ query AllServices { description displayName dnsRecords { - content - name - priority - recordType - ttl + ...dnsRecordFields } id isEnabled diff --git a/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart index 411186ad..a31058c4 100644 --- a/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart @@ -312,36 +312,9 @@ const documentNodeQueryAllServices = DocumentNode(definitions: [ arguments: [], directives: [], selectionSet: SelectionSetNode(selections: [ - FieldNode( - name: NameNode(value: 'content'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'name'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'priority'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'recordType'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), - FieldNode( - name: NameNode(value: 'ttl'), - alias: null, - arguments: [], - directives: [], - selectionSet: null), + FragmentSpreadNode( + name: NameNode(value: 'dnsRecordFields'), + directives: []), FieldNode( name: NameNode(value: '__typename'), alias: null, @@ -456,6 +429,7 @@ const documentNodeQueryAllServices = DocumentNode(definitions: [ directives: [], selectionSet: null) ])), + fragmentDefinitiondnsRecordFields, ]); Query$AllServices _parserFn$Query$AllServices(Map data) => Query$AllServices.fromJson(data); @@ -679,7 +653,7 @@ class Query$AllServices$services$allServices { final String displayName; - final List? dnsRecords; + final List? dnsRecords; final String id; @@ -807,7 +781,7 @@ abstract class CopyWith$Query$AllServices$services$allServices { TRes call( {String? description, String? displayName, - List? dnsRecords, + List? dnsRecords, String? id, bool? isEnabled, bool? isMovable, @@ -818,10 +792,9 @@ abstract class CopyWith$Query$AllServices$services$allServices { String? url, String? $__typename}); TRes dnsRecords( - Iterable? Function( + Iterable? Function( Iterable< - CopyWith$Query$AllServices$services$allServices$dnsRecords< - Query$AllServices$services$allServices$dnsRecords>>?) + CopyWith$Fragment$dnsRecordFields>?) _fn); CopyWith$Query$AllServices$services$allServices$storageUsage get storageUsage; @@ -860,8 +833,7 @@ class _CopyWithImpl$Query$AllServices$services$allServices : (displayName as String), dnsRecords: dnsRecords == _undefined ? _instance.dnsRecords - : (dnsRecords - as List?), + : (dnsRecords as List?), id: id == _undefined || id == null ? _instance.id : (id as String), isEnabled: isEnabled == _undefined || isEnabled == null ? _instance.isEnabled @@ -887,15 +859,15 @@ class _CopyWithImpl$Query$AllServices$services$allServices ? _instance.$__typename : ($__typename as String))); TRes dnsRecords( - Iterable? Function( + Iterable? Function( Iterable< - CopyWith$Query$AllServices$services$allServices$dnsRecords< - Query$AllServices$services$allServices$dnsRecords>>?) + CopyWith$Fragment$dnsRecordFields< + Fragment$dnsRecordFields>>?) _fn) => call( - dnsRecords: _fn(_instance.dnsRecords?.map((e) => - CopyWith$Query$AllServices$services$allServices$dnsRecords( - e, (i) => i)))?.toList()); + dnsRecords: _fn(_instance.dnsRecords + ?.map((e) => CopyWith$Fragment$dnsRecordFields(e, (i) => i))) + ?.toList()); CopyWith$Query$AllServices$services$allServices$storageUsage get storageUsage { final local$storageUsage = _instance.storageUsage; @@ -913,7 +885,7 @@ class _CopyWithStubImpl$Query$AllServices$services$allServices call( {String? description, String? displayName, - List? dnsRecords, + List? dnsRecords, String? id, bool? isEnabled, bool? isMovable, @@ -931,159 +903,6 @@ class _CopyWithStubImpl$Query$AllServices$services$allServices _res); } -@JsonSerializable(explicitToJson: true) -class Query$AllServices$services$allServices$dnsRecords { - Query$AllServices$services$allServices$dnsRecords( - {required this.content, - required this.name, - this.priority, - required this.recordType, - required this.ttl, - required this.$__typename}); - - @override - factory Query$AllServices$services$allServices$dnsRecords.fromJson( - Map json) => - _$Query$AllServices$services$allServices$dnsRecordsFromJson(json); - - final String content; - - final String name; - - final int? priority; - - final String recordType; - - final int ttl; - - @JsonKey(name: '__typename') - final String $__typename; - - Map toJson() => - _$Query$AllServices$services$allServices$dnsRecordsToJson(this); - int get hashCode { - final l$content = content; - final l$name = name; - final l$priority = priority; - final l$recordType = recordType; - final l$ttl = ttl; - final l$$__typename = $__typename; - return Object.hashAll( - [l$content, l$name, l$priority, l$recordType, l$ttl, l$$__typename]); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) return true; - if (!(other is Query$AllServices$services$allServices$dnsRecords) || - runtimeType != other.runtimeType) return false; - final l$content = content; - final lOther$content = other.content; - if (l$content != lOther$content) return false; - final l$name = name; - final lOther$name = other.name; - if (l$name != lOther$name) return false; - final l$priority = priority; - final lOther$priority = other.priority; - if (l$priority != lOther$priority) return false; - final l$recordType = recordType; - final lOther$recordType = other.recordType; - if (l$recordType != lOther$recordType) return false; - final l$ttl = ttl; - final lOther$ttl = other.ttl; - if (l$ttl != lOther$ttl) return false; - final l$$__typename = $__typename; - final lOther$$__typename = other.$__typename; - if (l$$__typename != lOther$$__typename) return false; - return true; - } -} - -extension UtilityExtension$Query$AllServices$services$allServices$dnsRecords - on Query$AllServices$services$allServices$dnsRecords { - CopyWith$Query$AllServices$services$allServices$dnsRecords< - Query$AllServices$services$allServices$dnsRecords> - get copyWith => - CopyWith$Query$AllServices$services$allServices$dnsRecords( - this, (i) => i); -} - -abstract class CopyWith$Query$AllServices$services$allServices$dnsRecords< - TRes> { - factory CopyWith$Query$AllServices$services$allServices$dnsRecords( - Query$AllServices$services$allServices$dnsRecords instance, - TRes Function(Query$AllServices$services$allServices$dnsRecords) - then) = - _CopyWithImpl$Query$AllServices$services$allServices$dnsRecords; - - factory CopyWith$Query$AllServices$services$allServices$dnsRecords.stub( - TRes res) = - _CopyWithStubImpl$Query$AllServices$services$allServices$dnsRecords; - - TRes call( - {String? content, - String? name, - int? priority, - String? recordType, - int? ttl, - String? $__typename}); -} - -class _CopyWithImpl$Query$AllServices$services$allServices$dnsRecords - implements - CopyWith$Query$AllServices$services$allServices$dnsRecords { - _CopyWithImpl$Query$AllServices$services$allServices$dnsRecords( - this._instance, this._then); - - final Query$AllServices$services$allServices$dnsRecords _instance; - - final TRes Function(Query$AllServices$services$allServices$dnsRecords) _then; - - static const _undefined = {}; - - TRes call( - {Object? content = _undefined, - Object? name = _undefined, - Object? priority = _undefined, - Object? recordType = _undefined, - Object? ttl = _undefined, - Object? $__typename = _undefined}) => - _then(Query$AllServices$services$allServices$dnsRecords( - content: content == _undefined || content == null - ? _instance.content - : (content as String), - name: name == _undefined || name == null - ? _instance.name - : (name as String), - priority: - priority == _undefined ? _instance.priority : (priority as int?), - recordType: recordType == _undefined || recordType == null - ? _instance.recordType - : (recordType as String), - ttl: ttl == _undefined || ttl == null ? _instance.ttl : (ttl as int), - $__typename: $__typename == _undefined || $__typename == null - ? _instance.$__typename - : ($__typename as String))); -} - -class _CopyWithStubImpl$Query$AllServices$services$allServices$dnsRecords - implements - CopyWith$Query$AllServices$services$allServices$dnsRecords { - _CopyWithStubImpl$Query$AllServices$services$allServices$dnsRecords( - this._res); - - TRes _res; - - call( - {String? content, - String? name, - int? priority, - String? recordType, - int? ttl, - String? $__typename}) => - _res; -} - @JsonSerializable(explicitToJson: true) class Query$AllServices$services$allServices$storageUsage { Query$AllServices$services$allServices$storageUsage( diff --git a/lib/logic/api_maps/graphql_maps/schema/services.graphql.g.dart b/lib/logic/api_maps/graphql_maps/schema/services.graphql.g.dart index 405ab468..52663401 100644 --- a/lib/logic/api_maps/graphql_maps/schema/services.graphql.g.dart +++ b/lib/logic/api_maps/graphql_maps/schema/services.graphql.g.dart @@ -62,8 +62,7 @@ Query$AllServices$services$allServices displayName: json['displayName'] as String, dnsRecords: (json['dnsRecords'] as List?) ?.map((e) => - Query$AllServices$services$allServices$dnsRecords.fromJson( - e as Map)) + Fragment$dnsRecordFields.fromJson(e as Map)) .toList(), id: json['id'] as String, isEnabled: json['isEnabled'] as bool, @@ -107,29 +106,6 @@ const _$Enum$ServiceStatusEnumEnumMap = { Enum$ServiceStatusEnum.$unknown: r'$unknown', }; -Query$AllServices$services$allServices$dnsRecords - _$Query$AllServices$services$allServices$dnsRecordsFromJson( - Map json) => - Query$AllServices$services$allServices$dnsRecords( - content: json['content'] as String, - name: json['name'] as String, - priority: json['priority'] as int?, - recordType: json['recordType'] as String, - ttl: json['ttl'] as int, - $__typename: json['__typename'] as String, - ); - -Map _$Query$AllServices$services$allServices$dnsRecordsToJson( - Query$AllServices$services$allServices$dnsRecords instance) => - { - 'content': instance.content, - 'name': instance.name, - 'priority': instance.priority, - 'recordType': instance.recordType, - 'ttl': instance.ttl, - '__typename': instance.$__typename, - }; - Query$AllServices$services$allServices$storageUsage _$Query$AllServices$services$allServices$storageUsageFromJson( Map json) => 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 82aa24be..c2cda13b 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server.dart @@ -13,6 +13,7 @@ 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/dns_records.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'; @@ -256,11 +257,8 @@ 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; + Future> getDnsRecords() async { + List records = []; QueryResult response; try { @@ -269,21 +267,17 @@ class ServerApi extends ApiMap 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', + records = response.parsedData!.system.domainInfo.requiredDnsRecords + .map( + (final Fragment$dnsRecordFields fragment) => + DnsRecord.fromGraphQL(fragment), ) - .content; + .toList(); } catch (e) { print(e); } - return dkim; + return records; } Future>> getApiTokens() async { diff --git a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart index 3088de67..c79f0987 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart @@ -242,25 +242,18 @@ class CloudflareApi extends DnsProviderApi { } @override - Future setDkim( - final String dkimRecordString, + Future setDnsRecord( + final DnsRecord record, final ServerDomain domain, ) async { final String domainZoneId = domain.zoneId; final String url = '$rootAddress/zones/$domainZoneId/dns_records'; - final DnsRecord dkimRecord = DnsRecord( - type: 'TXT', - name: 'selector._domainkey', - content: dkimRecordString, - ttl: 18000, - ); - final Dio client = await getClient(); try { await client.post( url, - data: dkimRecord.toJson(), + data: record.toJson(), ); } catch (e) { print(e); diff --git a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart index 6680975e..3ff6222e 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart @@ -19,8 +19,8 @@ abstract class DnsProviderApi extends ApiMap { required final ServerDomain domain, final String? ip4, }); - Future setDkim( - final String dkimRecordString, + Future setDnsRecord( + final DnsRecord record, final ServerDomain domain, ); Future getZoneId(final String domain); diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 29d19d81..6159b9be 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -7,6 +7,7 @@ import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; part 'dns_records_state.dart'; @@ -30,7 +31,7 @@ class DnsRecordsCubit emit( DnsRecordsState( dnsState: DnsRecordsStatus.refreshing, - dnsRecords: _getDesiredDnsRecords( + dnsRecords: getDesiredDnsRecords( serverInstallationCubit.state.serverDomain?.domainName, '', '', @@ -46,9 +47,10 @@ class DnsRecordsCubit final List records = await dnsProviderApiFactory! .getDnsProvider() .getDnsRecords(domain: domain); - final String? dkimPublicKey = await api.getDkim(); + final String? dkimPublicKey = + extractDkimRecord(await api.getDnsRecords())?.content; final List desiredRecords = - _getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey); + getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey); final List foundRecords = []; for (final DesiredDnsRecord record in desiredRecords) { if (record.description == 'record.dkim') { @@ -121,7 +123,6 @@ class DnsRecordsCubit emit(state.copyWith(dnsState: DnsRecordsStatus.refreshing)); final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4; - final String? dkimPublicKey = await api.getDkim(); final DnsProviderApi dnsProviderApi = dnsProviderApiFactory!.getDnsProvider(); await dnsProviderApi.removeSimilarRecords(domain: domain!); @@ -129,88 +130,13 @@ class DnsRecordsCubit domain: domain, ip4: ipAddress, ); - await dnsProviderApi.setDkim(dkimPublicKey ?? '', domain); + + final List records = await api.getDnsRecords(); + final DnsRecord? dkimRecord = extractDkimRecord(records); + if (dkimRecord != null) { + await dnsProviderApi.setDnsRecord(dkimRecord, domain); + } + await load(); } - - List _getDesiredDnsRecords( - final String? domainName, - final String? ipAddress, - final String? dkimPublicKey, - ) { - if (domainName == null || ipAddress == null) { - return []; - } - return [ - DesiredDnsRecord( - name: domainName, - content: ipAddress, - description: 'record.root', - ), - DesiredDnsRecord( - name: 'api.$domainName', - content: ipAddress, - description: 'record.api', - ), - DesiredDnsRecord( - name: 'cloud.$domainName', - content: ipAddress, - description: 'record.cloud', - ), - DesiredDnsRecord( - name: 'git.$domainName', - content: ipAddress, - description: 'record.git', - ), - DesiredDnsRecord( - name: 'meet.$domainName', - content: ipAddress, - description: 'record.meet', - ), - DesiredDnsRecord( - name: 'social.$domainName', - content: ipAddress, - description: 'record.social', - ), - DesiredDnsRecord( - name: 'password.$domainName', - content: ipAddress, - description: 'record.password', - ), - DesiredDnsRecord( - name: 'vpn.$domainName', - content: ipAddress, - description: 'record.vpn', - ), - DesiredDnsRecord( - name: domainName, - content: domainName, - description: 'record.mx', - type: 'MX', - category: DnsRecordsCategory.email, - ), - DesiredDnsRecord( - name: '_dmarc.$domainName', - content: 'v=DMARC1; p=none', - description: 'record.dmarc', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - DesiredDnsRecord( - name: domainName, - content: 'v=spf1 a mx ip4:$ipAddress -all', - description: 'record.spf', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - if (dkimPublicKey != null) - DesiredDnsRecord( - name: 'selector._domainkey.$domainName', - content: dkimPublicKey, - description: 'record.dkim', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - ]; - } } diff --git a/lib/logic/cubit/dns_records/dns_records_state.dart b/lib/logic/cubit/dns_records/dns_records_state.dart index 4b39d014..222dda66 100644 --- a/lib/logic/cubit/dns_records/dns_records_state.dart +++ b/lib/logic/cubit/dns_records/dns_records_state.dart @@ -7,12 +7,6 @@ enum DnsRecordsStatus { error, } -enum DnsRecordsCategory { - services, - email, - other, -} - class DnsRecordsState extends ServerInstallationDependendState { const DnsRecordsState({ this.dnsState = DnsRecordsStatus.uninitialized, @@ -37,38 +31,3 @@ class DnsRecordsState extends ServerInstallationDependendState { dnsRecords: dnsRecords ?? this.dnsRecords, ); } - -class DesiredDnsRecord { - const DesiredDnsRecord({ - required this.name, - required this.content, - this.type = 'A', - this.description = '', - this.category = DnsRecordsCategory.services, - this.isSatisfied = false, - }); - - final String name; - final String type; - final String content; - final String description; - final DnsRecordsCategory category; - final bool isSatisfied; - - DesiredDnsRecord copyWith({ - final String? name, - final String? type, - final String? content, - final String? description, - final DnsRecordsCategory? category, - final bool? isSatisfied, - }) => - DesiredDnsRecord( - name: name ?? this.name, - type: type ?? this.type, - content: content ?? this.content, - description: description ?? this.description, - category: category ?? this.category, - isSatisfied: isSatisfied ?? this.isSatisfied, - ); -} diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 4bd500fd..c4a3e7ff 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -21,10 +21,12 @@ import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/json/device_token.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/models/message.dart'; import 'package:selfprivacy/logic/models/server_basic_info.dart'; import 'package:selfprivacy/ui/components/action_button/action_button.dart'; import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; class IpNotFoundException implements Exception { IpNotFoundException(this.message); @@ -286,7 +288,7 @@ class ServerInstallationRepository { ], ), ); - } else if (e.response!.data['error']['code'] == 'resource_unavailable') { + } else { final NavigationService nav = getIt.get(); nav.showPopUpDialog( BrandAlert( @@ -390,15 +392,15 @@ class ServerInstallationRepository { dnsProviderApiFactory!.getDnsProvider(); final ServerApi api = ServerApi(); - String dkimRecordString = ''; + late DnsRecord record; try { - dkimRecordString = (await api.getDkim())!; + record = extractDkimRecord(await api.getDnsRecords())!; } catch (e) { print(e); rethrow; } - await dnsProviderApi.setDkim(dkimRecordString, cloudFlareDomain); + await dnsProviderApi.setDnsRecord(record, cloudFlareDomain); } Future isHttpServerWorking() async { diff --git a/lib/logic/models/json/dns_records.dart b/lib/logic/models/json/dns_records.dart index 1bb385b5..c4799876 100644 --- a/lib/logic/models/json/dns_records.dart +++ b/lib/logic/models/json/dns_records.dart @@ -1,5 +1,5 @@ import 'package:json_annotation/json_annotation.dart'; -import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; part 'dns_records.g.dart'; @@ -15,7 +15,7 @@ class DnsRecord { }); DnsRecord.fromGraphQL( - final Query$AllServices$services$allServices$dnsRecords record, + final Fragment$dnsRecordFields record, ) : this( type: record.recordType, name: record.name, diff --git a/lib/logic/models/service.dart b/lib/logic/models/service.dart index 79bd8f7e..898d2965 100644 --- a/lib/logic/models/service.dart +++ b/lib/logic/models/service.dart @@ -23,7 +23,10 @@ class Service { // Decode the base64 encoded svg icon to text. svgIcon: utf8.decode(base64.decode(service.svgIcon)), dnsRecords: service.dnsRecords - ?.map((final record) => DnsRecord.fromGraphQL(record)) + ?.map( + (final Fragment$dnsRecordFields record) => + DnsRecord.fromGraphQL(record), + ) .toList() ?? [], url: service.url, diff --git a/lib/ui/pages/dns_details/dns_details.dart b/lib/ui/pages/dns_details/dns_details.dart index 8bf264d6..692921eb 100644 --- a/lib/ui/pages/dns_details/dns_details.dart +++ b/lib/ui/pages/dns_details/dns_details.dart @@ -6,6 +6,7 @@ import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart'; import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; class DnsDetailsPage extends StatefulWidget { const DnsDetailsPage({super.key}); diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart new file mode 100644 index 00000000..8b75728f --- /dev/null +++ b/lib/utils/network_utils.dart @@ -0,0 +1,135 @@ +import 'package:selfprivacy/logic/models/json/dns_records.dart'; + +enum DnsRecordsCategory { + services, + email, + other, +} + +class DesiredDnsRecord { + const DesiredDnsRecord({ + required this.name, + required this.content, + this.type = 'A', + this.description = '', + this.category = DnsRecordsCategory.services, + this.isSatisfied = false, + }); + + final String name; + final String type; + final String content; + final String description; + final DnsRecordsCategory category; + final bool isSatisfied; + + DesiredDnsRecord copyWith({ + final String? name, + final String? type, + final String? content, + final String? description, + final DnsRecordsCategory? category, + final bool? isSatisfied, + }) => + DesiredDnsRecord( + name: name ?? this.name, + type: type ?? this.type, + content: content ?? this.content, + description: description ?? this.description, + category: category ?? this.category, + isSatisfied: isSatisfied ?? this.isSatisfied, + ); +} + +List getDesiredDnsRecords( + final String? domainName, + final String? ipAddress, + final String? dkimPublicKey, +) { + if (domainName == null || ipAddress == null) { + return []; + } + return [ + DesiredDnsRecord( + name: domainName, + content: ipAddress, + description: 'record.root', + ), + DesiredDnsRecord( + name: 'api.$domainName', + content: ipAddress, + description: 'record.api', + ), + DesiredDnsRecord( + name: 'cloud.$domainName', + content: ipAddress, + description: 'record.cloud', + ), + DesiredDnsRecord( + name: 'git.$domainName', + content: ipAddress, + description: 'record.git', + ), + DesiredDnsRecord( + name: 'meet.$domainName', + content: ipAddress, + description: 'record.meet', + ), + DesiredDnsRecord( + name: 'social.$domainName', + content: ipAddress, + description: 'record.social', + ), + DesiredDnsRecord( + name: 'password.$domainName', + content: ipAddress, + description: 'record.password', + ), + DesiredDnsRecord( + name: 'vpn.$domainName', + content: ipAddress, + description: 'record.vpn', + ), + DesiredDnsRecord( + name: domainName, + content: domainName, + description: 'record.mx', + type: 'MX', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: '_dmarc.$domainName', + content: 'v=DMARC1; p=none', + description: 'record.dmarc', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + DesiredDnsRecord( + name: domainName, + content: 'v=spf1 a mx ip4:$ipAddress -all', + description: 'record.spf', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + if (dkimPublicKey != null) + DesiredDnsRecord( + name: 'selector._domainkey.$domainName', + content: dkimPublicKey, + description: 'record.dkim', + type: 'TXT', + category: DnsRecordsCategory.email, + ), + ]; +} + +DnsRecord? extractDkimRecord(final List records) { + DnsRecord? dkimRecord; + + for (final DnsRecord record in records) { + if (record.type == 'TXT' && record.name == 'selector._domainkey') { + dkimRecord = record; + } + } + + return dkimRecord; +}