From d45417effe5b26e4fd99d970c5ba7a83f54391b1 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Thu, 9 Nov 2023 18:48:49 +0400 Subject: [PATCH] feat: Implement model adapter for Digital Ocean DNS - Adapt all external interfaces to new DNS record models --- .../digital_ocean_dns_api.dart | 2 +- .../digital_ocean/digital_ocean_api.dart | 1 - .../cubit/dns_records/dns_records_cubit.dart | 22 +- .../initializing/domain_setup_cubit.dart | 14 +- .../digital_ocean_dns_adapter.dart | 49 ++++ .../digital_ocean_dns_info.dart | 20 ++ .../digital_ocean_dns_info.g.dart | 0 .../providers/dns_providers/cloudflare.dart | 30 ++- .../dns_providers/digital_ocean_dns.dart | 233 ++++-------------- 9 files changed, 162 insertions(+), 209 deletions(-) create mode 100644 lib/logic/models/json/dns_providers/digital_ocean_dns_adapter.dart rename lib/logic/models/json/{ => dns_providers}/digital_ocean_dns_info.dart (77%) rename lib/logic/models/json/{ => dns_providers}/digital_ocean_dns_info.g.dart (100%) diff --git a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart index e1b5c601..e4523012 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart @@ -4,7 +4,7 @@ import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; -import 'package:selfprivacy/logic/models/json/digital_ocean_dns_info.dart'; +import 'package:selfprivacy/logic/models/json/dns_providers/digital_ocean_dns_info.dart'; class DigitalOceanDnsApi extends RestApiMap { DigitalOceanDnsApi({ diff --git a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart index 9ffdb666..bd453dee 100644 --- a/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart +++ b/lib/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_api.dart @@ -5,7 +5,6 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/generic_result.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart'; import 'package:selfprivacy/logic/api_maps/tls_options.dart'; -import 'package:selfprivacy/logic/models/disk_size.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/json/digital_ocean_server_info.dart'; import 'package:selfprivacy/utils/password_generator.dart'; diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 6f295274..e8579cd5 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -23,15 +23,9 @@ class DnsRecordsCubit @override Future load() async { emit( - DnsRecordsState( + const DnsRecordsState( dnsState: DnsRecordsStatus.refreshing, - dnsRecords: - ProvidersController.currentDnsProvider?.getDesiredDnsRecords( - serverInstallationCubit.state.serverDomain?.domainName, - '', - '', - ) ?? - [], + dnsRecords: [], ), ); @@ -44,12 +38,12 @@ class DnsRecordsCubit return; } - final foundRecords = - await ProvidersController.currentDnsProvider!.validateDnsRecords( - domain!, - ipAddress!, - extractDkimRecord(await api.getDnsRecords())?.content ?? '', - ); + final foundRecords = await ProvidersController.currentDnsProvider! + .validateDnsRecords(domain!, ipAddress!, + extractDkimRecord(await api.getDnsRecords())?.content ?? '', [] + + /// TODO: TOOD!!11kdoikadodsksakdpoadsaspodda + ); if (!foundRecords.success || foundRecords.data.isEmpty) { emit(const DnsRecordsState()); diff --git a/lib/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart b/lib/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart index bccbc551..98324cb8 100644 --- a/lib/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart @@ -11,14 +11,22 @@ class DomainSetupCubit extends Cubit { Future load() async { emit(Loading(LoadingTypes.loadingDomain)); - final GenericResult> result = + final GenericResult> result = await ProvidersController.currentDnsProvider!.domainList(); if (!result.success || result.data.isEmpty) { emit(Empty()); } else if (result.data.length == 1) { - emit(Loaded(result.data.first)); + emit(Loaded(result.data.first.domainName)); } else { - emit(MoreThenOne(result.data)); + emit( + MoreThenOne( + result.data + .map( + (final d) => d.domainName, + ) + .toList(), + ), + ); } } diff --git a/lib/logic/models/json/dns_providers/digital_ocean_dns_adapter.dart b/lib/logic/models/json/dns_providers/digital_ocean_dns_adapter.dart new file mode 100644 index 00000000..061af3a5 --- /dev/null +++ b/lib/logic/models/json/dns_providers/digital_ocean_dns_adapter.dart @@ -0,0 +1,49 @@ +part of 'digital_ocean_dns_info.dart'; + +DigitalOceanDnsRecord _fromDnsRecord( + final DnsRecord dnsRecord, + final String rootDomain, +) { + String convert(final String entry) => (entry == rootDomain) ? '@' : entry; + String name = dnsRecord.name ?? ''; + String content = dnsRecord.content ?? ''; + name = convert(name); + content = convert(content); + return DigitalOceanDnsRecord( + name: name, + data: content, + ttl: dnsRecord.ttl, + type: dnsRecord.type, + priority: dnsRecord.priority, + id: null, + ); +} + +DnsRecord _toDnsRecord( + final DigitalOceanDnsRecord digitalOceanRecord, + final String rootDomain, +) { + final String type = digitalOceanRecord.type; + String convert(final String entry) => (entry == '@') ? rootDomain : entry; + String name = digitalOceanRecord.name; + final String content = convert(digitalOceanRecord.data); + if (type != 'MX') { + name = convert(name); + } + return DnsRecord( + name: name, + content: content, + ttl: digitalOceanRecord.ttl, + type: type, + priority: digitalOceanRecord.priority ?? 10, + ); +} + +ServerDomain _toServerDomain(final DigitalOceanDomain digitalOceanDomain) => + ServerDomain( + domainName: digitalOceanDomain.name, + provider: DnsProviderType.digitalOcean, + ); + +DigitalOceanDomain _fromServerDomain(final ServerDomain serverDomain) => + DigitalOceanDomain(name: serverDomain.domainName); diff --git a/lib/logic/models/json/digital_ocean_dns_info.dart b/lib/logic/models/json/dns_providers/digital_ocean_dns_info.dart similarity index 77% rename from lib/logic/models/json/digital_ocean_dns_info.dart rename to lib/logic/models/json/dns_providers/digital_ocean_dns_info.dart index 0881b214..be23f711 100644 --- a/lib/logic/models/json/digital_ocean_dns_info.dart +++ b/lib/logic/models/json/dns_providers/digital_ocean_dns_info.dart @@ -1,6 +1,9 @@ import 'package:json_annotation/json_annotation.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; part 'digital_ocean_dns_info.g.dart'; +part 'digital_ocean_dns_adapter.dart'; /// https://docs.digitalocean.com/reference/api/api-reference/#tag/Domains @JsonSerializable() @@ -10,6 +13,11 @@ class DigitalOceanDomain { this.ttl, }); + factory DigitalOceanDomain.fromServerDomain( + final ServerDomain serverDomain, + ) => + _fromServerDomain(serverDomain); + /// The name of the domain itself. /// This should follow the standard domain format of domain.TLD. /// @@ -23,6 +31,7 @@ class DigitalOceanDomain { static DigitalOceanDomain fromJson(final Map json) => _$DigitalOceanDomainFromJson(json); + ServerDomain toServerDomain() => _toServerDomain(this); } /// https://docs.digitalocean.com/reference/api/api-reference/#tag/Domain-Records @@ -37,6 +46,15 @@ class DigitalOceanDnsRecord { this.priority, }); + factory DigitalOceanDnsRecord.fromDnsRecord( + final DnsRecord dnsRecord, + final String rootDomain, + ) => + _fromDnsRecord( + dnsRecord, + rootDomain, + ); + /// A unique identifier for each domain record. final int? id; @@ -63,4 +81,6 @@ class DigitalOceanDnsRecord { static DigitalOceanDnsRecord fromJson(final Map json) => _$DigitalOceanDnsRecordFromJson(json); Map toJson() => _$DigitalOceanDnsRecordToJson(this); + DnsRecord toDnsRecord(final String rootDomain) => + _toDnsRecord(this, rootDomain); } diff --git a/lib/logic/models/json/digital_ocean_dns_info.g.dart b/lib/logic/models/json/dns_providers/digital_ocean_dns_info.g.dart similarity index 100% rename from lib/logic/models/json/digital_ocean_dns_info.g.dart rename to lib/logic/models/json/dns_providers/digital_ocean_dns_info.g.dart diff --git a/lib/logic/providers/dns_providers/cloudflare.dart b/lib/logic/providers/dns_providers/cloudflare.dart index b34deca9..06691cb0 100644 --- a/lib/logic/providers/dns_providers/cloudflare.dart +++ b/lib/logic/providers/dns_providers/cloudflare.dart @@ -184,8 +184,26 @@ class CloudflareDnsProvider extends DnsProvider { final String dkimPublicKey, final List pendingDnsRecords, ) async { - final GenericResult> records = - await getDnsRecords(domain: domain); + final syncZoneIdResult = await syncZoneId(domain.domainName); + if (!syncZoneIdResult.success) { + return GenericResult( + success: syncZoneIdResult.success, + data: [], + code: syncZoneIdResult.code, + message: syncZoneIdResult.message, + ); + } + final result = + await _adapter.api().getDnsRecords(zoneId: _adapter.cachedZoneId); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: [], + code: result.code, + message: result.message, + ); + } + final records = result.data; final List foundRecords = []; try { for (final DnsRecord pendingDnsRecord in pendingDnsRecords) { @@ -194,14 +212,14 @@ class CloudflareDnsProvider extends DnsProvider { domain.domainName, ); if (record.name == 'selector._domainkey') { - final DnsRecord foundRecord = records.data.firstWhere( + final CloudflareDnsRecord foundRecord = records.firstWhere( (final r) => (r.name == record.name) && r.type == record.type, - orElse: () => DnsRecord( + orElse: () => CloudflareDnsRecord( + zoneName: domain.domainName, name: record.name, type: record.type, content: '', ttl: 800, - proxied: false, ), ); // remove all spaces and tabulators from @@ -219,7 +237,7 @@ class CloudflareDnsProvider extends DnsProvider { ), ); } else { - final foundMatch = records.data.any( + final foundMatch = records.any( (final r) => (r.name == record.name) && r.type == record.type && diff --git a/lib/logic/providers/dns_providers/digital_ocean_dns.dart b/lib/logic/providers/dns_providers/digital_ocean_dns.dart index 4fac4b65..68757203 100644 --- a/lib/logic/providers/dns_providers/digital_ocean_dns.dart +++ b/lib/logic/providers/dns_providers/digital_ocean_dns.dart @@ -1,9 +1,10 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/digital_ocean_dns/digital_ocean_dns_api.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; -import 'package:selfprivacy/logic/models/json/digital_ocean_dns_info.dart'; +import 'package:selfprivacy/logic/models/json/dns_providers/digital_ocean_dns_info.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; +import 'package:selfprivacy/utils/network_utils.dart'; class ApiAdapter { ApiAdapter({final bool isWithToken = true}) @@ -46,8 +47,8 @@ class DigitalOceanDnsProvider extends DnsProvider { } @override - Future>> domainList() async { - List domains = []; + Future>> domainList() async { + List domains = []; final result = await _adapter.api().getDomains(); if (result.data.isEmpty || !result.success) { return GenericResult( @@ -59,8 +60,8 @@ class DigitalOceanDnsProvider extends DnsProvider { } domains = result.data - .map( - (final el) => el.name, + .map( + (final el) => el.toServerDomain(), ) .toList(); @@ -82,14 +83,8 @@ class DigitalOceanDnsProvider extends DnsProvider { ip4, ) .map( - (final e) => DigitalOceanDnsRecord( - name: e.name ?? '', - id: null, - data: e.content ?? '', - ttl: e.ttl, - type: e.type, - priority: e.priority, - ), + (final e) => + DigitalOceanDnsRecord.fromDnsRecord(e, domain.domainName), ) .toList(), ); @@ -139,15 +134,7 @@ class DigitalOceanDnsProvider extends DnsProvider { } for (final rawRecord in result.data) { - records.add( - DnsRecord( - name: rawRecord.name, - type: rawRecord.type, - content: rawRecord.data, - ttl: rawRecord.ttl, - proxied: false, - ), - ); + records.add(rawRecord.toDnsRecord(domain.domainName)); } return GenericResult(data: records, success: true); @@ -177,47 +164,61 @@ class DigitalOceanDnsProvider extends DnsProvider { final ServerDomain domain, final String ip4, final String dkimPublicKey, + final List pendingDnsRecords, ) async { - final GenericResult> records = - await getDnsRecords(domain: domain); + final result = await _adapter.api().getDnsRecords(domain.domainName); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: [], + code: result.code, + message: result.message, + ); + } + final records = result.data; 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.data.firstWhere( + for (final DnsRecord pendingDnsRecord in pendingDnsRecords) { + final record = DigitalOceanDnsRecord.fromDnsRecord( + pendingDnsRecord, domain.domainName); + if (record.name == 'selector._domainkey') { + final DigitalOceanDnsRecord foundRecord = records.firstWhere( (final r) => (r.name == record.name) && r.type == record.type, - orElse: () => DnsRecord( + orElse: () => DigitalOceanDnsRecord( + id: null, name: record.name, type: record.type, - content: '', + data: '', 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)); - } + final String foundContent = + foundRecord.data.replaceAll(RegExp(r'\s+'), ''); + final String content = record.data.replaceAll(RegExp(r'\s+'), ''); + foundRecords.add( + DesiredDnsRecord( + name: record.name, + content: record.data, + isSatisfied: foundContent == content, + ), + ); } else { - if (records.data.any( + final foundMatch = records.any( (final r) => - (r.name == record.name) && + 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)); - } + r.data == record.data, + ); + foundRecords.add( + DesiredDnsRecord( + name: record.name, + content: record.data, + isSatisfied: foundMatch, + ), + ); } } } catch (e) { @@ -233,140 +234,4 @@ class DigitalOceanDnsProvider extends DnsProvider { success: true, ); } - - List getProjectDnsRecords( - final String? domainName, - final String? ip4, - ) { - final DnsRecord domainA = DnsRecord(type: 'A', name: '@', content: ip4); - - final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: '@'); - 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: '@', - content: 'v=spf1 a mx ip4:$ip4 -all', - ttl: 18000, - ); - - return [ - domainA, - apiA, - cloudA, - gitA, - meetA, - passwordA, - socialA, - mx, - txt1, - txt2, - vpn - ]; - } - - @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', - displayName: domainName, - ), - DesiredDnsRecord( - name: 'api', - content: ip4, - description: 'record.api', - displayName: 'api.$domainName', - ), - DesiredDnsRecord( - name: 'cloud', - content: ip4, - description: 'record.cloud', - displayName: 'cloud.$domainName', - ), - DesiredDnsRecord( - name: 'git', - content: ip4, - description: 'record.git', - displayName: 'git.$domainName', - ), - DesiredDnsRecord( - name: 'meet', - content: ip4, - description: 'record.meet', - displayName: 'meet.$domainName', - ), - DesiredDnsRecord( - name: 'social', - content: ip4, - description: 'record.social', - displayName: 'social.$domainName', - ), - DesiredDnsRecord( - name: 'password', - content: ip4, - description: 'record.password', - displayName: 'password.$domainName', - ), - DesiredDnsRecord( - name: 'vpn', - content: ip4, - description: 'record.vpn', - displayName: 'vpn.$domainName', - ), - const DesiredDnsRecord( - name: '@', - content: '@', - description: 'record.mx', - type: 'MX', - category: DnsRecordsCategory.email, - ), - const DesiredDnsRecord( - name: '_dmarc', - 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', - content: dkimPublicKey, - description: 'record.dkim', - type: 'TXT', - category: DnsRecordsCategory.email, - ), - ]; - } }