From 234064ed72820d6721279b267c94ae7e86d51d6c Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 9 May 2023 03:15:48 -0300 Subject: [PATCH 01/10] feat: Implement infrastructure for new DNS provider deSEC --- assets/images/logos/desec.svg | 89 +++++ lib/config/hive_config.dart | 3 + .../rest_maps/api_factory_creator.dart | 3 + .../rest_maps/dns_providers/desec/desec.dart | 287 ++++++++++++++++ .../dns_providers/desec/desec_factory.dart | 16 + .../server_installation_cubit.dart | 9 + .../server_installation_repository.dart | 4 + lib/logic/get_it/api_config.dart | 10 + lib/logic/models/hive/server_domain.dart | 2 + .../initializing/dns_provider_picker.dart | 307 ++++++++++++++++++ .../setup/initializing/initializing.dart | 60 +--- 11 files changed, 742 insertions(+), 48 deletions(-) create mode 100644 assets/images/logos/desec.svg create mode 100644 lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart create mode 100644 lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart create mode 100644 lib/ui/pages/setup/initializing/dns_provider_picker.dart diff --git a/assets/images/logos/desec.svg b/assets/images/logos/desec.svg new file mode 100644 index 00000000..cb54b268 --- /dev/null +++ b/assets/images/logos/desec.svg @@ -0,0 +1,89 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index b6ba018c..44b03f26 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -93,6 +93,9 @@ class BNames { /// A String field of [serverInstallationBox] box. static String serverProvider = 'serverProvider'; + /// A String field of [serverInstallationBox] box. + static String dnsProvider = 'dnsProvider'; + /// A String field of [serverLocation] box. static String serverLocation = 'serverLocation'; diff --git a/lib/logic/api_maps/rest_maps/api_factory_creator.dart b/lib/logic/api_maps/rest_maps/api_factory_creator.dart index 25518f3c..c1762429 100644 --- a/lib/logic/api_maps/rest_maps/api_factory_creator.dart +++ b/lib/logic/api_maps/rest_maps/api_factory_creator.dart @@ -1,5 +1,6 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_factory.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_factory.dart'; @@ -30,6 +31,8 @@ class ApiFactoryCreator { final DnsProviderApiFactorySettings settings, ) { switch (settings.provider) { + case DnsProvider.desec: + return DesecApiFactory(); case DnsProvider.cloudflare: return CloudflareApiFactory(); case DnsProvider.unknown: diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart new file mode 100644 index 00000000..1906be55 --- /dev/null +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart @@ -0,0 +1,287 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; + +class DesecApi extends DnsProviderApi { + DesecApi({ + this.hasLogger = false, + this.isWithToken = true, + this.customToken, + }); + @override + final bool hasLogger; + @override + final bool isWithToken; + + final String? customToken; + + @override + RegExp getApiTokenValidation() => + RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); + + @override + BaseOptions get options { + final BaseOptions options = BaseOptions(baseUrl: rootAddress); + if (isWithToken) { + final String? token = getIt().cloudFlareKey; + assert(token != null); + options.headers = {'Authorization': 'Bearer $token'}; + } + + if (customToken != null) { + options.headers = {'Authorization': 'Bearer $customToken'}; + } + + if (validateStatus != null) { + options.validateStatus = validateStatus!; + } + return options; + } + + @override + String rootAddress = 'https://desec.io/api/v1/domains/'; + + @override + Future> isApiTokenValid(final String token) async { + bool isValid = false; + Response? response; + String message = ''; + final Dio client = await getClient(); + try { + response = await client.get( + '', + options: Options( + followRedirects: false, + validateStatus: (final status) => + status != null && (status >= 200 || status == 401), + headers: {'Authorization': 'Token $token'}, + ), + ); + } catch (e) { + print(e); + isValid = false; + message = e.toString(); + } finally { + close(client); + } + + if (response == null) { + return APIGenericResult( + data: isValid, + success: false, + message: message, + ); + } + + if (response.statusCode == HttpStatus.ok) { + isValid = true; + } else if (response.statusCode == HttpStatus.unauthorized) { + isValid = false; + } else { + throw Exception('code: ${response.statusCode}'); + } + + return APIGenericResult( + data: isValid, + success: true, + message: response.statusMessage, + ); + } + + @override + Future getZoneId(final String domain) async => domain; + + @override + Future> removeSimilarRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final String domainName = domain.domainName; + final String url = '/$domainName/rrsets/'; + + final Dio client = await getClient(); + try { + final Response response = await client.get(url); + + final List records = response.data['result'] ?? []; + await client.put(url, data: records); + } catch (e) { + print(e); + return APIGenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return APIGenericResult(success: true, data: null); + } + + @override + Future> getDnsRecords({ + required final ServerDomain domain, + }) async { + Response response; + final String domainName = domain.domainName; + final List allRecords = []; + + final String url = '/$domainName/rrsets/'; + + final Dio client = await getClient(); + try { + response = await client.get(url); + final List records = response.data['result'] ?? []; + + for (final record in records) { + allRecords.add( + DnsRecord( + name: record['subname'], + type: record['type'], + content: record['records'], + ttl: record['ttl'], + ), + ); + } + } catch (e) { + print(e); + } finally { + close(client); + } + + return allRecords; + } + + @override + Future> createMultipleDnsRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final String domainName = domain.domainName; + final List listDnsRecords = projectDnsRecords(domainName, ip4); + final List allCreateFutures = []; + + final Dio client = await getClient(); + try { + for (final DnsRecord record in listDnsRecords) { + allCreateFutures.add( + client.post( + '/$domainName/rrsets/', + data: record.toJson(), + ), + ); + } + await Future.wait(allCreateFutures); + } on DioError catch (e) { + print(e.message); + rethrow; + } catch (e) { + print(e); + return APIGenericResult( + success: false, + data: null, + message: e.toString(), + ); + } finally { + close(client); + } + + return APIGenericResult(success: true, data: null); + } + + List projectDnsRecords( + final String? domainName, + final String? ip4, + ) { + final DnsRecord domainA = + DnsRecord(type: 'A', name: domainName, content: ip4); + + final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: domainName); + final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4); + final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4); + final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4); + final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4); + final DnsRecord passwordA = + DnsRecord(type: 'A', name: 'password', content: ip4); + final DnsRecord socialA = + DnsRecord(type: 'A', name: 'social', content: ip4); + final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4); + + final DnsRecord txt1 = DnsRecord( + type: 'TXT', + name: '_dmarc', + content: 'v=DMARC1; p=none', + ttl: 18000, + ); + + final DnsRecord txt2 = DnsRecord( + type: 'TXT', + name: domainName, + content: 'v=spf1 a mx ip4:$ip4 -all', + ttl: 18000, + ); + + return [ + domainA, + apiA, + cloudA, + gitA, + meetA, + passwordA, + socialA, + mx, + txt1, + txt2, + vpn + ]; + } + + @override + Future setDnsRecord( + final DnsRecord record, + final ServerDomain domain, + ) async { + final String domainZoneId = domain.zoneId; + final String url = '$rootAddress/zones/$domainZoneId/dns_records'; + + final Dio client = await getClient(); + try { + await client.post( + url, + data: record.toJson(), + ); + } catch (e) { + print(e); + } finally { + close(client); + } + } + + @override + Future> domainList() async { + final String url = '$rootAddress/zones'; + List domains = []; + + final Dio client = await getClient(); + try { + final Response response = await client.get( + url, + queryParameters: {'per_page': 50}, + ); + domains = response.data['result'] + .map((final el) => el['name'] as String) + .toList(); + } catch (e) { + print(e); + } finally { + close(client); + } + + return domains; + } +} diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart new file mode 100644 index 00000000..6c10259b --- /dev/null +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart @@ -0,0 +1,16 @@ +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec.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_api_settings.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart'; + +class DesecApiFactory extends DnsProviderApiFactory { + @override + DnsProviderApi getDnsProvider({ + final DnsProviderApiSettings settings = const DnsProviderApiSettings(), + }) => + DesecApi( + hasLogger: settings.hasLogger, + isWithToken: settings.isWithToken, + customToken: settings.customToken, + ); +} diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 5638b765..06df6d5a 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -66,6 +66,15 @@ class ServerInstallationCubit extends Cubit { ); } + void setDnsProviderType(final DnsProvider providerType) async { + await repository.saveDnsProviderType(providerType); + ApiController.initDnsProviderApiFactory( + DnsProviderApiFactorySettings( + provider: providerType, + ), + ); + } + ProviderApiTokenValidation serverProviderApiTokenValidation() => ApiController.currentServerProviderApiFactory! .getServerProvider() diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 5d45e7b9..851b2be5 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -706,6 +706,10 @@ class ServerInstallationRepository { getIt().init(); } + Future saveDnsProviderType(final DnsProvider type) async { + await getIt().storeDnsProviderType(type); + } + Future saveBackblazeKey( final BackblazeCredential backblazeCredential, ) async { diff --git a/lib/logic/get_it/api_config.dart b/lib/logic/get_it/api_config.dart index 434c9b32..11d73a85 100644 --- a/lib/logic/get_it/api_config.dart +++ b/lib/logic/get_it/api_config.dart @@ -14,6 +14,8 @@ class ApiConfigModel { String? get serverType => _serverType; String? get cloudFlareKey => _cloudFlareKey; ServerProvider? get serverProvider => _serverProvider; + DnsProvider? get dnsProvider => _dnsProvider; + BackblazeCredential? get backblazeCredential => _backblazeCredential; ServerDomain? get serverDomain => _serverDomain; BackblazeBucket? get backblazeBucket => _backblazeBucket; @@ -23,6 +25,7 @@ class ApiConfigModel { String? _cloudFlareKey; String? _serverType; ServerProvider? _serverProvider; + DnsProvider? _dnsProvider; ServerHostingDetails? _serverDetails; BackblazeCredential? _backblazeCredential; ServerDomain? _serverDomain; @@ -33,6 +36,11 @@ class ApiConfigModel { _serverProvider = value; } + Future storeDnsProviderType(final DnsProvider value) async { + await _box.put(BNames.dnsProvider, value); + _dnsProvider = value; + } + Future storeServerProviderKey(final String value) async { await _box.put(BNames.hetznerKey, value); _serverProviderKey = value; @@ -75,6 +83,7 @@ class ApiConfigModel { void clear() { _serverProviderKey = null; + _dnsProvider = null; _serverLocation = null; _cloudFlareKey = null; _backblazeCredential = null; @@ -95,5 +104,6 @@ class ApiConfigModel { _backblazeBucket = _box.get(BNames.backblazeBucket); _serverType = _box.get(BNames.serverTypeIdentifier); _serverProvider = _box.get(BNames.serverProvider); + _dnsProvider = _box.get(BNames.dnsProvider); } } diff --git a/lib/logic/models/hive/server_domain.dart b/lib/logic/models/hive/server_domain.dart index 9b5d32c1..913fcd42 100644 --- a/lib/logic/models/hive/server_domain.dart +++ b/lib/logic/models/hive/server_domain.dart @@ -29,4 +29,6 @@ enum DnsProvider { unknown, @HiveField(1) cloudflare, + @HiveField(2) + desec } diff --git a/lib/ui/pages/setup/initializing/dns_provider_picker.dart b/lib/ui/pages/setup/initializing/dns_provider_picker.dart new file mode 100644 index 00000000..cb0d2111 --- /dev/null +++ b/lib/ui/pages/setup/initializing/dns_provider_picker.dart @@ -0,0 +1,307 @@ +import 'package:cubit_form/cubit_form.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:selfprivacy/config/brand_theme.dart'; +import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; +import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; +import 'package:selfprivacy/ui/components/buttons/outlined_button.dart'; +import 'package:selfprivacy/ui/components/cards/outlined_card.dart'; +import 'package:selfprivacy/utils/launch_url.dart'; + +class DnsProviderPicker extends StatefulWidget { + const DnsProviderPicker({ + required this.formCubit, + required this.serverInstallationCubit, + super.key, + }); + + final DnsProviderFormCubit formCubit; + final ServerInstallationCubit serverInstallationCubit; + + @override + State createState() => _DnsProviderPickerState(); +} + +class _DnsProviderPickerState extends State { + DnsProvider selectedProvider = DnsProvider.unknown; + + void setProvider(final DnsProvider provider) { + setState(() { + selectedProvider = provider; + }); + } + + @override + Widget build(final BuildContext context) { + switch (selectedProvider) { + case DnsProvider.unknown: + return ProviderSelectionPage( + serverInstallationCubit: widget.serverInstallationCubit, + callback: setProvider, + ); + + case DnsProvider.cloudflare: + return ProviderInputDataPage( + providerCubit: widget.formCubit, + providerInfo: ProviderPageInfo( + providerType: DnsProvider.cloudflare, + pathToHow: 'how_cloudflare', + image: Image.asset( + 'assets/images/logos/cloudflare.png', + width: 150, + ), + ), + ); + + case DnsProvider.desec: + return ProviderInputDataPage( + providerCubit: widget.formCubit, + providerInfo: ProviderPageInfo( + providerType: DnsProvider.desec, + pathToHow: 'how_digital_ocean_dns', + image: Image.asset( + 'assets/images/logos/digital_ocean.png', + width: 150, + ), + ), + ); + } + } +} + +class ProviderPageInfo { + const ProviderPageInfo({ + required this.providerType, + required this.pathToHow, + required this.image, + }); + + final String pathToHow; + final Image image; + final DnsProvider providerType; +} + +class ProviderInputDataPage extends StatelessWidget { + const ProviderInputDataPage({ + required this.providerInfo, + required this.providerCubit, + super.key, + }); + + final ProviderPageInfo providerInfo; + final DnsProviderFormCubit providerCubit; + + @override + Widget build(final BuildContext context) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.connect_to_dns'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.connect_to_server_provider_text'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 32), + CubitFormTextField( + formFieldCubit: providerCubit.apiKey, + textAlign: TextAlign.center, + scrollPadding: const EdgeInsets.only(bottom: 70), + decoration: const InputDecoration( + hintText: 'Provider API Token', + ), + ), + const SizedBox(height: 32), + BrandButton.rised( + text: 'basis.connect'.tr(), + onPressed: () => providerCubit.trySubmit(), + ), + const SizedBox(height: 10), + BrandOutlinedButton( + child: Text('initializing.how'.tr()), + onPressed: () => showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (final BuildContext context) => Padding( + padding: paddingH15V0, + child: ListView( + padding: const EdgeInsets.symmetric(vertical: 16), + children: [ + BrandMarkdown( + fileName: providerInfo.pathToHow, + ), + ], + ), + ), + ), + ), + ], + ); +} + +class ProviderSelectionPage extends StatelessWidget { + const ProviderSelectionPage({ + required this.callback, + required this.serverInstallationCubit, + super.key, + }); + + final Function callback; + final ServerInstallationCubit serverInstallationCubit; + + @override + Widget build(final BuildContext context) => SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'initializing.select_dns'.tr(), + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 10), + Text( + 'initializing.select_provider'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + const SizedBox(height: 10), + OutlinedCard( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: const Color.fromARGB(255, 241, 215, 166), + ), + child: SvgPicture.asset( + 'assets/images/logos/cloudflare.svg', + ), + ), + const SizedBox(width: 16), + Text( + 'Hetzner Cloud', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_price_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_price_free'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_payment_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_payment_text_cloudflare' + .tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + BrandButton.rised( + text: 'basis.select'.tr(), + onPressed: () { + serverInstallationCubit + .setDnsProviderType(DnsProvider.cloudflare); + callback(DnsProvider.cloudflare); + }, + ), + // Outlined button that will open website + BrandOutlinedButton( + onPressed: () => + launchURL('https://dash.cloudflare.com/'), + title: 'initializing.select_provider_site_button'.tr(), + ), + ], + ), + ), + ), + const SizedBox(height: 16), + OutlinedCard( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 40, + height: 40, + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(40), + color: const Color.fromARGB(255, 245, 229, 82), + ), + child: SvgPicture.asset( + 'assets/images/logos/desec.svg', + ), + ), + const SizedBox(width: 16), + Text( + 'deSEC', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_price_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_price_free'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + Text( + 'initializing.select_provider_payment_title'.tr(), + style: Theme.of(context).textTheme.bodyLarge, + ), + Text( + 'initializing.select_provider_payment_text_do'.tr(), + style: Theme.of(context).textTheme.bodySmall, + ), + const SizedBox(height: 16), + BrandButton.rised( + text: 'basis.select'.tr(), + onPressed: () { + serverInstallationCubit + .setDnsProviderType(DnsProvider.desec); + callback(DnsProvider.desec); + }, + ), + // Outlined button that will open website + BrandOutlinedButton( + onPressed: () => launchURL('https://desec.io/'), + title: 'initializing.select_provider_site_button'.tr(), + ), + ], + ), + ), + ), + ], + ), + ); +} diff --git a/lib/ui/pages/setup/initializing/initializing.dart b/lib/ui/pages/setup/initializing/initializing.dart index 199203c3..749fbdc9 100644 --- a/lib/ui/pages/setup/initializing/initializing.dart +++ b/lib/ui/pages/setup/initializing/initializing.dart @@ -17,6 +17,7 @@ import 'package:selfprivacy/ui/components/drawers/progress_drawer.dart'; import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; import 'package:selfprivacy/ui/components/drawers/support_drawer.dart'; import 'package:selfprivacy/ui/layouts/responsive_layout_with_infobox.dart'; +import 'package:selfprivacy/ui/pages/setup/initializing/dns_provider_picker.dart'; import 'package:selfprivacy/ui/pages/setup/initializing/server_provider_picker.dart'; import 'package:selfprivacy/ui/pages/setup/initializing/server_type_picker.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart'; @@ -39,7 +40,7 @@ class InitializingPage extends StatelessWidget { actualInitializingPage = [ () => _stepServerProviderToken(cubit), () => _stepServerType(cubit), - () => _stepCloudflare(cubit), + () => _stepDnsProviderToken(cubit), () => _stepBackblaze(cubit), () => _stepDomain(cubit), () => _stepUser(cubit), @@ -238,56 +239,19 @@ class InitializingPage extends StatelessWidget { ), ); - Widget _stepCloudflare(final ServerInstallationCubit initializingCubit) => + Widget _stepDnsProviderToken( + final ServerInstallationCubit initializingCubit, + ) => BlocProvider( create: (final context) => DnsProviderFormCubit(initializingCubit), child: Builder( - builder: (final context) => ResponsiveLayoutWithInfobox( - topChild: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '${'initializing.connect_to_server_provider'.tr()}Cloudflare', - style: Theme.of(context).textTheme.headlineSmall, - ), - const SizedBox(height: 16), - Text( - 'initializing.manage_domain_dns'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - ], - ), - primaryColumn: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CubitFormTextField( - formFieldCubit: context.read().apiKey, - textAlign: TextAlign.center, - scrollPadding: const EdgeInsets.only(bottom: 70), - decoration: InputDecoration( - hintText: 'initializing.cloudflare_api_token'.tr(), - ), - ), - const SizedBox(height: 32), - BrandButton.filled( - onPressed: () => - context.read().trySubmit(), - text: 'basis.connect'.tr(), - ), - const SizedBox(height: 10), - BrandOutlinedButton( - onPressed: () { - context.read().showArticle( - article: 'how_cloudflare', - context: context, - ); - Scaffold.of(context).openEndDrawer(); - }, - title: 'initializing.how'.tr(), - ), - ], - ), - ), + builder: (final context) { + final providerCubit = context.watch(); + return DnsProviderPicker( + formCubit: providerCubit, + serverInstallationCubit: initializingCubit, + ); + }, ), ); From af90ddd78a5579462cea73b2246183ec80b4bb9a Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 12 May 2023 03:07:43 -0300 Subject: [PATCH 02/10] feat: Implement deSEC API support --- .../rest_maps/dns_providers/desec/desec.dart | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart index 1906be55..63c7acba 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart @@ -172,7 +172,18 @@ class DesecApi extends DnsProviderApi { allCreateFutures.add( client.post( '/$domainName/rrsets/', - data: record.toJson(), + data: record.name == null + ? { + 'type': record.type, + 'ttl': record.ttl, + 'records': [record.content], + } + : { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [record.content], + }, ), ); } @@ -198,10 +209,9 @@ class DesecApi extends DnsProviderApi { final String? domainName, final String? ip4, ) { - final DnsRecord domainA = - DnsRecord(type: 'A', name: domainName, content: ip4); + final DnsRecord domainA = DnsRecord(type: 'A', name: null, content: ip4); - final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: domainName); + final DnsRecord mx = DnsRecord(type: 'MX', name: null, content: domainName); final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4); final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4); final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4); @@ -221,7 +231,7 @@ class DesecApi extends DnsProviderApi { final DnsRecord txt2 = DnsRecord( type: 'TXT', - name: domainName, + name: null, content: 'v=spf1 a mx ip4:$ip4 -all', ttl: 18000, ); @@ -246,14 +256,24 @@ class DesecApi extends DnsProviderApi { final DnsRecord record, final ServerDomain domain, ) async { - final String domainZoneId = domain.zoneId; - final String url = '$rootAddress/zones/$domainZoneId/dns_records'; + final String url = '/${domain.domainName}/rrsets/'; final Dio client = await getClient(); try { await client.post( url, - data: record.toJson(), + data: record.name == null + ? { + 'type': record.type, + 'ttl': record.ttl, + 'records': [record.content], + } + : { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [record.content], + }, ); } catch (e) { print(e); @@ -264,14 +284,12 @@ class DesecApi extends DnsProviderApi { @override Future> domainList() async { - final String url = '$rootAddress/zones'; List domains = []; final Dio client = await getClient(); try { final Response response = await client.get( - url, - queryParameters: {'per_page': 50}, + '', ); domains = response.data['result'] .map((final el) => el['name'] as String) From 56dd40e90e7eba31abf8a8435445c9782cbd415e Mon Sep 17 00:00:00 2001 From: NaiJi Date: Fri, 12 May 2023 16:32:19 -0300 Subject: [PATCH 03/10] fix: Adjust graphql schemas to new dns provider - fix runtime bugs --- .../schema/disk_volumes.graphql.dart | 1 + .../graphql_maps/schema/schema.graphql | 3 +- .../graphql_maps/schema/schema.graphql.dart | 6 +- .../graphql_maps/schema/server_api.graphql | 9 + .../schema/server_api.graphql.dart | 546 +++++++++++++++++- .../schema/server_settings.graphql.dart | 2 +- .../graphql_maps/schema/services.graphql.dart | 1 - .../graphql_maps/schema/users.graphql.dart | 2 +- .../graphql_maps/server_api/server_api.dart | 20 + .../dns_providers/cloudflare/cloudflare.dart | 2 +- .../rest_maps/dns_providers/desec/desec.dart | 12 +- .../digital_ocean/digital_ocean.dart | 4 +- .../server_providers/hetzner/hetzner.dart | 6 +- .../server_providers/server_provider.dart | 15 + .../initializing/dns_provider_form_cubit.dart | 4 +- .../server_installation_cubit.dart | 37 +- .../server_installation_repository.dart | 45 +- .../server_installation_state.dart | 40 +- lib/logic/get_it/api_config.dart | 12 +- lib/logic/models/hive/server_domain.dart | 14 +- lib/logic/models/hive/server_domain.g.dart | 5 + ...udflare.dart => recovery_confirm_dns.dart} | 4 +- .../setup/recovering/recovery_routing.dart | 6 +- lib/ui/router/router.gr.dart | 526 ++++++++--------- 24 files changed, 968 insertions(+), 354 deletions(-) rename lib/ui/pages/setup/recovering/{recovery_confirm_cloudflare.dart => recovery_confirm_dns.dart} (96%) diff --git a/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart index 22409ef9..9ffbfe0b 100644 --- a/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/disk_volumes.graphql.dart @@ -3,6 +3,7 @@ import 'package:gql/ast.dart'; import 'package:graphql/client.dart' as graphql; import 'package:selfprivacy/utils/scalars.dart'; import 'schema.graphql.dart'; +import 'services.graphql.dart'; class Fragment$basicMutationReturnFields { Fragment$basicMutationReturnFields({ diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql b/lib/logic/api_maps/graphql_maps/schema/schema.graphql index ed167742..89bc30c8 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql @@ -75,7 +75,8 @@ type DeviceApiTokenMutationReturn implements MutationReturnInterface { } enum DnsProvider { - CLOUDFLARE + CLOUDFLARE, + DESEC } type DnsRecord { 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 1b92ccad..8548e4dd 100644 --- a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.dart @@ -1096,12 +1096,14 @@ class _CopyWithStubImpl$Input$UserMutationInput _res; } -enum Enum$DnsProvider { CLOUDFLARE, $unknown } +enum Enum$DnsProvider { CLOUDFLARE, DESEC, $unknown } String toJson$Enum$DnsProvider(Enum$DnsProvider e) { switch (e) { case Enum$DnsProvider.CLOUDFLARE: return r'CLOUDFLARE'; + case Enum$DnsProvider.DESEC: + return r'DESEC'; case Enum$DnsProvider.$unknown: return r'$unknown'; } @@ -1111,6 +1113,8 @@ Enum$DnsProvider fromJson$Enum$DnsProvider(String value) { switch (value) { case r'CLOUDFLARE': return Enum$DnsProvider.CLOUDFLARE; + case r'DESEC': + return Enum$DnsProvider.DESEC; default: return Enum$DnsProvider.$unknown; } diff --git a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql index d4339094..35df3749 100644 --- a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql +++ b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql @@ -72,6 +72,15 @@ query SystemServerProvider { } } +query SystemDnsProvider { + system { + domainInfo { + provider + } + } +} + + query GetApiTokens { api { devices { diff --git a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart index 4bb33228..4eda2c92 100644 --- a/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/server_api.graphql.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'disk_volumes.graphql.dart'; import 'package:gql/ast.dart'; import 'package:graphql/client.dart' as graphql; import 'package:selfprivacy/utils/scalars.dart'; import 'schema.graphql.dart'; +import 'services.graphql.dart'; class Fragment$basicMutationReturnFields { Fragment$basicMutationReturnFields({ @@ -6380,6 +6380,550 @@ class _CopyWithStubImpl$Query$SystemServerProvider$system$provider _res; } +class Query$SystemDnsProvider { + Query$SystemDnsProvider({ + required this.system, + required this.$__typename, + }); + + factory Query$SystemDnsProvider.fromJson(Map json) { + final l$system = json['system']; + final l$$__typename = json['__typename']; + return Query$SystemDnsProvider( + system: Query$SystemDnsProvider$system.fromJson( + (l$system as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemDnsProvider$system system; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$system = system; + _resultData['system'] = l$system.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$system = system; + final l$$__typename = $__typename; + return Object.hashAll([ + l$system, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemDnsProvider) || + runtimeType != other.runtimeType) { + return false; + } + final l$system = system; + final lOther$system = other.system; + if (l$system != lOther$system) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemDnsProvider on Query$SystemDnsProvider { + CopyWith$Query$SystemDnsProvider get copyWith => + CopyWith$Query$SystemDnsProvider( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemDnsProvider { + factory CopyWith$Query$SystemDnsProvider( + Query$SystemDnsProvider instance, + TRes Function(Query$SystemDnsProvider) then, + ) = _CopyWithImpl$Query$SystemDnsProvider; + + factory CopyWith$Query$SystemDnsProvider.stub(TRes res) = + _CopyWithStubImpl$Query$SystemDnsProvider; + + TRes call({ + Query$SystemDnsProvider$system? system, + String? $__typename, + }); + CopyWith$Query$SystemDnsProvider$system get system; +} + +class _CopyWithImpl$Query$SystemDnsProvider + implements CopyWith$Query$SystemDnsProvider { + _CopyWithImpl$Query$SystemDnsProvider( + this._instance, + this._then, + ); + + final Query$SystemDnsProvider _instance; + + final TRes Function(Query$SystemDnsProvider) _then; + + static const _undefined = {}; + + TRes call({ + Object? system = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemDnsProvider( + system: system == _undefined || system == null + ? _instance.system + : (system as Query$SystemDnsProvider$system), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemDnsProvider$system get system { + final local$system = _instance.system; + return CopyWith$Query$SystemDnsProvider$system( + local$system, (e) => call(system: e)); + } +} + +class _CopyWithStubImpl$Query$SystemDnsProvider + implements CopyWith$Query$SystemDnsProvider { + _CopyWithStubImpl$Query$SystemDnsProvider(this._res); + + TRes _res; + + call({ + Query$SystemDnsProvider$system? system, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemDnsProvider$system get system => + CopyWith$Query$SystemDnsProvider$system.stub(_res); +} + +const documentNodeQuerySystemDnsProvider = DocumentNode(definitions: [ + OperationDefinitionNode( + type: OperationType.query, + name: NameNode(value: 'SystemDnsProvider'), + variableDefinitions: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'system'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'domainInfo'), + alias: null, + arguments: [], + directives: [], + selectionSet: SelectionSetNode(selections: [ + FieldNode( + name: NameNode(value: 'provider'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), + FieldNode( + name: NameNode(value: '__typename'), + alias: null, + arguments: [], + directives: [], + selectionSet: null, + ), + ]), + ), +]); +Query$SystemDnsProvider _parserFn$Query$SystemDnsProvider( + Map data) => + Query$SystemDnsProvider.fromJson(data); + +class Options$Query$SystemDnsProvider + extends graphql.QueryOptions { + Options$Query$SystemDnsProvider({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + Duration? pollInterval, + graphql.Context? context, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult, + pollInterval: pollInterval, + context: context, + document: documentNodeQuerySystemDnsProvider, + parserFn: _parserFn$Query$SystemDnsProvider, + ); +} + +class WatchOptions$Query$SystemDnsProvider + extends graphql.WatchQueryOptions { + WatchOptions$Query$SystemDnsProvider({ + String? operationName, + graphql.FetchPolicy? fetchPolicy, + graphql.ErrorPolicy? errorPolicy, + graphql.CacheRereadPolicy? cacheRereadPolicy, + Object? optimisticResult, + graphql.Context? context, + Duration? pollInterval, + bool? eagerlyFetchResults, + bool carryForwardDataOnException = true, + bool fetchResults = false, + }) : super( + operationName: operationName, + fetchPolicy: fetchPolicy, + errorPolicy: errorPolicy, + cacheRereadPolicy: cacheRereadPolicy, + optimisticResult: optimisticResult, + context: context, + document: documentNodeQuerySystemDnsProvider, + pollInterval: pollInterval, + eagerlyFetchResults: eagerlyFetchResults, + carryForwardDataOnException: carryForwardDataOnException, + fetchResults: fetchResults, + parserFn: _parserFn$Query$SystemDnsProvider, + ); +} + +class FetchMoreOptions$Query$SystemDnsProvider + extends graphql.FetchMoreOptions { + FetchMoreOptions$Query$SystemDnsProvider( + {required graphql.UpdateQuery updateQuery}) + : super( + updateQuery: updateQuery, + document: documentNodeQuerySystemDnsProvider, + ); +} + +extension ClientExtension$Query$SystemDnsProvider on graphql.GraphQLClient { + Future> query$SystemDnsProvider( + [Options$Query$SystemDnsProvider? options]) async => + await this.query(options ?? Options$Query$SystemDnsProvider()); + graphql.ObservableQuery watchQuery$SystemDnsProvider( + [WatchOptions$Query$SystemDnsProvider? options]) => + this.watchQuery(options ?? WatchOptions$Query$SystemDnsProvider()); + void writeQuery$SystemDnsProvider({ + required Query$SystemDnsProvider data, + bool broadcast = true, + }) => + this.writeQuery( + graphql.Request( + operation: graphql.Operation( + document: documentNodeQuerySystemDnsProvider)), + data: data.toJson(), + broadcast: broadcast, + ); + Query$SystemDnsProvider? readQuery$SystemDnsProvider( + {bool optimistic = true}) { + final result = this.readQuery( + graphql.Request( + operation: + graphql.Operation(document: documentNodeQuerySystemDnsProvider)), + optimistic: optimistic, + ); + return result == null ? null : Query$SystemDnsProvider.fromJson(result); + } +} + +class Query$SystemDnsProvider$system { + Query$SystemDnsProvider$system({ + required this.domainInfo, + required this.$__typename, + }); + + factory Query$SystemDnsProvider$system.fromJson(Map json) { + final l$domainInfo = json['domainInfo']; + final l$$__typename = json['__typename']; + return Query$SystemDnsProvider$system( + domainInfo: Query$SystemDnsProvider$system$domainInfo.fromJson( + (l$domainInfo as Map)), + $__typename: (l$$__typename as String), + ); + } + + final Query$SystemDnsProvider$system$domainInfo domainInfo; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$domainInfo = domainInfo; + _resultData['domainInfo'] = l$domainInfo.toJson(); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$domainInfo = domainInfo; + final l$$__typename = $__typename; + return Object.hashAll([ + l$domainInfo, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemDnsProvider$system) || + runtimeType != other.runtimeType) { + return false; + } + final l$domainInfo = domainInfo; + final lOther$domainInfo = other.domainInfo; + if (l$domainInfo != lOther$domainInfo) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemDnsProvider$system + on Query$SystemDnsProvider$system { + CopyWith$Query$SystemDnsProvider$system + get copyWith => CopyWith$Query$SystemDnsProvider$system( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemDnsProvider$system { + factory CopyWith$Query$SystemDnsProvider$system( + Query$SystemDnsProvider$system instance, + TRes Function(Query$SystemDnsProvider$system) then, + ) = _CopyWithImpl$Query$SystemDnsProvider$system; + + factory CopyWith$Query$SystemDnsProvider$system.stub(TRes res) = + _CopyWithStubImpl$Query$SystemDnsProvider$system; + + TRes call({ + Query$SystemDnsProvider$system$domainInfo? domainInfo, + String? $__typename, + }); + CopyWith$Query$SystemDnsProvider$system$domainInfo get domainInfo; +} + +class _CopyWithImpl$Query$SystemDnsProvider$system + implements CopyWith$Query$SystemDnsProvider$system { + _CopyWithImpl$Query$SystemDnsProvider$system( + this._instance, + this._then, + ); + + final Query$SystemDnsProvider$system _instance; + + final TRes Function(Query$SystemDnsProvider$system) _then; + + static const _undefined = {}; + + TRes call({ + Object? domainInfo = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemDnsProvider$system( + domainInfo: domainInfo == _undefined || domainInfo == null + ? _instance.domainInfo + : (domainInfo as Query$SystemDnsProvider$system$domainInfo), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); + CopyWith$Query$SystemDnsProvider$system$domainInfo get domainInfo { + final local$domainInfo = _instance.domainInfo; + return CopyWith$Query$SystemDnsProvider$system$domainInfo( + local$domainInfo, (e) => call(domainInfo: e)); + } +} + +class _CopyWithStubImpl$Query$SystemDnsProvider$system + implements CopyWith$Query$SystemDnsProvider$system { + _CopyWithStubImpl$Query$SystemDnsProvider$system(this._res); + + TRes _res; + + call({ + Query$SystemDnsProvider$system$domainInfo? domainInfo, + String? $__typename, + }) => + _res; + CopyWith$Query$SystemDnsProvider$system$domainInfo get domainInfo => + CopyWith$Query$SystemDnsProvider$system$domainInfo.stub(_res); +} + +class Query$SystemDnsProvider$system$domainInfo { + Query$SystemDnsProvider$system$domainInfo({ + required this.provider, + required this.$__typename, + }); + + factory Query$SystemDnsProvider$system$domainInfo.fromJson( + Map json) { + final l$provider = json['provider']; + final l$$__typename = json['__typename']; + return Query$SystemDnsProvider$system$domainInfo( + provider: fromJson$Enum$DnsProvider((l$provider as String)), + $__typename: (l$$__typename as String), + ); + } + + final Enum$DnsProvider provider; + + final String $__typename; + + Map toJson() { + final _resultData = {}; + final l$provider = provider; + _resultData['provider'] = toJson$Enum$DnsProvider(l$provider); + final l$$__typename = $__typename; + _resultData['__typename'] = l$$__typename; + return _resultData; + } + + @override + int get hashCode { + final l$provider = provider; + final l$$__typename = $__typename; + return Object.hashAll([ + l$provider, + l$$__typename, + ]); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) { + return true; + } + if (!(other is Query$SystemDnsProvider$system$domainInfo) || + runtimeType != other.runtimeType) { + return false; + } + final l$provider = provider; + final lOther$provider = other.provider; + if (l$provider != lOther$provider) { + return false; + } + final l$$__typename = $__typename; + final lOther$$__typename = other.$__typename; + if (l$$__typename != lOther$$__typename) { + return false; + } + return true; + } +} + +extension UtilityExtension$Query$SystemDnsProvider$system$domainInfo + on Query$SystemDnsProvider$system$domainInfo { + CopyWith$Query$SystemDnsProvider$system$domainInfo< + Query$SystemDnsProvider$system$domainInfo> + get copyWith => CopyWith$Query$SystemDnsProvider$system$domainInfo( + this, + (i) => i, + ); +} + +abstract class CopyWith$Query$SystemDnsProvider$system$domainInfo { + factory CopyWith$Query$SystemDnsProvider$system$domainInfo( + Query$SystemDnsProvider$system$domainInfo instance, + TRes Function(Query$SystemDnsProvider$system$domainInfo) then, + ) = _CopyWithImpl$Query$SystemDnsProvider$system$domainInfo; + + factory CopyWith$Query$SystemDnsProvider$system$domainInfo.stub(TRes res) = + _CopyWithStubImpl$Query$SystemDnsProvider$system$domainInfo; + + TRes call({ + Enum$DnsProvider? provider, + String? $__typename, + }); +} + +class _CopyWithImpl$Query$SystemDnsProvider$system$domainInfo + implements CopyWith$Query$SystemDnsProvider$system$domainInfo { + _CopyWithImpl$Query$SystemDnsProvider$system$domainInfo( + this._instance, + this._then, + ); + + final Query$SystemDnsProvider$system$domainInfo _instance; + + final TRes Function(Query$SystemDnsProvider$system$domainInfo) _then; + + static const _undefined = {}; + + TRes call({ + Object? provider = _undefined, + Object? $__typename = _undefined, + }) => + _then(Query$SystemDnsProvider$system$domainInfo( + provider: provider == _undefined || provider == null + ? _instance.provider + : (provider as Enum$DnsProvider), + $__typename: $__typename == _undefined || $__typename == null + ? _instance.$__typename + : ($__typename as String), + )); +} + +class _CopyWithStubImpl$Query$SystemDnsProvider$system$domainInfo + implements CopyWith$Query$SystemDnsProvider$system$domainInfo { + _CopyWithStubImpl$Query$SystemDnsProvider$system$domainInfo(this._res); + + TRes _res; + + call({ + Enum$DnsProvider? provider, + String? $__typename, + }) => + _res; +} + class Query$GetApiTokens { Query$GetApiTokens({ required this.api, 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 14996423..8da4e347 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 @@ -1,8 +1,8 @@ import 'dart:async'; -import 'disk_volumes.graphql.dart'; import 'package:gql/ast.dart'; import 'package:graphql/client.dart' as graphql; import 'schema.graphql.dart'; +import 'services.graphql.dart'; class Fragment$basicMutationReturnFields { Fragment$basicMutationReturnFields({ 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 dfea309c..1501d709 100644 --- a/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/services.graphql.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'disk_volumes.graphql.dart'; import 'package:gql/ast.dart'; import 'package:graphql/client.dart' as graphql; import 'package:selfprivacy/utils/scalars.dart'; diff --git a/lib/logic/api_maps/graphql_maps/schema/users.graphql.dart b/lib/logic/api_maps/graphql_maps/schema/users.graphql.dart index c2d0027f..dbc8eccc 100644 --- a/lib/logic/api_maps/graphql_maps/schema/users.graphql.dart +++ b/lib/logic/api_maps/graphql_maps/schema/users.graphql.dart @@ -1,8 +1,8 @@ import 'dart:async'; -import 'disk_volumes.graphql.dart'; import 'package:gql/ast.dart'; import 'package:graphql/client.dart' as graphql; import 'schema.graphql.dart'; +import 'services.graphql.dart'; class Fragment$basicMutationReturnFields { Fragment$basicMutationReturnFields({ diff --git a/lib/logic/api_maps/graphql_maps/server_api/server_api.dart b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart index bb703103..51da63ac 100644 --- a/lib/logic/api_maps/graphql_maps/server_api/server_api.dart +++ b/lib/logic/api_maps/graphql_maps/server_api/server_api.dart @@ -11,6 +11,7 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dar import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart'; 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/api_token.dart'; import 'package:selfprivacy/logic/models/json/backup.dart'; @@ -87,6 +88,25 @@ class ServerApi extends ApiMap return providerType; } + Future getDnsProviderType() async { + QueryResult response; + DnsProvider providerType = DnsProvider.unknown; + + try { + final GraphQLClient client = await getClient(); + response = await client.query$SystemDnsProvider(); + if (response.hasException) { + print(response.exception.toString()); + } + providerType = DnsProvider.fromGraphQL( + response.parsedData!.system.domainInfo.provider, + ); + } catch (e) { + print(e); + } + return providerType; + } + Future isUsingBinds() async { QueryResult response; bool usesBinds = false; 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 14cf5c96..04ff622b 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 @@ -27,7 +27,7 @@ class CloudflareApi extends DnsProviderApi { BaseOptions get options { final BaseOptions options = BaseOptions(baseUrl: rootAddress); if (isWithToken) { - final String? token = getIt().cloudFlareKey; + final String? token = getIt().dnsProviderKey; assert(token != null); options.headers = {'Authorization': 'Bearer $token'}; } diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart index 63c7acba..4e55bbfa 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart @@ -27,13 +27,13 @@ class DesecApi extends DnsProviderApi { BaseOptions get options { final BaseOptions options = BaseOptions(baseUrl: rootAddress); if (isWithToken) { - final String? token = getIt().cloudFlareKey; + final String? token = getIt().dnsProviderKey; assert(token != null); - options.headers = {'Authorization': 'Bearer $token'}; + options.headers = {'Authorization': 'Token $token'}; } if (customToken != null) { - options.headers = {'Authorization': 'Bearer $customToken'}; + options.headers = {'Authorization': 'Token $customToken'}; } if (validateStatus != null) { @@ -107,7 +107,7 @@ class DesecApi extends DnsProviderApi { try { final Response response = await client.get(url); - final List records = response.data['result'] ?? []; + final List records = response.data; await client.put(url, data: records); } catch (e) { print(e); @@ -136,7 +136,7 @@ class DesecApi extends DnsProviderApi { final Dio client = await getClient(); try { response = await client.get(url); - final List records = response.data['result'] ?? []; + final List records = response.data; for (final record in records) { allRecords.add( @@ -291,7 +291,7 @@ class DesecApi extends DnsProviderApi { final Response response = await client.get( '', ); - domains = response.data['result'] + domains = response.data .map((final el) => el['name'] as String) .toList(); } catch (e) { diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart index b2f4c624..21861dd8 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart @@ -332,6 +332,7 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { required final User rootUser, required final String domainName, required final String serverType, + required final DnsProvider dnsProvider, }) async { ServerHostingDetails? serverDetails; @@ -344,9 +345,10 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi { final String formattedHostname = getHostnameFromDomain(domainName); const String infectBranch = 'providers/digital-ocean'; final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false'; + final String dnsProviderType = dnsProviderToInfectName(dnsProvider); final String userdataString = - "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$formattedHostname bash 2>&1 | tee /tmp/infect.log"; + "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | DNS_PROVIDER_TYPE=$dnsProviderType PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$formattedHostname bash 2>&1 | tee /tmp/infect.log"; print(userdataString); Response? serverCreateResponse; diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index 7f51c768..c2228030 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart @@ -356,6 +356,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { required final User rootUser, required final String domainName, required final String serverType, + required final DnsProvider dnsProvider, }) async { final APIGenericResult newVolumeResponse = await createVolume(); @@ -374,6 +375,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { domainName: domainName, volume: newVolumeResponse.data!, serverType: serverType, + dnsProvider: dnsProvider, ); } @@ -383,6 +385,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { required final String domainName, required final ServerVolume volume, required final String serverType, + required final DnsProvider dnsProvider, }) async { final Dio client = await getClient(); @@ -395,9 +398,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false'; final String base64Password = base64.encode(utf8.encode(rootUser.password ?? 'PASS')); + final String dnsProviderType = dnsProviderToInfectName(dnsProvider); final String userdataString = - "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log"; + "#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | DNS_PROVIDER_TYPE=$dnsProviderType STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log"; Response? serverCreateResponse; ServerHostingDetails? serverDetails; diff --git a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart index c858d67b..ae7911d0 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/server_provider.dart @@ -39,6 +39,7 @@ abstract class ServerProviderApi extends ApiMap { required final User rootUser, required final String domainName, required final String serverType, + required final DnsProvider dnsProvider, }); Future> createReverseDns({ required final ServerHostingDetails serverDetails, @@ -54,6 +55,20 @@ abstract class ServerProviderApi extends ApiMap { final DateTime end, ); + String dnsProviderToInfectName(final DnsProvider dnsProvider) { + String dnsProviderType; + switch (dnsProvider) { + case DnsProvider.desec: + dnsProviderType = 'DESEC'; + break; + case DnsProvider.cloudflare: + default: + dnsProviderType = 'CLOUDFLARE'; + break; + } + return dnsProviderType; + } + /// Provider name key which lets infect understand what kind of installation /// it requires, for example 'digitaloceal' for Digital Ocean String get infectProviderName; diff --git a/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart index 553c3492..edb97de6 100644 --- a/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; class DnsProviderFormCubit extends FormCubit { DnsProviderFormCubit(this.initializingCubit) { @@ -11,7 +10,6 @@ class DnsProviderFormCubit extends FormCubit { initalValue: '', validations: [ RequiredStringValidation('validations.required'.tr()), - LengthStringNotEqualValidation(40) ], ); @@ -20,7 +18,7 @@ class DnsProviderFormCubit extends FormCubit { @override FutureOr onSubmit() async { - initializingCubit.setCloudflareKey(apiKey.state.value); + initializingCubit.setDnsApiToken(apiKey.state.value); } final ServerInstallationCubit initializingCubit; diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 06df6d5a..cc5bffcc 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -110,16 +110,6 @@ class ServerInstallationCubit extends Cubit { Future isDnsProviderApiTokenValid( final String providerToken, ) async { - if (ApiController.currentDnsProviderApiFactory == null) { - // No other DNS provider is supported for now, - // so it's safe to hardcode Cloudflare - ApiController.initDnsProviderApiFactory( - DnsProviderApiFactorySettings( - provider: DnsProvider.cloudflare, - ), - ); - } - final APIGenericResult apiResponse = await ApiController.currentDnsProviderApiFactory! .getDnsProvider( @@ -223,16 +213,16 @@ class ServerInstallationCubit extends Cubit { ); } - void setCloudflareKey(final String cloudFlareKey) async { + void setDnsApiToken(final String dnsApiToken) async { if (state is ServerInstallationRecovery) { - setAndValidateCloudflareToken(cloudFlareKey); + setAndValidateDnsApiToken(dnsApiToken); return; } - await repository.saveCloudFlareKey(cloudFlareKey); + await repository.saveDnsProviderKey(dnsApiToken); emit( (state as ServerInstallationNotFinished) - .copyWith(cloudFlareKey: cloudFlareKey), + .copyWith(dnsApiToken: dnsApiToken), ); } @@ -293,7 +283,7 @@ class ServerInstallationCubit extends Cubit { await repository.createServer( state.rootUser!, state.serverDomain!.domainName, - state.cloudFlareKey!, + state.dnsApiToken!, state.backblazeCredential!, onCancel: onCancel, onSuccess: onSuccess, @@ -595,7 +585,7 @@ class ServerInstallationCubit extends Cubit { ), ); break; - case RecoveryStep.cloudflareToken: + case RecoveryStep.dnsProviderToken: repository.deleteServerDetails(); emit( dataState.copyWith( @@ -691,12 +681,12 @@ class ServerInstallationCubit extends Cubit { emit( dataState.copyWith( serverDetails: serverDetails, - currentStep: RecoveryStep.cloudflareToken, + currentStep: RecoveryStep.dnsProviderToken, ), ); } - Future setAndValidateCloudflareToken(final String token) async { + Future setAndValidateDnsApiToken(final String token) async { final ServerInstallationRecovery dataState = state as ServerInstallationRecovery; final ServerDomain? serverDomain = dataState.serverDomain; @@ -714,10 +704,13 @@ class ServerInstallationCubit extends Cubit { ServerDomain( domainName: serverDomain.domainName, zoneId: zoneId, - provider: DnsProvider.cloudflare, + provider: await ServerApi( + customToken: token, + isWithToken: true, + ).getDnsProviderType(), ), ); - await repository.saveCloudFlareKey(token); + await repository.saveDnsProviderKey(token); emit( dataState.copyWith( serverDomain: ServerDomain( @@ -725,7 +718,7 @@ class ServerInstallationCubit extends Cubit { zoneId: zoneId, provider: DnsProvider.cloudflare, ), - cloudFlareKey: token, + dnsApiToken: token, currentStep: RecoveryStep.backblazeToken, ), ); @@ -776,7 +769,7 @@ class ServerInstallationCubit extends Cubit { ServerInstallationNotFinished( providerApiToken: state.providerApiToken, serverDomain: state.serverDomain, - cloudFlareKey: state.cloudFlareKey, + dnsApiToken: state.dnsApiToken, backblazeCredential: state.backblazeCredential, rootUser: state.rootUser, serverDetails: null, diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 851b2be5..ff167518 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -45,7 +45,7 @@ class ServerInstallationRepository { Future load() async { final String? providerApiToken = getIt().serverProviderKey; final String? location = getIt().serverLocation; - final String? cloudflareToken = getIt().cloudFlareKey; + final String? dnsApiToken = getIt().dnsProviderKey; final String? serverTypeIdentificator = getIt().serverType; final ServerDomain? serverDomain = getIt().serverDomain; final ServerProvider? serverProvider = @@ -54,6 +54,7 @@ class ServerInstallationRepository { getIt().backblazeCredential; final ServerHostingDetails? serverDetails = getIt().serverDetails; + final DnsProvider? dnsProvider = getIt().dnsProvider; if (serverProvider != null || (serverDetails != null && @@ -75,18 +76,21 @@ class ServerInstallationRepository { ); } - // No other DNS provider is supported for now, so it's fine. - ApiController.initDnsProviderApiFactory( - DnsProviderApiFactorySettings( - provider: DnsProvider.cloudflare, - ), - ); + if (dnsProvider != null || + (serverDomain != null && + serverDomain.provider != DnsProvider.unknown)) { + ApiController.initDnsProviderApiFactory( + DnsProviderApiFactorySettings( + provider: dnsProvider ?? serverDomain!.provider, + ), + ); + } if (box.get(BNames.hasFinalChecked, defaultValue: false)) { return ServerInstallationFinished( providerApiToken: providerApiToken!, serverTypeIdentificator: serverTypeIdentificator ?? '', - cloudFlareKey: cloudflareToken!, + dnsApiToken: dnsApiToken!, serverDomain: serverDomain!, backblazeCredential: backblazeCredential!, serverDetails: serverDetails!, @@ -103,14 +107,14 @@ class ServerInstallationRepository { serverDomain != null) { return ServerInstallationRecovery( providerApiToken: providerApiToken, - cloudFlareKey: cloudflareToken, + dnsApiToken: dnsApiToken, serverDomain: serverDomain, backblazeCredential: backblazeCredential, serverDetails: serverDetails, rootUser: box.get(BNames.rootUser), currentStep: _getCurrentRecoveryStep( providerApiToken, - cloudflareToken, + dnsApiToken, serverDomain, serverDetails, ), @@ -120,7 +124,7 @@ class ServerInstallationRepository { return ServerInstallationNotFinished( providerApiToken: providerApiToken, - cloudFlareKey: cloudflareToken, + dnsApiToken: dnsApiToken, serverDomain: serverDomain, backblazeCredential: backblazeCredential, serverDetails: serverDetails, @@ -147,7 +151,7 @@ class ServerInstallationRepository { if (serverDomain.provider != DnsProvider.unknown) { return RecoveryStep.backblazeToken; } - return RecoveryStep.cloudflareToken; + return RecoveryStep.dnsProviderToken; } return RecoveryStep.serverSelection; } @@ -238,7 +242,7 @@ class ServerInstallationRepository { Future createServer( final User rootUser, final String domainName, - final String cloudFlareKey, + final String dnsApiToken, final BackblazeCredential backblazeCredential, { required final void Function() onCancel, required final Future Function(ServerHostingDetails serverDetails) @@ -256,7 +260,8 @@ class ServerInstallationRepository { ServerHostingDetails? serverDetails; try { final APIGenericResult createResult = await api.createServer( - dnsApiToken: cloudFlareKey, + dnsProvider: getIt().dnsProvider!, + dnsApiToken: dnsApiToken, rootUser: rootUser, domainName: domainName, serverType: getIt().serverType!, @@ -280,7 +285,8 @@ class ServerInstallationRepository { try { final APIGenericResult createServerResult = await api.createServer( - dnsApiToken: cloudFlareKey, + dnsProvider: getIt().dnsProvider!, + dnsApiToken: dnsApiToken, rootUser: rootUser, domainName: domainName, serverType: getIt().serverType!, @@ -304,7 +310,8 @@ class ServerInstallationRepository { ServerHostingDetails? serverDetails; try { final APIGenericResult createResult = await api.createServer( - dnsApiToken: cloudFlareKey, + dnsProvider: getIt().dnsProvider!, + dnsApiToken: dnsApiToken, rootUser: rootUser, domainName: domainName, serverType: getIt().serverType!, @@ -721,11 +728,11 @@ class ServerInstallationRepository { getIt().init(); } - Future saveCloudFlareKey(final String key) async { - await getIt().storeCloudFlareKey(key); + Future saveDnsProviderKey(final String key) async { + await getIt().storeDnsProviderKey(key); } - Future deleteCloudFlareKey() async { + Future deleteDnsProviderKey() async { await box.delete(BNames.cloudFlareKey); getIt().init(); } diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index 331c3e2a..5ceaafdd 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -4,7 +4,7 @@ abstract class ServerInstallationState extends Equatable { const ServerInstallationState({ required this.providerApiToken, required this.serverTypeIdentificator, - required this.cloudFlareKey, + required this.dnsApiToken, required this.backblazeCredential, required this.serverDomain, required this.rootUser, @@ -18,7 +18,7 @@ abstract class ServerInstallationState extends Equatable { List get props => [ providerApiToken, serverTypeIdentificator, - cloudFlareKey, + dnsApiToken, backblazeCredential, serverDomain, rootUser, @@ -28,7 +28,7 @@ abstract class ServerInstallationState extends Equatable { ]; final String? providerApiToken; - final String? cloudFlareKey; + final String? dnsApiToken; final String? serverTypeIdentificator; final BackblazeCredential? backblazeCredential; final ServerDomain? serverDomain; @@ -40,7 +40,7 @@ abstract class ServerInstallationState extends Equatable { bool get isServerProviderApiKeyFilled => providerApiToken != null; bool get isServerTypeFilled => serverTypeIdentificator != null; - bool get isDnsProviderFilled => cloudFlareKey != null; + bool get isDnsProviderFilled => dnsApiToken != null; bool get isBackupsProviderFilled => backblazeCredential != null; bool get isDomainSelected => serverDomain != null; bool get isPrimaryUserFilled => rootUser != null; @@ -87,7 +87,7 @@ class TimerState extends ServerInstallationNotFinished { }) : super( providerApiToken: dataState.providerApiToken, serverTypeIdentificator: dataState.serverTypeIdentificator, - cloudFlareKey: dataState.cloudFlareKey, + dnsApiToken: dataState.dnsApiToken, backblazeCredential: dataState.backblazeCredential, serverDomain: dataState.serverDomain, rootUser: dataState.rootUser, @@ -114,7 +114,7 @@ enum ServerSetupProgress { nothingYet, serverProviderFilled, servertTypeFilled, - cloudFlareFilled, + dnsProviderFilled, backblazeFilled, domainFilled, userFilled, @@ -133,7 +133,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { required this.dnsMatches, super.providerApiToken, super.serverTypeIdentificator, - super.cloudFlareKey, + super.dnsApiToken, super.backblazeCredential, super.serverDomain, super.rootUser, @@ -146,7 +146,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { List get props => [ providerApiToken, serverTypeIdentificator, - cloudFlareKey, + dnsApiToken, backblazeCredential, serverDomain, rootUser, @@ -160,7 +160,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { ServerInstallationNotFinished copyWith({ final String? providerApiToken, final String? serverTypeIdentificator, - final String? cloudFlareKey, + final String? dnsApiToken, final BackblazeCredential? backblazeCredential, final ServerDomain? serverDomain, final User? rootUser, @@ -175,7 +175,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { providerApiToken: providerApiToken ?? this.providerApiToken, serverTypeIdentificator: serverTypeIdentificator ?? this.serverTypeIdentificator, - cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, + dnsApiToken: dnsApiToken ?? this.dnsApiToken, backblazeCredential: backblazeCredential ?? this.backblazeCredential, serverDomain: serverDomain ?? this.serverDomain, rootUser: rootUser ?? this.rootUser, @@ -192,7 +192,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { ServerInstallationFinished finish() => ServerInstallationFinished( providerApiToken: providerApiToken!, serverTypeIdentificator: serverTypeIdentificator ?? '', - cloudFlareKey: cloudFlareKey!, + dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, rootUser: rootUser!, @@ -208,7 +208,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished { : super( providerApiToken: null, serverTypeIdentificator: null, - cloudFlareKey: null, + dnsApiToken: null, backblazeCredential: null, serverDomain: null, rootUser: null, @@ -225,7 +225,7 @@ class ServerInstallationFinished extends ServerInstallationState { const ServerInstallationFinished({ required String super.providerApiToken, required String super.serverTypeIdentificator, - required String super.cloudFlareKey, + required String super.dnsApiToken, required BackblazeCredential super.backblazeCredential, required ServerDomain super.serverDomain, required User super.rootUser, @@ -239,7 +239,7 @@ class ServerInstallationFinished extends ServerInstallationState { List get props => [ providerApiToken, serverTypeIdentificator, - cloudFlareKey, + dnsApiToken, backblazeCredential, serverDomain, rootUser, @@ -256,7 +256,7 @@ enum RecoveryStep { oldToken, serverProviderToken, serverSelection, - cloudflareToken, + dnsProviderToken, backblazeToken, } @@ -278,7 +278,7 @@ class ServerInstallationRecovery extends ServerInstallationState { required this.recoveryCapabilities, super.providerApiToken, super.serverTypeIdentificator, - super.cloudFlareKey, + super.dnsApiToken, super.backblazeCredential, super.serverDomain, super.rootUser, @@ -295,7 +295,7 @@ class ServerInstallationRecovery extends ServerInstallationState { List get props => [ providerApiToken, serverTypeIdentificator, - cloudFlareKey, + dnsApiToken, backblazeCredential, serverDomain, rootUser, @@ -308,7 +308,7 @@ class ServerInstallationRecovery extends ServerInstallationState { ServerInstallationRecovery copyWith({ final String? providerApiToken, final String? serverTypeIdentificator, - final String? cloudFlareKey, + final String? dnsApiToken, final BackblazeCredential? backblazeCredential, final ServerDomain? serverDomain, final User? rootUser, @@ -320,7 +320,7 @@ class ServerInstallationRecovery extends ServerInstallationState { providerApiToken: providerApiToken ?? this.providerApiToken, serverTypeIdentificator: serverTypeIdentificator ?? this.serverTypeIdentificator, - cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, + dnsApiToken: dnsApiToken ?? this.dnsApiToken, backblazeCredential: backblazeCredential ?? this.backblazeCredential, serverDomain: serverDomain ?? this.serverDomain, rootUser: rootUser ?? this.rootUser, @@ -332,7 +332,7 @@ class ServerInstallationRecovery extends ServerInstallationState { ServerInstallationFinished finish() => ServerInstallationFinished( providerApiToken: providerApiToken!, serverTypeIdentificator: serverTypeIdentificator ?? '', - cloudFlareKey: cloudFlareKey!, + dnsApiToken: dnsApiToken!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, rootUser: rootUser!, diff --git a/lib/logic/get_it/api_config.dart b/lib/logic/get_it/api_config.dart index 11d73a85..ba49b9b3 100644 --- a/lib/logic/get_it/api_config.dart +++ b/lib/logic/get_it/api_config.dart @@ -12,7 +12,7 @@ class ApiConfigModel { String? get serverProviderKey => _serverProviderKey; String? get serverLocation => _serverLocation; String? get serverType => _serverType; - String? get cloudFlareKey => _cloudFlareKey; + String? get dnsProviderKey => _dnsProviderKey; ServerProvider? get serverProvider => _serverProvider; DnsProvider? get dnsProvider => _dnsProvider; @@ -22,7 +22,7 @@ class ApiConfigModel { String? _serverProviderKey; String? _serverLocation; - String? _cloudFlareKey; + String? _dnsProviderKey; String? _serverType; ServerProvider? _serverProvider; DnsProvider? _dnsProvider; @@ -46,9 +46,9 @@ class ApiConfigModel { _serverProviderKey = value; } - Future storeCloudFlareKey(final String value) async { + Future storeDnsProviderKey(final String value) async { await _box.put(BNames.cloudFlareKey, value); - _cloudFlareKey = value; + _dnsProviderKey = value; } Future storeServerTypeIdentifier(final String typeIdentifier) async { @@ -85,7 +85,7 @@ class ApiConfigModel { _serverProviderKey = null; _dnsProvider = null; _serverLocation = null; - _cloudFlareKey = null; + _dnsProviderKey = null; _backblazeCredential = null; _serverDomain = null; _serverDetails = null; @@ -97,7 +97,7 @@ class ApiConfigModel { void init() { _serverProviderKey = _box.get(BNames.hetznerKey); _serverLocation = _box.get(BNames.serverLocation); - _cloudFlareKey = _box.get(BNames.cloudFlareKey); + _dnsProviderKey = _box.get(BNames.cloudFlareKey); _backblazeCredential = _box.get(BNames.backblazeCredential); _serverDomain = _box.get(BNames.serverDomain); _serverDetails = _box.get(BNames.serverDetails); diff --git a/lib/logic/models/hive/server_domain.dart b/lib/logic/models/hive/server_domain.dart index 913fcd42..de3ef92a 100644 --- a/lib/logic/models/hive/server_domain.dart +++ b/lib/logic/models/hive/server_domain.dart @@ -1,4 +1,5 @@ import 'package:hive/hive.dart'; +import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart'; part 'server_domain.g.dart'; @@ -30,5 +31,16 @@ enum DnsProvider { @HiveField(1) cloudflare, @HiveField(2) - desec + desec; + + factory DnsProvider.fromGraphQL(final Enum$DnsProvider provider) { + switch (provider) { + case Enum$DnsProvider.CLOUDFLARE: + return cloudflare; + case Enum$DnsProvider.DESEC: + return desec; + default: + return unknown; + } + } } diff --git a/lib/logic/models/hive/server_domain.g.dart b/lib/logic/models/hive/server_domain.g.dart index 3265db6b..6770d9bc 100644 --- a/lib/logic/models/hive/server_domain.g.dart +++ b/lib/logic/models/hive/server_domain.g.dart @@ -58,6 +58,8 @@ class DnsProviderAdapter extends TypeAdapter { return DnsProvider.unknown; case 1: return DnsProvider.cloudflare; + case 2: + return DnsProvider.desec; default: return DnsProvider.unknown; } @@ -72,6 +74,9 @@ class DnsProviderAdapter extends TypeAdapter { case DnsProvider.cloudflare: writer.writeByte(1); break; + case DnsProvider.desec: + writer.writeByte(2); + break; } } diff --git a/lib/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart b/lib/ui/pages/setup/recovering/recovery_confirm_dns.dart similarity index 96% rename from lib/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart rename to lib/ui/pages/setup/recovering/recovery_confirm_dns.dart index 93c889a5..9dcad056 100644 --- a/lib/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart +++ b/lib/ui/pages/setup/recovering/recovery_confirm_dns.dart @@ -7,8 +7,8 @@ import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart'; -class RecoveryConfirmCloudflare extends StatelessWidget { - const RecoveryConfirmCloudflare({super.key}); +class RecoveryConfirmDns extends StatelessWidget { + const RecoveryConfirmDns({super.key}); @override Widget build(final BuildContext context) { diff --git a/lib/ui/pages/setup/recovering/recovery_routing.dart b/lib/ui/pages/setup/recovering/recovery_routing.dart index 14c3f9a7..be5eb2ea 100644 --- a/lib/ui/pages/setup/recovering/recovery_routing.dart +++ b/lib/ui/pages/setup/recovering/recovery_routing.dart @@ -12,7 +12,7 @@ import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_old_token.dart' import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_recovery_key.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_new_device_key.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_backblaze.dart'; -import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart'; +import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_dns.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_server.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_server_provider_connected.dart'; import 'package:selfprivacy/ui/pages/setup/recovering/recovery_method_select.dart'; @@ -55,8 +55,8 @@ class RecoveryRouting extends StatelessWidget { case RecoveryStep.serverSelection: currentPage = const RecoveryConfirmServer(); break; - case RecoveryStep.cloudflareToken: - currentPage = const RecoveryConfirmCloudflare(); + case RecoveryStep.dnsProviderToken: + currentPage = const RecoveryConfirmDns(); break; case RecoveryStep.backblazeToken: currentPage = const RecoveryConfirmBackblaze(); diff --git a/lib/ui/router/router.gr.dart b/lib/ui/router/router.gr.dart index 328c5f76..675056f3 100644 --- a/lib/ui/router/router.gr.dart +++ b/lib/ui/router/router.gr.dart @@ -14,52 +14,16 @@ abstract class _$RootRouter extends RootStackRouter { @override final Map pagesMap = { - AboutApplicationRoute.name: (routeData) { + BackupDetailsRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, - child: const AboutApplicationPage(), + child: const BackupDetailsPage(), ); }, - AppSettingsRoute.name: (routeData) { + RootRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, - child: const AppSettingsPage(), - ); - }, - DeveloperSettingsRoute.name: (routeData) { - return AutoRoutePage( - routeData: routeData, - child: const DeveloperSettingsPage(), - ); - }, - ConsoleRoute.name: (routeData) { - return AutoRoutePage( - routeData: routeData, - child: const ConsolePage(), - ); - }, - MoreRoute.name: (routeData) { - return AutoRoutePage( - routeData: routeData, - child: const MorePage(), - ); - }, - OnboardingRoute.name: (routeData) { - return AutoRoutePage( - routeData: routeData, - child: const OnboardingPage(), - ); - }, - ProvidersRoute.name: (routeData) { - return AutoRoutePage( - routeData: routeData, - child: const ProvidersPage(), - ); - }, - ServerDetailsRoute.name: (routeData) { - return AutoRoutePage( - routeData: routeData, - child: const ServerDetailsScreen(), + child: WrappedRoute(child: const RootPage()), ); }, ServiceRoute.name: (routeData) { @@ -78,6 +42,12 @@ abstract class _$RootRouter extends RootStackRouter { child: const ServicesPage(), ); }, + ServerDetailsRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ServerDetailsScreen(), + ); + }, UsersRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, @@ -100,10 +70,46 @@ abstract class _$RootRouter extends RootStackRouter { ), ); }, - BackupDetailsRoute.name: (routeData) { + AppSettingsRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, - child: const BackupDetailsPage(), + child: const AppSettingsPage(), + ); + }, + DeveloperSettingsRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const DeveloperSettingsPage(), + ); + }, + MoreRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const MorePage(), + ); + }, + AboutApplicationRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const AboutApplicationPage(), + ); + }, + ConsoleRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ConsolePage(), + ); + }, + ProvidersRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const ProvidersPage(), + ); + }, + RecoveryKeyRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const RecoveryKeyPage(), ); }, DnsDetailsRoute.name: (routeData) { @@ -124,26 +130,12 @@ abstract class _$RootRouter extends RootStackRouter { child: const InitializingPage(), ); }, - RecoveryKeyRoute.name: (routeData) { + ServerStorageRoute.name: (routeData) { + final args = routeData.argsAs(); return AutoRoutePage( routeData: routeData, - child: const RecoveryKeyPage(), - ); - }, - DevicesRoute.name: (routeData) { - return AutoRoutePage( - routeData: routeData, - child: const DevicesScreen(), - ); - }, - ServicesMigrationRoute.name: (routeData) { - final args = routeData.argsAs(); - return AutoRoutePage( - routeData: routeData, - child: ServicesMigrationPage( - services: args.services, + child: ServerStoragePage( diskStatus: args.diskStatus, - isMigration: args.isMigration, key: args.key, ), ); @@ -159,133 +151,57 @@ abstract class _$RootRouter extends RootStackRouter { ), ); }, - ServerStorageRoute.name: (routeData) { - final args = routeData.argsAs(); + ServicesMigrationRoute.name: (routeData) { + final args = routeData.argsAs(); return AutoRoutePage( routeData: routeData, - child: ServerStoragePage( + child: ServicesMigrationPage( + services: args.services, diskStatus: args.diskStatus, + isMigration: args.isMigration, key: args.key, ), ); }, - RootRoute.name: (routeData) { + DevicesRoute.name: (routeData) { return AutoRoutePage( routeData: routeData, - child: WrappedRoute(child: const RootPage()), + child: const DevicesScreen(), + ); + }, + OnboardingRoute.name: (routeData) { + return AutoRoutePage( + routeData: routeData, + child: const OnboardingPage(), ); }, }; } /// generated route for -/// [AboutApplicationPage] -class AboutApplicationRoute extends PageRouteInfo { - const AboutApplicationRoute({List? children}) +/// [BackupDetailsPage] +class BackupDetailsRoute extends PageRouteInfo { + const BackupDetailsRoute({List? children}) : super( - AboutApplicationRoute.name, + BackupDetailsRoute.name, initialChildren: children, ); - static const String name = 'AboutApplicationRoute'; + static const String name = 'BackupDetailsRoute'; static const PageInfo page = PageInfo(name); } /// generated route for -/// [AppSettingsPage] -class AppSettingsRoute extends PageRouteInfo { - const AppSettingsRoute({List? children}) +/// [RootPage] +class RootRoute extends PageRouteInfo { + const RootRoute({List? children}) : super( - AppSettingsRoute.name, + RootRoute.name, initialChildren: children, ); - static const String name = 'AppSettingsRoute'; - - static const PageInfo page = PageInfo(name); -} - -/// generated route for -/// [DeveloperSettingsPage] -class DeveloperSettingsRoute extends PageRouteInfo { - const DeveloperSettingsRoute({List? children}) - : super( - DeveloperSettingsRoute.name, - initialChildren: children, - ); - - static const String name = 'DeveloperSettingsRoute'; - - static const PageInfo page = PageInfo(name); -} - -/// generated route for -/// [ConsolePage] -class ConsoleRoute extends PageRouteInfo { - const ConsoleRoute({List? children}) - : super( - ConsoleRoute.name, - initialChildren: children, - ); - - static const String name = 'ConsoleRoute'; - - static const PageInfo page = PageInfo(name); -} - -/// generated route for -/// [MorePage] -class MoreRoute extends PageRouteInfo { - const MoreRoute({List? children}) - : super( - MoreRoute.name, - initialChildren: children, - ); - - static const String name = 'MoreRoute'; - - static const PageInfo page = PageInfo(name); -} - -/// generated route for -/// [OnboardingPage] -class OnboardingRoute extends PageRouteInfo { - const OnboardingRoute({List? children}) - : super( - OnboardingRoute.name, - initialChildren: children, - ); - - static const String name = 'OnboardingRoute'; - - static const PageInfo page = PageInfo(name); -} - -/// generated route for -/// [ProvidersPage] -class ProvidersRoute extends PageRouteInfo { - const ProvidersRoute({List? children}) - : super( - ProvidersRoute.name, - initialChildren: children, - ); - - static const String name = 'ProvidersRoute'; - - static const PageInfo page = PageInfo(name); -} - -/// generated route for -/// [ServerDetailsScreen] -class ServerDetailsRoute extends PageRouteInfo { - const ServerDetailsRoute({List? children}) - : super( - ServerDetailsRoute.name, - initialChildren: children, - ); - - static const String name = 'ServerDetailsRoute'; + static const String name = 'RootRoute'; static const PageInfo page = PageInfo(name); } @@ -342,6 +258,20 @@ class ServicesRoute extends PageRouteInfo { static const PageInfo page = PageInfo(name); } +/// generated route for +/// [ServerDetailsScreen] +class ServerDetailsRoute extends PageRouteInfo { + const ServerDetailsRoute({List? children}) + : super( + ServerDetailsRoute.name, + initialChildren: children, + ); + + static const String name = 'ServerDetailsRoute'; + + static const PageInfo page = PageInfo(name); +} + /// generated route for /// [UsersPage] class UsersRoute extends PageRouteInfo { @@ -409,15 +339,99 @@ class UserDetailsRouteArgs { } /// generated route for -/// [BackupDetailsPage] -class BackupDetailsRoute extends PageRouteInfo { - const BackupDetailsRoute({List? children}) +/// [AppSettingsPage] +class AppSettingsRoute extends PageRouteInfo { + const AppSettingsRoute({List? children}) : super( - BackupDetailsRoute.name, + AppSettingsRoute.name, initialChildren: children, ); - static const String name = 'BackupDetailsRoute'; + static const String name = 'AppSettingsRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [DeveloperSettingsPage] +class DeveloperSettingsRoute extends PageRouteInfo { + const DeveloperSettingsRoute({List? children}) + : super( + DeveloperSettingsRoute.name, + initialChildren: children, + ); + + static const String name = 'DeveloperSettingsRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [MorePage] +class MoreRoute extends PageRouteInfo { + const MoreRoute({List? children}) + : super( + MoreRoute.name, + initialChildren: children, + ); + + static const String name = 'MoreRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [AboutApplicationPage] +class AboutApplicationRoute extends PageRouteInfo { + const AboutApplicationRoute({List? children}) + : super( + AboutApplicationRoute.name, + initialChildren: children, + ); + + static const String name = 'AboutApplicationRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [ConsolePage] +class ConsoleRoute extends PageRouteInfo { + const ConsoleRoute({List? children}) + : super( + ConsoleRoute.name, + initialChildren: children, + ); + + static const String name = 'ConsoleRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [ProvidersPage] +class ProvidersRoute extends PageRouteInfo { + const ProvidersRoute({List? children}) + : super( + ProvidersRoute.name, + initialChildren: children, + ); + + static const String name = 'ProvidersRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [RecoveryKeyPage] +class RecoveryKeyRoute extends PageRouteInfo { + const RecoveryKeyRoute({List? children}) + : super( + RecoveryKeyRoute.name, + initialChildren: children, + ); + + static const String name = 'RecoveryKeyRoute'; static const PageInfo page = PageInfo(name); } @@ -465,31 +479,84 @@ class InitializingRoute extends PageRouteInfo { } /// generated route for -/// [RecoveryKeyPage] -class RecoveryKeyRoute extends PageRouteInfo { - const RecoveryKeyRoute({List? children}) - : super( - RecoveryKeyRoute.name, +/// [ServerStoragePage] +class ServerStorageRoute extends PageRouteInfo { + ServerStorageRoute({ + required DiskStatus diskStatus, + Key? key, + List? children, + }) : super( + ServerStorageRoute.name, + args: ServerStorageRouteArgs( + diskStatus: diskStatus, + key: key, + ), initialChildren: children, ); - static const String name = 'RecoveryKeyRoute'; + static const String name = 'ServerStorageRoute'; - static const PageInfo page = PageInfo(name); + static const PageInfo page = + PageInfo(name); +} + +class ServerStorageRouteArgs { + const ServerStorageRouteArgs({ + required this.diskStatus, + this.key, + }); + + final DiskStatus diskStatus; + + final Key? key; + + @override + String toString() { + return 'ServerStorageRouteArgs{diskStatus: $diskStatus, key: $key}'; + } } /// generated route for -/// [DevicesScreen] -class DevicesRoute extends PageRouteInfo { - const DevicesRoute({List? children}) - : super( - DevicesRoute.name, +/// [ExtendingVolumePage] +class ExtendingVolumeRoute extends PageRouteInfo { + ExtendingVolumeRoute({ + required DiskVolume diskVolumeToResize, + required DiskStatus diskStatus, + Key? key, + List? children, + }) : super( + ExtendingVolumeRoute.name, + args: ExtendingVolumeRouteArgs( + diskVolumeToResize: diskVolumeToResize, + diskStatus: diskStatus, + key: key, + ), initialChildren: children, ); - static const String name = 'DevicesRoute'; + static const String name = 'ExtendingVolumeRoute'; - static const PageInfo page = PageInfo(name); + static const PageInfo page = + PageInfo(name); +} + +class ExtendingVolumeRouteArgs { + const ExtendingVolumeRouteArgs({ + required this.diskVolumeToResize, + required this.diskStatus, + this.key, + }); + + final DiskVolume diskVolumeToResize; + + final DiskStatus diskStatus; + + final Key? key; + + @override + String toString() { + return 'ExtendingVolumeRouteArgs{diskVolumeToResize: $diskVolumeToResize, diskStatus: $diskStatus, key: $key}'; + } } /// generated route for @@ -541,96 +608,29 @@ class ServicesMigrationRouteArgs { } /// generated route for -/// [ExtendingVolumePage] -class ExtendingVolumeRoute extends PageRouteInfo { - ExtendingVolumeRoute({ - required DiskVolume diskVolumeToResize, - required DiskStatus diskStatus, - Key? key, - List? children, - }) : super( - ExtendingVolumeRoute.name, - args: ExtendingVolumeRouteArgs( - diskVolumeToResize: diskVolumeToResize, - diskStatus: diskStatus, - key: key, - ), - initialChildren: children, - ); - - static const String name = 'ExtendingVolumeRoute'; - - static const PageInfo page = - PageInfo(name); -} - -class ExtendingVolumeRouteArgs { - const ExtendingVolumeRouteArgs({ - required this.diskVolumeToResize, - required this.diskStatus, - this.key, - }); - - final DiskVolume diskVolumeToResize; - - final DiskStatus diskStatus; - - final Key? key; - - @override - String toString() { - return 'ExtendingVolumeRouteArgs{diskVolumeToResize: $diskVolumeToResize, diskStatus: $diskStatus, key: $key}'; - } -} - -/// generated route for -/// [ServerStoragePage] -class ServerStorageRoute extends PageRouteInfo { - ServerStorageRoute({ - required DiskStatus diskStatus, - Key? key, - List? children, - }) : super( - ServerStorageRoute.name, - args: ServerStorageRouteArgs( - diskStatus: diskStatus, - key: key, - ), - initialChildren: children, - ); - - static const String name = 'ServerStorageRoute'; - - static const PageInfo page = - PageInfo(name); -} - -class ServerStorageRouteArgs { - const ServerStorageRouteArgs({ - required this.diskStatus, - this.key, - }); - - final DiskStatus diskStatus; - - final Key? key; - - @override - String toString() { - return 'ServerStorageRouteArgs{diskStatus: $diskStatus, key: $key}'; - } -} - -/// generated route for -/// [RootPage] -class RootRoute extends PageRouteInfo { - const RootRoute({List? children}) +/// [DevicesScreen] +class DevicesRoute extends PageRouteInfo { + const DevicesRoute({List? children}) : super( - RootRoute.name, + DevicesRoute.name, initialChildren: children, ); - static const String name = 'RootRoute'; + static const String name = 'DevicesRoute'; + + static const PageInfo page = PageInfo(name); +} + +/// generated route for +/// [OnboardingPage] +class OnboardingRoute extends PageRouteInfo { + const OnboardingRoute({List? children}) + : super( + OnboardingRoute.name, + initialChildren: children, + ); + + static const String name = 'OnboardingRoute'; static const PageInfo page = PageInfo(name); } From 0fb404bed43dc5adcd56bc4e23ceca7ba68e4441 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Mon, 15 May 2023 12:22:06 +0300 Subject: [PATCH 04/10] fix(ui): Privacy policy link --- lib/ui/pages/more/about_application.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/pages/more/about_application.dart b/lib/ui/pages/more/about_application.dart index 54e493de..1fa5c932 100644 --- a/lib/ui/pages/more/about_application.dart +++ b/lib/ui/pages/more/about_application.dart @@ -50,7 +50,7 @@ class AboutApplicationPage extends StatelessWidget { children: [ TextButton( onPressed: () => launchUrl( - Uri.parse('https://selfprivacy.ru/privacy-policy'), + Uri.parse('https://selfprivacy.org/privacy-policy/'), mode: LaunchMode.externalApplication, ), child: Text('about_application_page.privacy_policy'.tr()), From e9665ad75d1dcc2cd4768801a6f939eebcd53917 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 16 May 2023 11:06:01 -0300 Subject: [PATCH 05/10] feat: Implement polymorphic DNS check for DNS API --- .../dns_providers/cloudflare/cloudflare.dart | 144 +++++++++++ .../rest_maps/dns_providers/desec/desec.dart | 226 +++++++++++++++--- .../rest_maps/dns_providers/dns_provider.dart | 12 +- .../server_providers/hetzner/hetzner.dart | 2 +- .../cubit/dns_records/dns_records_cubit.dart | 92 +++---- lib/utils/network_utils.dart | 81 ------- 6 files changed, 385 insertions(+), 172 deletions(-) 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 04ff622b..eb22e4d8 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 @@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; class CloudflareApi extends DnsProviderApi { CloudflareApi({ @@ -317,4 +318,147 @@ class CloudflareApi extends DnsProviderApi { return domains; } + + @override + Future>> validateDnsRecords( + final ServerDomain domain, + final String ip4, + final String dkimPublicKey, + ) async { + final List records = await getDnsRecords(domain: domain); + final List foundRecords = []; + try { + final List desiredRecords = + getDesiredDnsRecords(domain.domainName, ip4, dkimPublicKey); + for (final DesiredDnsRecord record in desiredRecords) { + if (record.description == 'record.dkim') { + final DnsRecord foundRecord = records.firstWhere( + (final r) => (r.name == record.name) && r.type == record.type, + orElse: () => DnsRecord( + name: record.name, + type: record.type, + content: '', + ttl: 800, + proxied: false, + ), + ); + // remove all spaces and tabulators from + // the foundRecord.content and the record.content + // to compare them + final String? foundContent = + foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); + final String content = record.content.replaceAll(RegExp(r'\s+'), ''); + if (foundContent == content) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } else { + if (records.any( + (final r) => + (r.name == record.name) && + r.type == record.type && + r.content == record.content, + )) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } + } + } catch (e) { + print(e); + return APIGenericResult( + data: [], + success: false, + message: e.toString(), + ); + } + return APIGenericResult( + data: foundRecords, + success: true, + ); + } + + @override + List getDesiredDnsRecords( + final String? domainName, + final String? ip4, + final String? dkimPublicKey, + ) { + if (domainName == null || ip4 == null) { + return []; + } + return [ + DesiredDnsRecord( + name: domainName, + content: ip4, + description: 'record.root', + ), + DesiredDnsRecord( + name: 'api.$domainName', + content: ip4, + description: 'record.api', + ), + DesiredDnsRecord( + name: 'cloud.$domainName', + content: ip4, + description: 'record.cloud', + ), + DesiredDnsRecord( + name: 'git.$domainName', + content: ip4, + description: 'record.git', + ), + DesiredDnsRecord( + name: 'meet.$domainName', + content: ip4, + description: 'record.meet', + ), + DesiredDnsRecord( + name: 'social.$domainName', + content: ip4, + description: 'record.social', + ), + DesiredDnsRecord( + name: 'password.$domainName', + content: ip4, + description: 'record.password', + ), + DesiredDnsRecord( + name: 'vpn.$domainName', + content: ip4, + 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:$ip4 -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/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart index 4e55bbfa..91eed57a 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart @@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; class DesecApi extends DnsProviderApi { DesecApi({ @@ -61,6 +62,7 @@ class DesecApi extends DnsProviderApi { headers: {'Authorization': 'Token $token'}, ), ); + await Future.delayed(const Duration(seconds: 1)); } catch (e) { print(e); isValid = false; @@ -102,13 +104,29 @@ class DesecApi extends DnsProviderApi { }) async { final String domainName = domain.domainName; final String url = '/$domainName/rrsets/'; + final List listDnsRecords = projectDnsRecords(domainName, ip4); final Dio client = await getClient(); try { - final Response response = await client.get(url); - - final List records = response.data; - await client.put(url, data: records); + final List bulkRecords = []; + for (final DnsRecord record in listDnsRecords) { + bulkRecords.add( + record.name == null + ? { + 'type': record.type, + 'ttl': record.ttl, + 'records': [], + } + : { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [], + }, + ); + } + await client.put(url, data: bulkRecords); + await Future.delayed(const Duration(seconds: 1)); } catch (e) { print(e); return APIGenericResult( @@ -136,14 +154,18 @@ class DesecApi extends DnsProviderApi { final Dio client = await getClient(); try { response = await client.get(url); + await Future.delayed(const Duration(seconds: 1)); final List records = response.data; for (final record in records) { + final String? content = (record['records'] is List) + ? record['records'][0] + : record['records']; allRecords.add( DnsRecord( name: record['subname'], type: record['type'], - content: record['records'], + content: content, ttl: record['ttl'], ), ); @@ -164,30 +186,31 @@ class DesecApi extends DnsProviderApi { }) async { final String domainName = domain.domainName; final List listDnsRecords = projectDnsRecords(domainName, ip4); - final List allCreateFutures = []; final Dio client = await getClient(); try { + final List bulkRecords = []; for (final DnsRecord record in listDnsRecords) { - allCreateFutures.add( - client.post( - '/$domainName/rrsets/', - data: record.name == null - ? { - 'type': record.type, - 'ttl': record.ttl, - 'records': [record.content], - } - : { - 'subname': record.name, - 'type': record.type, - 'ttl': record.ttl, - 'records': [record.content], - }, - ), + bulkRecords.add( + record.name == null + ? { + 'type': record.type, + 'ttl': record.ttl, + 'records': [record.content], + } + : { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [record.content], + }, ); } - await Future.wait(allCreateFutures); + await client.post( + '/$domainName/rrsets/', + data: bulkRecords, + ); + await Future.delayed(const Duration(seconds: 1)); } on DioError catch (e) { print(e.message); rethrow; @@ -209,9 +232,10 @@ class DesecApi extends DnsProviderApi { final String? domainName, final String? ip4, ) { - final DnsRecord domainA = DnsRecord(type: 'A', name: null, content: ip4); + final DnsRecord domainA = DnsRecord(type: 'A', name: '', content: ip4); - final DnsRecord mx = DnsRecord(type: 'MX', name: null, content: domainName); + final DnsRecord mx = + DnsRecord(type: 'MX', name: '', content: '10 $domainName.'); final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4); final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4); final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4); @@ -225,14 +249,14 @@ class DesecApi extends DnsProviderApi { final DnsRecord txt1 = DnsRecord( type: 'TXT', name: '_dmarc', - content: 'v=DMARC1; p=none', + content: '"v=DMARC1; p=none"', ttl: 18000, ); final DnsRecord txt2 = DnsRecord( type: 'TXT', - name: null, - content: 'v=spf1 a mx ip4:$ip4 -all', + name: '', + content: '"v=spf1 a mx ip4:$ip4 -all"', ttl: 18000, ); @@ -275,6 +299,7 @@ class DesecApi extends DnsProviderApi { 'records': [record.content], }, ); + await Future.delayed(const Duration(seconds: 1)); } catch (e) { print(e); } finally { @@ -291,6 +316,7 @@ class DesecApi extends DnsProviderApi { final Response response = await client.get( '', ); + await Future.delayed(const Duration(seconds: 1)); domains = response.data .map((final el) => el['name'] as String) .toList(); @@ -302,4 +328,148 @@ class DesecApi extends DnsProviderApi { return domains; } + + @override + Future>> validateDnsRecords( + final ServerDomain domain, + final String ip4, + final String dkimPublicKey, + ) async { + final List records = await getDnsRecords(domain: domain); + final List foundRecords = []; + try { + final List desiredRecords = + getDesiredDnsRecords(domain.domainName, ip4, dkimPublicKey); + for (final DesiredDnsRecord record in desiredRecords) { + if (record.description == 'record.dkim') { + final DnsRecord foundRecord = records.firstWhere( + (final r) => (r.name == record.name) && r.type == record.type, + orElse: () => DnsRecord( + name: record.name, + type: record.type, + content: '', + ttl: 800, + proxied: false, + ), + ); + // remove all spaces and tabulators from + // the foundRecord.content and the record.content + // to compare them + final String? foundContent = + foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); + final String content = record.content.replaceAll(RegExp(r'\s+'), ''); + if (foundContent == content) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } else { + if (records.any( + (final r) => + ('${r.name}.${domain.domainName}' == record.name || + record.name == '') && + r.type == record.type && + r.content == record.content, + )) { + foundRecords.add(record.copyWith(isSatisfied: true)); + } else { + foundRecords.add(record.copyWith(isSatisfied: false)); + } + } + } + } catch (e) { + print(e); + return APIGenericResult( + data: [], + success: false, + message: e.toString(), + ); + } + return APIGenericResult( + data: foundRecords, + success: true, + ); + } + + @override + List getDesiredDnsRecords( + final String? domainName, + final String? ip4, + final String? dkimPublicKey, + ) { + if (domainName == null || ip4 == null) { + return []; + } + return [ + DesiredDnsRecord( + name: '', + content: ip4, + description: 'record.root', + ), + DesiredDnsRecord( + name: 'api.$domainName', + content: ip4, + description: 'record.api', + ), + DesiredDnsRecord( + name: 'cloud.$domainName', + content: ip4, + description: 'record.cloud', + ), + DesiredDnsRecord( + name: 'git.$domainName', + content: ip4, + description: 'record.git', + ), + DesiredDnsRecord( + name: 'meet.$domainName', + content: ip4, + description: 'record.meet', + ), + DesiredDnsRecord( + name: 'social.$domainName', + content: ip4, + description: 'record.social', + ), + DesiredDnsRecord( + name: 'password.$domainName', + content: ip4, + description: 'record.password', + ), + DesiredDnsRecord( + name: 'vpn.$domainName', + content: ip4, + description: 'record.vpn', + ), + DesiredDnsRecord( + name: '', + content: '10 $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: '', + content: '"v=spf1 a mx ip4:$ip4 -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/api_maps/rest_maps/dns_providers/dns_provider.dart b/lib/logic/api_maps/rest_maps/dns_providers/dns_provider.dart index 106d185c..b85f94d1 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 @@ -2,6 +2,7 @@ import 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; export 'package:selfprivacy/logic/api_maps/api_generic_result.dart'; @@ -26,9 +27,18 @@ abstract class DnsProviderApi extends ApiMap { final DnsRecord record, final ServerDomain domain, ); + Future>> validateDnsRecords( + final ServerDomain domain, + final String ip4, + final String dkimPublicKey, + ); + List getDesiredDnsRecords( + final String? domainName, + final String? ip4, + final String? dkimPublicKey, + ); Future getZoneId(final String domain); Future> domainList(); - Future> isApiTokenValid(final String token); RegExp getApiTokenValidation(); } diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index c2228030..f9a2104e 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart @@ -394,7 +394,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { final String apiToken = StringGenerators.apiToken(); final String hostname = getHostnameFromDomain(domainName); - const String infectBranch = 'providers/hetzner'; + const String infectBranch = 'testing/desec'; final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false'; final String base64Password = base64.encode(utf8.encode(rootUser.password ?? 'PASS')); diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 3403dc68..de87910d 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -25,11 +25,13 @@ class DnsRecordsCubit emit( DnsRecordsState( dnsState: DnsRecordsStatus.refreshing, - dnsRecords: getDesiredDnsRecords( - serverInstallationCubit.state.serverDomain?.domainName, - '', - '', - ), + dnsRecords: ApiController.currentDnsProviderApiFactory! + .getDnsProvider() + .getDesiredDnsRecords( + serverInstallationCubit.state.serverDomain?.domainName, + '', + '', + ), ), ); @@ -37,64 +39,32 @@ class DnsRecordsCubit final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4; - if (domain != null && ipAddress != null) { - final List records = await ApiController - .currentDnsProviderApiFactory! - .getDnsProvider() - .getDnsRecords(domain: domain); - final String? dkimPublicKey = - extractDkimRecord(await api.getDnsRecords())?.content; - final List desiredRecords = - getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey); - final List foundRecords = []; - for (final DesiredDnsRecord record in desiredRecords) { - if (record.description == 'record.dkim') { - final DnsRecord foundRecord = records.firstWhere( - (final r) => r.name == record.name && r.type == record.type, - orElse: () => DnsRecord( - name: record.name, - type: record.type, - content: '', - ttl: 800, - proxied: false, - ), - ); - // remove all spaces and tabulators from - // the foundRecord.content and the record.content - // to compare them - final String? foundContent = - foundRecord.content?.replaceAll(RegExp(r'\s+'), ''); - final String content = - record.content.replaceAll(RegExp(r'\s+'), ''); - if (foundContent == content) { - foundRecords.add(record.copyWith(isSatisfied: true)); - } else { - foundRecords.add(record.copyWith(isSatisfied: false)); - } - } else { - if (records.any( - (final r) => - r.name == record.name && - r.type == record.type && - r.content == record.content, - )) { - foundRecords.add(record.copyWith(isSatisfied: true)); - } else { - foundRecords.add(record.copyWith(isSatisfied: false)); - } - } - } - emit( - DnsRecordsState( - dnsRecords: foundRecords, - dnsState: foundRecords.any((final r) => r.isSatisfied == false) - ? DnsRecordsStatus.error - : DnsRecordsStatus.good, - ), - ); - } else { + if (domain == null && ipAddress == null) { emit(const DnsRecordsState()); + return; } + + final foundRecords = await ApiController.currentDnsProviderApiFactory! + .getDnsProvider() + .validateDnsRecords( + domain!, + ipAddress!, + extractDkimRecord(await api.getDnsRecords())?.content ?? '', + ); + + if (!foundRecords.success || foundRecords.data.isEmpty) { + emit(const DnsRecordsState()); + return; + } + + emit( + DnsRecordsState( + dnsRecords: foundRecords.data, + dnsState: foundRecords.data.any((final r) => r.isSatisfied == false) + ? DnsRecordsStatus.error + : DnsRecordsStatus.good, + ), + ); } } diff --git a/lib/utils/network_utils.dart b/lib/utils/network_utils.dart index fc06ecb8..3b7f9219 100644 --- a/lib/utils/network_utils.dart +++ b/lib/utils/network_utils.dart @@ -41,87 +41,6 @@ class DesiredDnsRecord { ); } -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; From 6ddc2328f04111c868c49d2f372854fa8ad1fade Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 16 May 2023 12:49:55 -0300 Subject: [PATCH 06/10] feat: Implement proper DKIM creation for deSEC --- assets/translations/en.json | 4 +++- assets/translations/ru.json | 4 +++- .../rest_maps/dns_providers/desec/desec.dart | 19 ++++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 0d825bff..b902c3a2 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -286,11 +286,13 @@ "select_provider_countries_text_hetzner": "Germany, Finland, USA", "select_provider_countries_text_do": "USA, Netherlands, Singapore, UK, Germany, Canada, India, Australia", "select_provider_price_title": "Average price", + "select_provider_price_free": "Free", "select_provider_price_text_hetzner": "€8 per month for a relatively small server and 50GB of disk storage", "select_provider_price_text_do": "$17 per month for a relatively small server and 50GB of disk storage", "select_provider_payment_title": "Payment methods", "select_provider_payment_text_hetzner": "Credit cards, SWIFT, SEPA, PayPal", "select_provider_payment_text_do": "Credit cards, Google Pay, PayPal", + "select_provider_payment_text_cloudflare": "Credit cards", "select_provider_email_notice": "E-mail hosting won't be available for new clients. Nevertheless it will be unlocked as soon as you complete your first payment.", "select_provider_site_button": "Visit site", "connect_to_server_provider": "Now log in ", @@ -506,4 +508,4 @@ "reset_onboarding_description": "Reset onboarding switch to show onboarding screen again", "cubit_statuses": "Cubit loading statuses" } -} +} \ No newline at end of file diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 35498bd9..6b624c70 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -280,10 +280,12 @@ "select_provider_countries_text_hetzner": "Германия, Финляндия, США", "select_provider_countries_text_do": "США, Нидерланды, Сингапур, Великобритания, Германия, Канада, Индия, Австралия", "select_provider_price_title": "Средняя цена", + "select_provider_price_free": "Бесплатно", "select_provider_price_text_hetzner": "€8 в месяц за небольшой сервер и 50GB места на диске", "select_provider_price_text_do": "$17 в месяц за небольшой сервер и 50GB места на диске", "select_provider_payment_title": "Методы оплаты", "select_provider_payment_text_hetzner": "Банковские карты, SWIFT, SEPA, PayPal", + "select_provider_payment_text_cloudflare": "Банковские карты", "select_provider_payment_text_do": "Банковские карты, Google Pay, PayPal", "select_provider_email_notice": "Хостинг электронной почты недоступен для новых клиентов. Разблокировать можно будет после первой оплаты.", "select_provider_site_button": "Посетить сайт", @@ -473,4 +475,4 @@ "length_not_equal": "Длина строки [], должна быть равна {}", "length_longer": "Длина строки [], должна быть меньше либо равна {}" } -} +} \ No newline at end of file diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart index 91eed57a..0a8197dc 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart @@ -196,13 +196,13 @@ class DesecApi extends DnsProviderApi { ? { 'type': record.type, 'ttl': record.ttl, - 'records': [record.content], + 'records': [extractContent(record)], } : { 'subname': record.name, 'type': record.type, 'ttl': record.ttl, - 'records': [record.content], + 'records': [extractContent(record)], }, ); } @@ -275,6 +275,15 @@ class DesecApi extends DnsProviderApi { ]; } + String? extractContent(final DnsRecord record) { + String? content = record.content; + if (record.type == 'TXT' && content != null && !content.startsWith('"')) { + content = '"$content"'; + } + + return content; + } + @override Future setDnsRecord( final DnsRecord record, @@ -290,13 +299,13 @@ class DesecApi extends DnsProviderApi { ? { 'type': record.type, 'ttl': record.ttl, - 'records': [record.content], + 'records': [extractContent(record)], } : { 'subname': record.name, 'type': record.type, 'ttl': record.ttl, - 'records': [record.content], + 'records': [extractContent(record)], }, ); await Future.delayed(const Duration(seconds: 1)); @@ -465,7 +474,7 @@ class DesecApi extends DnsProviderApi { if (dkimPublicKey != null) DesiredDnsRecord( name: 'selector._domainkey.$domainName', - content: dkimPublicKey, + content: '"$dkimPublicKey"', description: 'record.dkim', type: 'TXT', category: DnsRecordsCategory.email, From 732f39ef48d2eee8b65a1103755505a68ccd30f1 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 16 May 2023 14:58:51 -0300 Subject: [PATCH 07/10] chore: Add assets for deSEC --- assets/images/logos/cloudflare.svg | 1 + assets/markdown/how_desec-en.md | 9 +++++++++ assets/markdown/how_desec-ru.md | 9 +++++++++ assets/translations/en.json | 8 ++++---- assets/translations/ru.json | 8 ++++---- 5 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 assets/images/logos/cloudflare.svg create mode 100644 assets/markdown/how_desec-en.md create mode 100644 assets/markdown/how_desec-ru.md diff --git a/assets/images/logos/cloudflare.svg b/assets/images/logos/cloudflare.svg new file mode 100644 index 00000000..7099a7e9 --- /dev/null +++ b/assets/images/logos/cloudflare.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/markdown/how_desec-en.md b/assets/markdown/how_desec-en.md new file mode 100644 index 00000000..c86d3855 --- /dev/null +++ b/assets/markdown/how_desec-en.md @@ -0,0 +1,9 @@ +### How to get deSEC API Token +1. Log in at: https://desec.io/login +2. Go to **Domains** page at: https://desec.io/domains +3. Go to **Token management** tab. +4. Click on the round "plus" button in the upper right corner. +5. **"Generate New Token"** dialogue must be displayed. Enter any **Token name** you wish. *Advanced settings* are not required, so do not touch anything there. +6. Click on **Save**. +7. Make sure you **save** the token's **secret value** as it will only be displayed once. +8. Now you can safely **close** the dialogue. \ No newline at end of file diff --git a/assets/markdown/how_desec-ru.md b/assets/markdown/how_desec-ru.md new file mode 100644 index 00000000..a93acc77 --- /dev/null +++ b/assets/markdown/how_desec-ru.md @@ -0,0 +1,9 @@ +### Как получить deSEC API Токен +1. Авторизуемся в deSEC: https://desec.io/login +2. Переходим на страницу **Domains** по ссылке: https://desec.io/domains +3. Переходим на вкладку **Token management**. +4. Нажимаем на большую кнопку с плюсом в правом верхнем углу страницы. +5. Должен был появиться **"Generate New Token"** диалог. Вводим любое имя токена в **Token name**. *Advanced settings* необязательны, так что ничего там не трогаем. +6. Кликаем **Save**. +7. Обязательно сохраняем "**secret value**" ключ токена, потому что он отображается исключительно один раз. +8. Теперь спокойно закрываем диалог, нажав **close**. \ No newline at end of file diff --git a/assets/translations/en.json b/assets/translations/en.json index b902c3a2..653e33f8 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -313,13 +313,13 @@ "choose_server_type_storage": "{} GB of system storage", "choose_server_type_payment_per_month": "{} per month", "no_server_types_found": "No available server types found. Make sure your account is accessible and try to change your server location.", - "cloudflare_bad_key_error": "Cloudflare API key is invalid", + "cloudflare_bad_key_error": "DNS Provider API key is invalid", "backblaze_bad_key_error": "Backblaze storage information is invalid", "select_dns": "Now let's select a DNS provider", "manage_domain_dns": "To manage your domain's DNS", "use_this_domain": "Use this domain?", "use_this_domain_text": "The token you provided gives access to the following domain", - "cloudflare_api_token": "CloudFlare API Token", + "cloudflare_api_token": "DNS Provider API Token", "connect_backblaze_storage": "Connect Backblaze storage", "no_connected_domains": "No connected domains at the moment", "loading_domain_list": "Loading domain list", @@ -390,8 +390,8 @@ "modal_confirmation_dns_invalid": "Reverse DNS points to another domain", "modal_confirmation_ip_valid": "IP is the same as in DNS record", "modal_confirmation_ip_invalid": "IP is not the same as in DNS record", - "confirm_cloudflare": "Connect to CloudFlare", - "confirm_cloudflare_description": "Enter a Cloudflare token with access to {}:", + "confirm_cloudflare": "Connect to your DNS Provider", + "confirm_cloudflare_description": "Enter a token of your DNS Provider with access to {}:", "confirm_backblaze": "Connect to Backblaze", "confirm_backblaze_description": "Enter a Backblaze token with access to backup storage:" }, diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 6b624c70..b4bdd938 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -307,12 +307,12 @@ "choose_server_type_storage": "{} GB системного хранилища", "choose_server_type_payment_per_month": "{} в месяц", "no_server_types_found": "Не найдено доступных типов сервера! Пожалуйста, убедитесь, что у вас есть доступ к провайдеру сервера...", - "cloudflare_bad_key_error": "Cloudflare API ключ неверен", + "cloudflare_bad_key_error": "API ключ неверен", "backblaze_bad_key_error": "Информация о Backblaze хранилище неверна", "manage_domain_dns": "Для управления DNS вашего домена", "use_this_domain": "Используем этот домен?", "use_this_domain_text": "Указанный вами токен даёт контроль над этим доменом", - "cloudflare_api_token": "CloudFlare API ключ", + "cloudflare_api_token": "API ключ DNS провайдера", "connect_backblaze_storage": "Подключите облачное хранилище Backblaze", "no_connected_domains": "На данный момент подлюченных доменов нет", "loading_domain_list": "Загружаем список доменов", @@ -368,8 +368,8 @@ "modal_confirmation_dns_invalid": "Обратный DNS указывает на другой домен", "modal_confirmation_ip_valid": "IP совпадает с указанным в DNS записи", "modal_confirmation_ip_invalid": "IP не совпадает с указанным в DNS записи", - "confirm_cloudflare": "Подключение к Cloudflare", - "confirm_cloudflare_description": "Введите токен Cloudflare, который имеет права на {}:", + "confirm_cloudflare": "Подключение к DNS Провайдеру", + "confirm_cloudflare_description": "Введите токен DNS Провайдера, который имеет права на {}:", "confirm_backblaze_description": "Введите токен Backblaze, который имеет права на хранилище резервных копий:", "confirm_backblaze": "Подключение к Backblaze", "server_provider_connected": "Подключение к вашему серверному провайдеру", From ce017c6ea8460a250f4d56248dbad204bb48d6b4 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 16 May 2023 15:03:31 -0300 Subject: [PATCH 08/10] fix: Make minor improvements for deSEC --- .../rest_maps/dns_providers/desec/desec.dart | 4 ++- .../initializing/dns_provider_picker.dart | 35 +++++++------------ .../recovery_server_provider_connected.dart | 4 +-- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart index 0a8197dc..aea1d0da 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart @@ -352,7 +352,9 @@ class DesecApi extends DnsProviderApi { for (final DesiredDnsRecord record in desiredRecords) { if (record.description == 'record.dkim') { final DnsRecord foundRecord = records.firstWhere( - (final r) => (r.name == record.name) && r.type == record.type, + (final r) => + ('${r.name}.${domain.domainName}' == record.name) && + r.type == record.type, orElse: () => DnsRecord( name: record.name, type: record.type, diff --git a/lib/ui/pages/setup/initializing/dns_provider_picker.dart b/lib/ui/pages/setup/initializing/dns_provider_picker.dart index cb0d2111..7ee580a7 100644 --- a/lib/ui/pages/setup/initializing/dns_provider_picker.dart +++ b/lib/ui/pages/setup/initializing/dns_provider_picker.dart @@ -5,6 +5,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart'; +import 'package:selfprivacy/logic/cubit/support_system/support_system_cubit.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/ui/components/brand_md/brand_md.dart'; import 'package:selfprivacy/ui/components/buttons/brand_button.dart'; @@ -51,7 +52,7 @@ class _DnsProviderPickerState extends State { providerType: DnsProvider.cloudflare, pathToHow: 'how_cloudflare', image: Image.asset( - 'assets/images/logos/cloudflare.png', + 'assets/images/logos/cloudflare.svg', width: 150, ), ), @@ -62,9 +63,9 @@ class _DnsProviderPickerState extends State { providerCubit: widget.formCubit, providerInfo: ProviderPageInfo( providerType: DnsProvider.desec, - pathToHow: 'how_digital_ocean_dns', + pathToHow: 'how_desec', image: Image.asset( - 'assets/images/logos/digital_ocean.png', + 'assets/images/logos/desec.svg', width: 150, ), ), @@ -100,7 +101,7 @@ class ProviderInputDataPage extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'initializing.connect_to_dns'.tr(), + 'initializing.cloudflare_api_token'.tr(), style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox(height: 16), @@ -125,22 +126,12 @@ class ProviderInputDataPage extends StatelessWidget { const SizedBox(height: 10), BrandOutlinedButton( child: Text('initializing.how'.tr()), - onPressed: () => showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (final BuildContext context) => Padding( - padding: paddingH15V0, - child: ListView( - padding: const EdgeInsets.symmetric(vertical: 16), - children: [ - BrandMarkdown( - fileName: providerInfo.pathToHow, - ), - ], - ), - ), - ), + onPressed: () { + context.read().showArticle( + article: providerInfo.pathToHow, + context: context, + ); + }, ), ], ); @@ -186,7 +177,7 @@ class ProviderSelectionPage extends StatelessWidget { padding: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(40), - color: const Color.fromARGB(255, 241, 215, 166), + color: const Color.fromARGB(255, 244, 128, 31), ), child: SvgPicture.asset( 'assets/images/logos/cloudflare.svg', @@ -194,7 +185,7 @@ class ProviderSelectionPage extends StatelessWidget { ), const SizedBox(width: 16), Text( - 'Hetzner Cloud', + 'Cloudflare', style: Theme.of(context).textTheme.titleMedium, ), ], diff --git a/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart b/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart index 91999fb9..93d0eb87 100644 --- a/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart +++ b/lib/ui/pages/setup/recovering/recovery_server_provider_connected.dart @@ -45,9 +45,7 @@ class RecoveryServerProviderConnected extends StatelessWidget { ), const SizedBox(height: 16), BrandButton.filled( - onPressed: formCubitState.isSubmitting - ? null - : () => context.read().trySubmit(), + onPressed: () => context.read().trySubmit(), child: Text('basis.continue'.tr()), ), const SizedBox(height: 16), From 232699bdb1efdad9693906fc9d252ce1fd6d685f Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 16 May 2023 15:49:03 -0300 Subject: [PATCH 09/10] feat: Implement proper access recovery for DNS providers --- lib/logic/api_maps/graphql_maps/api_map.dart | 2 +- lib/logic/api_maps/staging_options.dart | 12 +++++++++-- .../cubit/dns_records/dns_records_cubit.dart | 3 +++ .../server_installation_cubit.dart | 18 ++++++++++++----- .../server_installation_repository.dart | 20 +++++++++++-------- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/lib/logic/api_maps/graphql_maps/api_map.dart b/lib/logic/api_maps/graphql_maps/api_map.dart index a633866e..34e39b7a 100644 --- a/lib/logic/api_maps/graphql_maps/api_map.dart +++ b/lib/logic/api_maps/graphql_maps/api_map.dart @@ -56,7 +56,7 @@ class ResponseLoggingParser extends ResponseParser { abstract class ApiMap { Future getClient() async { IOClient? ioClient; - if (StagingOptions.stagingAcme) { + if (StagingOptions.stagingAcme || !StagingOptions.verifyCertificate) { final HttpClient httpClient = HttpClient(); httpClient.badCertificateCallback = ( final cert, diff --git a/lib/logic/api_maps/staging_options.dart b/lib/logic/api_maps/staging_options.dart index 7d3084b7..a4e98fe8 100644 --- a/lib/logic/api_maps/staging_options.dart +++ b/lib/logic/api_maps/staging_options.dart @@ -1,8 +1,16 @@ -/// Controls staging environment for network, is used during manual -/// integration testing and such +/// Controls staging environment for network class StagingOptions { /// Whether we request for staging temprorary certificates. /// Hardcode to 'true' in the middle of testing to not /// get your domain banned by constant certificate renewal + /// + /// If set to 'true', the 'verifyCertificate' becomes useless static bool get stagingAcme => false; + + /// Should we consider CERTIFICATE_VERIFY_FAILED code an error + /// For now it's just a global variable and DNS API + /// classes can change it at will + /// + /// Doesn't matter if 'statingAcme' is set to 'true' + static bool verifyCertificate = false; } diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index de87910d..2e57f0f7 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -1,7 +1,10 @@ import 'package:cubit_form/cubit_form.dart'; +import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; +import 'package:selfprivacy/logic/get_it/api_config.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index cc5bffcc..fa0a04e1 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -541,13 +541,20 @@ class ServerInstallationCubit extends Cubit { customToken: serverDetails.apiToken, isWithToken: true, ).getServerProviderType(); - if (provider == ServerProvider.unknown) { + final dnsProvider = await ServerApi( + customToken: serverDetails.apiToken, + isWithToken: true, + ).getDnsProviderType(); + if (provider == ServerProvider.unknown || + dnsProvider == DnsProvider.unknown) { getIt() .showSnackBar('recovering.generic_error'.tr()); return; } await repository.saveServerDetails(serverDetails); + await repository.saveDnsProviderType(dnsProvider); setServerProviderType(provider); + setDnsProviderType(dnsProvider); emit( dataState.copyWith( serverDetails: serverDetails, @@ -700,14 +707,15 @@ class ServerInstallationCubit extends Cubit { .showSnackBar('recovering.domain_not_available_on_token'.tr()); return; } + final dnsProviderType = await ServerApi( + customToken: dataState.serverDetails!.apiToken, + isWithToken: true, + ).getDnsProviderType(); await repository.saveDomain( ServerDomain( domainName: serverDomain.domainName, zoneId: zoneId, - provider: await ServerApi( - customToken: token, - isWithToken: true, - ).getDnsProviderType(), + provider: dnsProviderType, ), ); await repository.saveDnsProviderKey(token); diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index ff167518..bc92f645 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -15,6 +15,7 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider. import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_api_settings.dart'; import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart'; +import 'package:selfprivacy/logic/api_maps/staging_options.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; @@ -76,17 +77,20 @@ class ServerInstallationRepository { ); } - if (dnsProvider != null || - (serverDomain != null && - serverDomain.provider != DnsProvider.unknown)) { - ApiController.initDnsProviderApiFactory( - DnsProviderApiFactorySettings( - provider: dnsProvider ?? serverDomain!.provider, - ), - ); + if (ApiController.currentDnsProviderApiFactory == null) { + if (dnsProvider != null || + (serverDomain != null && + serverDomain.provider != DnsProvider.unknown)) { + ApiController.initDnsProviderApiFactory( + DnsProviderApiFactorySettings( + provider: dnsProvider ?? serverDomain!.provider, + ), + ); + } } if (box.get(BNames.hasFinalChecked, defaultValue: false)) { + StagingOptions.verifyCertificate = true; return ServerInstallationFinished( providerApiToken: providerApiToken!, serverTypeIdentificator: serverTypeIdentificator ?? '', From 4afcedebb70f84f8c7395f6277d5a6c3317c3954 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Tue, 16 May 2023 16:55:26 -0300 Subject: [PATCH 10/10] feat: Remove certificate check during installation --- .../rest_maps/dns_providers/desec/desec.dart | 62 ++++++++----------- .../server_providers/hetzner/hetzner.dart | 2 +- .../cubit/dns_records/dns_records_cubit.dart | 18 +++--- .../server_installation_cubit.dart | 3 + 4 files changed, 38 insertions(+), 47 deletions(-) diff --git a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart index aea1d0da..c1a8f43b 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart @@ -111,20 +111,22 @@ class DesecApi extends DnsProviderApi { final List bulkRecords = []; for (final DnsRecord record in listDnsRecords) { bulkRecords.add( - record.name == null - ? { - 'type': record.type, - 'ttl': record.ttl, - 'records': [], - } - : { - 'subname': record.name, - 'type': record.type, - 'ttl': record.ttl, - 'records': [], - }, + { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [], + }, ); } + bulkRecords.add( + { + 'subname': 'selector._domainkey', + 'type': 'TXT', + 'ttl': 18000, + 'records': [], + }, + ); await client.put(url, data: bulkRecords); await Future.delayed(const Duration(seconds: 1)); } catch (e) { @@ -192,18 +194,12 @@ class DesecApi extends DnsProviderApi { final List bulkRecords = []; for (final DnsRecord record in listDnsRecords) { bulkRecords.add( - record.name == null - ? { - 'type': record.type, - 'ttl': record.ttl, - 'records': [extractContent(record)], - } - : { - 'subname': record.name, - 'type': record.type, - 'ttl': record.ttl, - 'records': [extractContent(record)], - }, + { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [extractContent(record)], + }, ); } await client.post( @@ -295,18 +291,12 @@ class DesecApi extends DnsProviderApi { try { await client.post( url, - data: record.name == null - ? { - 'type': record.type, - 'ttl': record.ttl, - 'records': [extractContent(record)], - } - : { - 'subname': record.name, - 'type': record.type, - 'ttl': record.ttl, - 'records': [extractContent(record)], - }, + data: { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [extractContent(record)], + }, ); await Future.delayed(const Duration(seconds: 1)); } catch (e) { diff --git a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart index f9a2104e..c2228030 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart @@ -394,7 +394,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi { final String apiToken = StringGenerators.apiToken(); final String hostname = getHostnameFromDomain(domainName); - const String infectBranch = 'testing/desec'; + const String infectBranch = 'providers/hetzner'; final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false'; final String base64Password = base64.encode(utf8.encode(rootUser.password ?? 'PASS')); diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 2e57f0f7..472ed954 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -1,10 +1,7 @@ import 'package:cubit_form/cubit_form.dart'; -import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart'; -import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; -import 'package:selfprivacy/logic/get_it/api_config.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; @@ -28,13 +25,14 @@ class DnsRecordsCubit emit( DnsRecordsState( dnsState: DnsRecordsStatus.refreshing, - dnsRecords: ApiController.currentDnsProviderApiFactory! - .getDnsProvider() - .getDesiredDnsRecords( - serverInstallationCubit.state.serverDomain?.domainName, - '', - '', - ), + dnsRecords: ApiController.currentDnsProviderApiFactory + ?.getDnsProvider() + .getDesiredDnsRecords( + serverInstallationCubit.state.serverDomain?.domainName, + '', + '', + ) ?? + [], ), ); diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index fa0a04e1..47952c9a 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -10,6 +10,7 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_api_settings.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_api_settings.dart'; +import 'package:selfprivacy/logic/api_maps/staging_options.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; @@ -436,6 +437,7 @@ class ServerInstallationCubit extends Cubit { emit(TimerState(dataState: dataState, isLoading: true)); final bool isServerWorking = await repository.isHttpServerWorking(); + StagingOptions.verifyCertificate = true; if (isServerWorking) { bool dkimCreated = true; @@ -758,6 +760,7 @@ class ServerInstallationCubit extends Cubit { void clearAppConfig() { closeTimer(); ApiController.clearProviderApiFactories(); + StagingOptions.verifyCertificate = false; repository.clearAppConfig(); emit(const ServerInstallationEmpty()); }