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_api.dart similarity index 69% rename from lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart rename to lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart index 8298b08d..b2fc0e30 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart @@ -45,7 +45,6 @@ class DesecApi extends DnsProviderApi { @override String rootAddress = 'https://desec.io/api/v1/domains/'; - @override Future> isApiTokenValid(final String token) async { bool isValid = false; Response? response; @@ -93,40 +92,16 @@ class DesecApi extends DnsProviderApi { ); } - @override - Future getZoneId(final String domain) async => domain; - - @override - Future> removeSimilarRecords({ + Future> updateRecords({ required final ServerDomain domain, - final String? ip4, + required final List records, }) async { final String domainName = domain.domainName; final String url = '/$domainName/rrsets/'; - final List listDnsRecords = projectDnsRecords(domainName, ip4); final Dio client = await getClient(); try { - final List bulkRecords = []; - for (final DnsRecord record in listDnsRecords) { - bulkRecords.add( - { - '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 client.put(url, data: records); await Future.delayed(const Duration(seconds: 1)); } catch (e) { print(e); @@ -142,13 +117,12 @@ class DesecApi extends DnsProviderApi { return GenericResult(success: true, data: null); } - @override - Future> getDnsRecords({ + Future>> getDnsRecords({ required final ServerDomain domain, }) async { - Response response; + Response? response; final String domainName = domain.domainName; - final List allRecords = []; + List allRecords = []; final String url = '/$domainName/rrsets/'; @@ -156,59 +130,33 @@ class DesecApi extends DnsProviderApi { 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: content, - ttl: record['ttl'], - ), - ); - } + allRecords = response.data; } catch (e) { print(e); + return GenericResult( + data: allRecords, + success: false, + message: e.toString(), + code: response?.statusCode, + ); } finally { close(client); } - return allRecords; + return GenericResult(data: allRecords, success: true); } - @override - Future> createMultipleDnsRecords({ + Future> createRecords({ required final ServerDomain domain, - final String? ip4, + required final List records, }) async { final String domainName = domain.domainName; - final List listDnsRecords = projectDnsRecords(domainName, ip4); + final String url = '/$domainName/rrsets/'; final Dio client = await getClient(); try { - final List bulkRecords = []; - for (final DnsRecord record in listDnsRecords) { - bulkRecords.add( - { - 'subname': record.name, - 'type': record.type, - 'ttl': record.ttl, - 'records': [extractContent(record)], - }, - ); - } - await client.post( - '/$domainName/rrsets/', - data: bulkRecords, - ); + await client.post(url, data: records); await Future.delayed(const Duration(seconds: 1)); - } on DioError catch (e) { - print(e.message); - rethrow; } catch (e) { print(e); return GenericResult( @@ -223,53 +171,6 @@ class DesecApi extends DnsProviderApi { return GenericResult(success: true, data: null); } - List projectDnsRecords( - final String? domainName, - final String? ip4, - ) { - final DnsRecord domainA = DnsRecord(type: 'A', name: '', content: ip4); - - 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); - 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 - ]; - } - String? extractContent(final DnsRecord record) { String? content = record.content; if (record.type == 'TXT' && content != null && !content.startsWith('"')) { @@ -279,32 +180,6 @@ class DesecApi extends DnsProviderApi { return content; } - @override - Future setDnsRecord( - final DnsRecord record, - final ServerDomain domain, - ) async { - final String url = '/${domain.domainName}/rrsets/'; - - final Dio client = await getClient(); - try { - await client.post( - url, - data: { - 'subname': record.name, - 'type': record.type, - 'ttl': record.ttl, - 'records': [extractContent(record)], - }, - ); - await Future.delayed(const Duration(seconds: 1)); - } catch (e) { - print(e); - } finally { - close(client); - } - } - @override Future> domainList() async { List 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_api_factory.dart similarity index 96% rename from lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart rename to lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api_factory.dart index 6c10259b..11a9c37b 100644 --- a/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_factory.dart +++ b/lib/logic/api_maps/rest_maps/dns_providers/desec/desec_api_factory.dart @@ -1,4 +1,4 @@ -import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec_api.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'; diff --git a/lib/logic/providers/dns_providers/desec.dart b/lib/logic/providers/dns_providers/desec.dart index c7a5bab8..922a743c 100644 --- a/lib/logic/providers/dns_providers/desec.dart +++ b/lib/logic/providers/dns_providers/desec.dart @@ -1,3 +1,238 @@ +import 'package:selfprivacy/logic/api_maps/generic_result.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desec/desec_api.dart'; +import 'package:selfprivacy/logic/models/hive/server_domain.dart'; +import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/providers/dns_providers/dns_provider.dart'; -class DesecDnsProvider extends DnsProvider {} +class ApiAdapter { + ApiAdapter({final bool isWithToken = true}) + : _api = DesecApi( + isWithToken: isWithToken, + ); + + DesecApi api({final bool getInitialized = true}) => getInitialized + ? _api + : DesecApi( + isWithToken: false, + ); + + final DesecApi _api; +} + +class DesecDnsProvider extends DnsProvider { + DesecDnsProvider() : _adapter = ApiAdapter(); + DesecDnsProvider.load( + final bool isAuthotized, + ) : _adapter = ApiAdapter( + isWithToken: isAuthotized, + ); + + ApiAdapter _adapter; + + @override + Future> tryInitApiByToken(final String token) async { + final api = _adapter.api(getInitialized: false); + final result = await api.isApiTokenValid(token); + if (!result.data || !result.success) { + return result; + } + + _adapter = ApiAdapter(isWithToken: true); + return result; + } + + @override + Future> getZoneId(final String domain) async => + GenericResult( + data: domain, + success: true, + ); + + @override + Future> removeDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final List listDnsRecords = projectDnsRecords( + domain.domainName, + ip4, + ); + + final List bulkRecords = []; + for (final DnsRecord record in listDnsRecords) { + bulkRecords.add( + { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [], + }, + ); + } + bulkRecords.add( + { + 'subname': 'selector._domainkey', + 'type': 'TXT', + 'ttl': 18000, + 'records': [], + }, + ); + + return _adapter.api().updateRecords( + domain: domain, + records: bulkRecords, + ); + } + + @override + Future>> getDnsRecords({ + required final ServerDomain domain, + }) async { + final List records = []; + final result = await _adapter.api().getDnsRecords(domain: domain); + if (result.data.isEmpty || !result.success) { + return GenericResult( + success: result.success, + data: records, + code: result.code, + message: result.message, + ); + } + + try { + for (final record in result.data) { + final String? content = (record['records'] is List) + ? record['records'][0] + : record['records']; + records.add( + DnsRecord( + name: record['subname'], + type: record['type'], + content: content, + ttl: record['ttl'], + ), + ); + } + } catch (e) { + print(e); + return GenericResult( + success: false, + data: records, + message: e.toString(), + ); + } + + return GenericResult(success: true, data: records); + } + + List projectDnsRecords( + final String? domainName, + final String? ip4, + ) { + final DnsRecord domainA = DnsRecord(type: 'A', name: '', content: ip4); + + 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); + 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 + Future> createDomainRecords({ + required final ServerDomain domain, + final String? ip4, + }) async { + final List listDnsRecords = projectDnsRecords( + domain.domainName, + ip4, + ); + + final List bulkRecords = []; + for (final DnsRecord record in listDnsRecords) { + bulkRecords.add( + { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [extractContent(record)], + }, + ); + } + + return _adapter.api().createRecords( + domain: domain, + records: bulkRecords, + ); + } + + @override + Future> setDnsRecord( + final DnsRecord record, + final ServerDomain domain, + ) async { + final result = await _adapter.api().createRecords( + domain: domain, + records: [ + { + 'subname': record.name, + 'type': record.type, + 'ttl': record.ttl, + 'records': [extractContent(record)], + }, + ], + ); + + if (!result.success) { + return GenericResult( + success: result.success, + data: null, + code: result.code, + message: result.message, + ); + } + } + + String? extractContent(final DnsRecord record) { + String? content = record.content; + if (record.type == 'TXT' && content != null && !content.startsWith('"')) { + content = '"$content"'; + } + + return content; + } +}