Implement Dns Provider Api Abstractions

pull/101/head
NaiJi ✨ 2022-07-14 16:34:08 +03:00
parent 8deb240426
commit dac310f913
24 changed files with 305 additions and 170 deletions

View File

@ -1,23 +1,37 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_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/hetzner/hetzner_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
class UnknownApiProviderException implements Exception {
UnknownApiProviderException(this.message);
final String message;
}
class ApiFactoryCreator { class ApiFactoryCreator {
static ProviderApiFactory createProviderApiFactory( static ServerProviderApiFactory createServerProviderApiFactory(
final ServerProvider provider, final ServerProvider provider,
) { ) {
switch (provider) { switch (provider) {
case ServerProvider.hetzner: case ServerProvider.hetzner:
case ServerProvider.unknown: // ?? :)
return HetznerApiFactory(); return HetznerApiFactory();
case ServerProvider.unknown:
throw UnknownApiProviderException('Unknown server provider');
} }
} }
// createDnsApiFactory static DnsProviderApiFactory createDnsProviderApiFactory(
final DnsProvider provider,
// createStorageApiFactory ) {
switch (provider) {
// etc . . . case DnsProvider.cloudflare:
return CloudflareApiFactory();
case DnsProvider.unknown:
throw UnknownApiProviderException('Unknown DNS provider');
}
}
} }
class VolumeApiFactoryCreator { class VolumeApiFactoryCreator {
@ -26,14 +40,9 @@ class VolumeApiFactoryCreator {
) { ) {
switch (provider) { switch (provider) {
case ServerProvider.hetzner: case ServerProvider.hetzner:
case ServerProvider.unknown: // ?? :)
return HetznerApiFactory(); return HetznerApiFactory();
case ServerProvider.unknown:
throw UnknownApiProviderException('Unknown volume provider');
} }
} }
// createDnsApiFactory
// createStorageApiFactory
// etc . . .
} }

View File

@ -2,16 +2,11 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.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/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart';
class DomainNotFoundException implements Exception { class CloudflareApi extends DnsProviderApi {
DomainNotFoundException(this.message);
final String message;
}
class CloudflareApi extends ApiMap {
CloudflareApi({ CloudflareApi({
this.hasLogger = false, this.hasLogger = false,
this.isWithToken = true, this.isWithToken = true,
@ -24,6 +19,10 @@ class CloudflareApi extends ApiMap {
final String? customToken; final String? customToken;
@override
RegExp getApiTokenValidation() =>
RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
@override @override
BaseOptions get options { BaseOptions get options {
final BaseOptions options = BaseOptions(baseUrl: rootAddress); final BaseOptions options = BaseOptions(baseUrl: rootAddress);
@ -46,27 +45,37 @@ class CloudflareApi extends ApiMap {
@override @override
String rootAddress = 'https://api.cloudflare.com/client/v4'; String rootAddress = 'https://api.cloudflare.com/client/v4';
Future<bool> isValid(final String token) async { @override
validateStatus = (final status) => Future<bool> isApiTokenValid(final String token) async {
status == HttpStatus.ok || status == HttpStatus.unauthorized; bool isValid = false;
Response? response;
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get( try {
'/user/tokens/verify', response = await client.get(
options: Options(headers: {'Authorization': 'Bearer $token'}), '/user/tokens/verify',
); options: Options(headers: {'Authorization': 'Bearer $token'}),
);
close(client); } catch (e) {
print(e);
if (response.statusCode == HttpStatus.ok) { isValid = false;
return true; } finally {
} else if (response.statusCode == HttpStatus.unauthorized) { close(client);
return false;
} else {
throw Exception('code: ${response.statusCode}');
} }
if (response != null) {
if (response.statusCode == HttpStatus.ok) {
isValid = true;
} else if (response.statusCode == HttpStatus.unauthorized) {
isValid = false;
} else {
throw Exception('code: ${response.statusCode}');
}
}
return isValid;
} }
@override
Future<String> getZoneId(final String domain) async { Future<String> getZoneId(final String domain) async {
validateStatus = (final status) => validateStatus = (final status) =>
status == HttpStatus.ok || status == HttpStatus.forbidden; status == HttpStatus.ok || status == HttpStatus.forbidden;
@ -85,12 +94,13 @@ class CloudflareApi extends ApiMap {
} }
} }
@override
Future<void> removeSimilarRecords({ Future<void> removeSimilarRecords({
required final ServerDomain cloudFlareDomain, required final ServerDomain domain,
final String? ip4, final String? ip4,
}) async { }) async {
final String domainName = cloudFlareDomain.domainName; final String domainName = domain.domainName;
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final String url = '/zones/$domainZoneId/dns_records'; final String url = '/zones/$domainZoneId/dns_records';
@ -112,11 +122,12 @@ class CloudflareApi extends ApiMap {
close(client); close(client);
} }
@override
Future<List<DnsRecord>> getDnsRecords({ Future<List<DnsRecord>> getDnsRecords({
required final ServerDomain cloudFlareDomain, required final ServerDomain domain,
}) async { }) async {
final String domainName = cloudFlareDomain.domainName; final String domainName = domain.domainName;
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final String url = '/zones/$domainZoneId/dns_records'; final String url = '/zones/$domainZoneId/dns_records';
@ -144,12 +155,13 @@ class CloudflareApi extends ApiMap {
return allRecords; return allRecords;
} }
@override
Future<void> createMultipleDnsRecords({ Future<void> createMultipleDnsRecords({
required final ServerDomain cloudFlareDomain, required final ServerDomain domain,
final String? ip4, final String? ip4,
}) async { }) async {
final String domainName = cloudFlareDomain.domainName; final String domainName = domain.domainName;
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final List<DnsRecord> listDnsRecords = projectDnsRecords(domainName, ip4); final List<DnsRecord> listDnsRecords = projectDnsRecords(domainName, ip4);
final List<Future> allCreateFutures = <Future>[]; final List<Future> allCreateFutures = <Future>[];
@ -219,11 +231,12 @@ class CloudflareApi extends ApiMap {
]; ];
} }
@override
Future<void> setDkim( Future<void> setDkim(
final String dkimRecordString, final String dkimRecordString,
final ServerDomain cloudFlareDomain, final ServerDomain domain,
) async { ) async {
final String domainZoneId = cloudFlareDomain.zoneId; final String domainZoneId = domain.zoneId;
final String url = '$rootAddress/zones/$domainZoneId/dns_records'; final String url = '$rootAddress/zones/$domainZoneId/dns_records';
final DnsRecord dkimRecord = DnsRecord( final DnsRecord dkimRecord = DnsRecord(
@ -242,6 +255,7 @@ class CloudflareApi extends ApiMap {
client.close(); client.close();
} }
@override
Future<List<String>> domainList() async { Future<List<String>> domainList() async {
final String url = '$rootAddress/zones'; final String url = '$rootAddress/zones';
final Dio client = await getClient(); final Dio client = await getClient();

View File

@ -0,0 +1,15 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
class CloudflareApiFactory extends DnsProviderApiFactory {
@override
DnsProviderApi getDnsProvider({
final DnsProviderApiSettings settings = const DnsProviderApiSettings(),
}) =>
CloudflareApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
customToken: settings.customToken,
);
}

View File

@ -0,0 +1,31 @@
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';
class DomainNotFoundException implements Exception {
DomainNotFoundException(this.message);
final String message;
}
abstract class DnsProviderApi extends ApiMap {
Future<List<DnsRecord>> getDnsRecords({
required final ServerDomain domain,
});
Future<void> removeSimilarRecords({
required final ServerDomain domain,
final String? ip4,
});
Future<void> createMultipleDnsRecords({
required final ServerDomain domain,
final String? ip4,
});
Future<void> setDkim(
final String dkimRecordString,
final ServerDomain domain,
);
Future<String> getZoneId(final String domain);
Future<List<String>> domainList();
Future<bool> isApiTokenValid(final String token);
RegExp getApiTokenValidation();
}

View File

@ -0,0 +1,17 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
class DnsProviderApiSettings extends ProviderApiSettings {
const DnsProviderApiSettings({
final super.hasLogger = false,
final super.isWithToken = true,
final this.customToken,
});
final String? customToken;
}
abstract class DnsProviderApiFactory {
DnsProviderApi getDnsProvider({
final DnsProviderApiSettings settings = const DnsProviderApiSettings(),
});
}

View File

@ -0,0 +1,5 @@
class ProviderApiSettings {
const ProviderApiSettings({this.hasLogger = false, this.isWithToken = true});
final bool hasLogger;
final bool isWithToken;
}

View File

@ -1,25 +0,0 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/volume_provider.dart';
class HetznerApiFactory extends ProviderApiFactory
with VolumeProviderApiFactory {
@override
ServerProviderApi getProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
@override
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
}

View File

@ -1,20 +0,0 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/volume_provider.dart';
class ProviderApiSettings {
const ProviderApiSettings({this.hasLogger = false, this.isWithToken = true});
final bool hasLogger;
final bool isWithToken;
}
abstract class ProviderApiFactory {
ServerProviderApi getProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}
mixin VolumeProviderApiFactory {
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}

View File

@ -3,8 +3,8 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/volume_provider.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';

View File

@ -0,0 +1,26 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
class HetznerApiFactory extends ServerProviderApiFactory
with VolumeProviderApiFactory {
@override
ServerProviderApi getServerProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
@override
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
}

View File

@ -0,0 +1,15 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/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/volume_provider.dart';
abstract class ServerProviderApiFactory {
ServerProviderApi getServerProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}
mixin VolumeProviderApiFactory {
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}

View File

@ -1,9 +1,11 @@
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/dns_records.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
part 'dns_records_state.dart'; part 'dns_records_state.dart';
@ -16,8 +18,12 @@ class DnsRecordsCubit
const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing), const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing),
); );
DnsProviderApiFactory? dnsProviderApiFactory =
ApiFactoryCreator.createDnsProviderApiFactory(
DnsProvider.cloudflare, // TODO: HARDCODE FOR NOW!!!
); // TODO: Remove when provider selection is implemented.
final ServerApi api = ServerApi(); final ServerApi api = ServerApi();
final CloudflareApi cloudflare = CloudflareApi();
@override @override
Future<void> load() async { Future<void> load() async {
@ -31,14 +37,15 @@ class DnsRecordsCubit
), ),
), ),
); );
print('Loading DNS status');
if (serverInstallationCubit.state is ServerInstallationFinished) { if (serverInstallationCubit.state is ServerInstallationFinished) {
final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
final String? ipAddress = final String? ipAddress =
serverInstallationCubit.state.serverDetails?.ip4; serverInstallationCubit.state.serverDetails?.ip4;
if (domain != null && ipAddress != null) { if (domain != null && ipAddress != null) {
final List<DnsRecord> records = final List<DnsRecord> records = await dnsProviderApiFactory!
await cloudflare.getDnsRecords(cloudFlareDomain: domain); .getDnsProvider()
.getDnsRecords(domain: domain);
final String? dkimPublicKey = await api.getDkim(); final String? dkimPublicKey = await api.getDkim();
final List<DesiredDnsRecord> desiredRecords = final List<DesiredDnsRecord> desiredRecords =
_getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey); _getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey);
@ -116,12 +123,14 @@ class DnsRecordsCubit
final ServerDomain? domain = serverInstallationCubit.state.serverDomain; final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4; final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4;
final String? dkimPublicKey = await api.getDkim(); final String? dkimPublicKey = await api.getDkim();
await cloudflare.removeSimilarRecords(cloudFlareDomain: domain!); final DnsProviderApi dnsProviderApi =
await cloudflare.createMultipleDnsRecords( dnsProviderApiFactory!.getDnsProvider();
cloudFlareDomain: domain, await dnsProviderApi.removeSimilarRecords(domain: domain!);
await dnsProviderApi.createMultipleDnsRecords(
domain: domain,
ip4: ipAddress, ip4: ipAddress,
); );
await cloudflare.setDkim(dkimPublicKey ?? '', domain); await dnsProviderApi.setDkim(dkimPublicKey ?? '', domain);
await load(); await load();
} }

View File

@ -2,13 +2,12 @@ import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
class CloudFlareFormCubit extends FormCubit { class DnsProviderFormCubit extends FormCubit {
CloudFlareFormCubit(this.initializingCubit) { DnsProviderFormCubit(this.initializingCubit) {
final RegExp regExp = RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); final RegExp regExp = initializingCubit.getDnsProviderApiTokenValidation();
apiKey = FieldCubit( apiKey = FieldCubit(
initalValue: '', initalValue: '',
validations: [ validations: [
@ -30,16 +29,15 @@ class CloudFlareFormCubit extends FormCubit {
} }
final ServerInstallationCubit initializingCubit; final ServerInstallationCubit initializingCubit;
late final FieldCubit<String> apiKey; late final FieldCubit<String> apiKey;
@override @override
FutureOr<bool> asyncValidation() async { FutureOr<bool> asyncValidation() async {
late bool isKeyValid; late bool isKeyValid;
final CloudflareApi apiClient = CloudflareApi(isWithToken: false);
try { try {
isKeyValid = await apiClient.isValid(apiKey.state.value); isKeyValid = await initializingCubit
.isDnsProviderApiTokenValid(apiKey.state.value);
} catch (e) { } catch (e) {
addError(e); addError(e);
isKeyValid = false; isKeyValid = false;

View File

@ -1,5 +1,4 @@
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
@ -10,9 +9,10 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
Future<void> load() async { Future<void> load() async {
emit(Loading(LoadingTypes.loadingDomain)); emit(Loading(LoadingTypes.loadingDomain));
final CloudflareApi api = CloudflareApi(); final List<String> list = await serverInstallationCubit
.repository.dnsProviderApiFactory!
final List<String> list = await api.domainList(); .getDnsProvider()
.domainList();
if (list.isEmpty) { if (list.isEmpty) {
emit(Empty()); emit(Empty());
} else if (list.length == 1) { } else if (list.length == 1) {
@ -28,11 +28,13 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
Future<void> saveDomain() async { Future<void> saveDomain() async {
assert(state is Loaded, 'wrong state'); assert(state is Loaded, 'wrong state');
final String domainName = (state as Loaded).domain; final String domainName = (state as Loaded).domain;
final CloudflareApi api = CloudflareApi();
emit(Loading(LoadingTypes.saving)); emit(Loading(LoadingTypes.saving));
final String zoneId = await api.getZoneId(domainName); final String zoneId = await serverInstallationCubit
.repository.dnsProviderApiFactory!
.getDnsProvider()
.getZoneId(domainName);
final ServerDomain domain = ServerDomain( final ServerDomain domain = ServerDomain(
domainName: domainName, domainName: domainName,

View File

@ -8,7 +8,7 @@ import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
class ProviderFormCubit extends FormCubit { class ProviderFormCubit extends FormCubit {
ProviderFormCubit(this.serverInstallationCubit) { ProviderFormCubit(this.serverInstallationCubit) {
final RegExp regExp = final RegExp regExp =
serverInstallationCubit.getProviderApiTokenValidation(); serverInstallationCubit.getServerProviderApiTokenValidation();
apiKey = FieldCubit( apiKey = FieldCubit(
initalValue: '', initalValue: '',
validations: [ validations: [
@ -38,7 +38,7 @@ class ProviderFormCubit extends FormCubit {
try { try {
isKeyValid = await serverInstallationCubit isKeyValid = await serverInstallationCubit
.isProviderApiTokenValid(apiKey.state.value); .isServerProviderApiTokenValid(apiKey.state.value);
} catch (e) { } catch (e) {
addError(e); addError(e);
isKeyValid = false; isKeyValid = false;

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/models/hetzner_metrics.dart'; import 'package:selfprivacy/logic/models/hetzner_metrics.dart';

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';

View File

@ -4,7 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.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_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
@ -50,13 +51,31 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
} }
} }
RegExp getProviderApiTokenValidation() => repository.serverProviderApiFactory! RegExp getServerProviderApiTokenValidation() =>
.getProvider() repository.serverProviderApiFactory!
.getServerProvider()
.getApiTokenValidation();
RegExp getDnsProviderApiTokenValidation() => repository.dnsProviderApiFactory!
.getDnsProvider()
.getApiTokenValidation(); .getApiTokenValidation();
Future<bool> isProviderApiTokenValid(final String providerToken) async => Future<bool> isServerProviderApiTokenValid(
final String providerToken,
) async =>
repository.serverProviderApiFactory! repository.serverProviderApiFactory!
.getProvider(settings: const ProviderApiSettings(isWithToken: false)) .getServerProvider(
settings: const ProviderApiSettings(isWithToken: false),
)
.isApiTokenValid(providerToken);
Future<bool> isDnsProviderApiTokenValid(
final String providerToken,
) async =>
repository.dnsProviderApiFactory!
.getDnsProvider(
settings: const DnsProviderApiSettings(isWithToken: false),
)
.isApiTokenValid(providerToken); .isApiTokenValid(providerToken);
void setHetznerKey(final String hetznerKey) async { void setHetznerKey(final String hetznerKey) async {

View File

@ -10,9 +10,10 @@ import 'package:pub_semver/pub_semver.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
@ -40,10 +41,14 @@ class ServerAuthorizationException implements Exception {
class ServerInstallationRepository { class ServerInstallationRepository {
Box box = Hive.box(BNames.serverInstallationBox); Box box = Hive.box(BNames.serverInstallationBox);
Box<User> usersBox = Hive.box(BNames.usersBox); Box<User> usersBox = Hive.box(BNames.usersBox);
ProviderApiFactory? serverProviderApiFactory = ServerProviderApiFactory? serverProviderApiFactory =
ApiFactoryCreator.createProviderApiFactory( ApiFactoryCreator.createServerProviderApiFactory(
ServerProvider.hetzner, // HARDCODE FOR NOW!!! ServerProvider.hetzner, // TODO: HARDCODE FOR NOW!!!
); // Remove when provider selection is implemented. ); // TODO: Remove when provider selection is implemented.
DnsProviderApiFactory? dnsProviderApiFactory =
ApiFactoryCreator.createDnsProviderApiFactory(
DnsProvider.cloudflare, // TODO: HARDCODE FOR NOW!!!
);
Future<ServerInstallationState> load() async { Future<ServerInstallationState> load() async {
final String? providerApiToken = getIt<ApiConfigModel>().hetznerKey; final String? providerApiToken = getIt<ApiConfigModel>().hetznerKey;
@ -54,9 +59,18 @@ class ServerInstallationRepository {
final ServerHostingDetails? serverDetails = final ServerHostingDetails? serverDetails =
getIt<ApiConfigModel>().serverDetails; getIt<ApiConfigModel>().serverDetails;
if (serverDetails != null) { if (serverDetails != null &&
serverDetails.provider != ServerProvider.unknown) {
serverProviderApiFactory = serverProviderApiFactory =
ApiFactoryCreator.createProviderApiFactory(serverDetails.provider); ApiFactoryCreator.createServerProviderApiFactory(
serverDetails.provider,
);
}
if (serverDomain != null && serverDomain.provider != DnsProvider.unknown) {
dnsProviderApiFactory = ApiFactoryCreator.createDnsProviderApiFactory(
serverDomain.provider,
);
} }
if (box.get(BNames.hasFinalChecked, defaultValue: false)) { if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
@ -142,20 +156,22 @@ class ServerInstallationRepository {
) async { ) async {
ServerHostingDetails? serverDetails; ServerHostingDetails? serverDetails;
final ServerProviderApi api = serverProviderApiFactory!.getProvider(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
serverDetails = await api.powerOn(); serverDetails = await api.powerOn();
return serverDetails; return serverDetails;
} }
Future<String?> getDomainId(final String token, final String domain) async { Future<String?> getDomainId(final String token, final String domain) async {
final CloudflareApi cloudflareApi = CloudflareApi( final DnsProviderApi dnsProviderApi = dnsProviderApiFactory!.getDnsProvider(
isWithToken: false, settings: DnsProviderApiSettings(
customToken: token, isWithToken: false,
customToken: token,
),
); );
try { try {
final String domainId = await cloudflareApi.getZoneId(domain); final String domainId = await dnsProviderApi.getZoneId(domain);
return domainId; return domainId;
} on DomainNotFoundException { } on DomainNotFoundException {
return null; return null;
@ -220,7 +236,7 @@ class ServerInstallationRepository {
required final Future<void> Function(ServerHostingDetails serverDetails) required final Future<void> Function(ServerHostingDetails serverDetails)
onSuccess, onSuccess,
}) async { }) async {
final ServerProviderApi api = serverProviderApiFactory!.getProvider(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
try { try {
final ServerHostingDetails? serverDetails = await api.createServer( final ServerHostingDetails? serverDetails = await api.createServer(
dnsApiToken: cloudFlareKey, dnsApiToken: cloudFlareKey,
@ -285,18 +301,20 @@ class ServerInstallationRepository {
final ServerDomain domain, { final ServerDomain domain, {
required final void Function() onCancel, required final void Function() onCancel,
}) async { }) async {
final CloudflareApi cloudflareApi = CloudflareApi(); final DnsProviderApi dnsProviderApi =
final ServerProviderApi serverApi = serverProviderApiFactory!.getProvider(); dnsProviderApiFactory!.getDnsProvider();
final ServerProviderApi serverApi =
serverProviderApiFactory!.getServerProvider();
await cloudflareApi.removeSimilarRecords( await dnsProviderApi.removeSimilarRecords(
ip4: serverDetails.ip4, ip4: serverDetails.ip4,
cloudFlareDomain: domain, domain: domain,
); );
try { try {
await cloudflareApi.createMultipleDnsRecords( await dnsProviderApi.createMultipleDnsRecords(
ip4: serverDetails.ip4, ip4: serverDetails.ip4,
cloudFlareDomain: domain, domain: domain,
); );
} on DioError catch (e) { } on DioError catch (e) {
final NavigationService nav = getIt.get<NavigationService>(); final NavigationService nav = getIt.get<NavigationService>();
@ -337,12 +355,13 @@ class ServerInstallationRepository {
} }
Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async { Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async {
final CloudflareApi cloudflareApi = CloudflareApi(); final DnsProviderApi dnsProviderApi =
dnsProviderApiFactory!.getDnsProvider();
final ServerApi api = ServerApi(); final ServerApi api = ServerApi();
final String? dkimRecordString = await api.getDkim(); final String? dkimRecordString = await api.getDkim();
await cloudflareApi.setDkim(dkimRecordString ?? '', cloudFlareDomain); await dnsProviderApi.setDkim(dkimRecordString ?? '', cloudFlareDomain);
} }
Future<bool> isHttpServerWorking() async { Future<bool> isHttpServerWorking() async {
@ -357,12 +376,12 @@ class ServerInstallationRepository {
} }
Future<ServerHostingDetails> restart() async { Future<ServerHostingDetails> restart() async {
final ServerProviderApi api = serverProviderApiFactory!.getProvider(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
return api.restart(); return api.restart();
} }
Future<ServerHostingDetails> powerOn() async { Future<ServerHostingDetails> powerOn() async {
final ServerProviderApi api = serverProviderApiFactory!.getProvider(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
return api.powerOn(); return api.powerOn();
} }
@ -599,7 +618,7 @@ class ServerInstallationRepository {
} }
Future<List<ServerBasicInfo>> getServersOnProviderAccount() async { Future<List<ServerBasicInfo>> getServersOnProviderAccount() async {
final ServerProviderApi api = serverProviderApiFactory!.getProvider(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
return api.getServers(); return api.getServers();
} }
@ -678,8 +697,9 @@ class ServerInstallationRepository {
} }
Future<void> deleteServer(final ServerDomain serverDomain) async { Future<void> deleteServer(final ServerDomain serverDomain) async {
final ServerProviderApi api = serverProviderApiFactory!.getProvider(); final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
final CloudflareApi cloudFlare = CloudflareApi(); final DnsProviderApi dnsProviderApi =
dnsProviderApiFactory!.getDnsProvider();
await api.deleteServer( await api.deleteServer(
domainName: serverDomain.domainName, domainName: serverDomain.domainName,
@ -692,7 +712,7 @@ class ServerInstallationRepository {
await box.put(BNames.isLoading, false); await box.put(BNames.isLoading, false);
await box.put(BNames.serverDetails, null); await box.put(BNames.serverDetails, null);
await cloudFlare.removeSimilarRecords(cloudFlareDomain: serverDomain); await dnsProviderApi.removeSimilarRecords(domain: serverDomain);
} }
Future<void> deleteServerRelatedRecords() async { Future<void> deleteServerRelatedRecords() async {

View File

@ -1,6 +1,6 @@
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_factory.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';

View File

@ -5,8 +5,8 @@ import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_cloudflare.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
@ -195,10 +195,10 @@ class InitializingPage extends StatelessWidget {
Widget _stepCloudflare(final ServerInstallationCubit initializingCubit) => Widget _stepCloudflare(final ServerInstallationCubit initializingCubit) =>
BlocProvider( BlocProvider(
create: (final context) => CloudFlareFormCubit(initializingCubit), create: (final context) => DnsProviderFormCubit(initializingCubit),
child: Builder( child: Builder(
builder: (final context) { builder: (final context) {
final formCubitState = context.watch<CloudFlareFormCubit>().state; final formCubitState = context.watch<DnsProviderFormCubit>().state;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -213,7 +213,7 @@ class InitializingPage extends StatelessWidget {
BrandText.body2('initializing.4'.tr()), BrandText.body2('initializing.4'.tr()),
const Spacer(), const Spacer(),
CubitFormTextField( CubitFormTextField(
formFieldCubit: context.read<CloudFlareFormCubit>().apiKey, formFieldCubit: context.read<DnsProviderFormCubit>().apiKey,
textAlign: TextAlign.center, textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70), scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: InputDecoration( decoration: InputDecoration(
@ -224,7 +224,7 @@ class InitializingPage extends StatelessWidget {
BrandButton.rised( BrandButton.rised(
onPressed: formCubitState.isSubmitting onPressed: formCubitState.isSubmitting
? null ? null
: () => context.read<CloudFlareFormCubit>().trySubmit(), : () => context.read<DnsProviderFormCubit>().trySubmit(),
text: 'basis.connect'.tr(), text: 'basis.connect'.tr(),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),

View File

@ -2,7 +2,7 @@ import 'package:cubit_form/cubit_form.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
@ -18,11 +18,11 @@ class RecoveryConfirmCloudflare extends StatelessWidget {
context.watch<ServerInstallationCubit>(); context.watch<ServerInstallationCubit>();
return BlocProvider( return BlocProvider(
create: (final BuildContext context) => CloudFlareFormCubit(appConfig), create: (final BuildContext context) => DnsProviderFormCubit(appConfig),
child: Builder( child: Builder(
builder: (final BuildContext context) { builder: (final BuildContext context) {
final FormCubitState formCubitState = final FormCubitState formCubitState =
context.watch<CloudFlareFormCubit>().state; context.watch<DnsProviderFormCubit>().state;
return BrandHeroScreen( return BrandHeroScreen(
heroTitle: 'recovering.confirm_cloudflare'.tr(), heroTitle: 'recovering.confirm_cloudflare'.tr(),
@ -35,7 +35,7 @@ class RecoveryConfirmCloudflare extends StatelessWidget {
context.read<ServerInstallationCubit>().revertRecoveryStep, context.read<ServerInstallationCubit>().revertRecoveryStep,
children: [ children: [
CubitFormTextField( CubitFormTextField(
formFieldCubit: context.read<CloudFlareFormCubit>().apiKey, formFieldCubit: context.read<DnsProviderFormCubit>().apiKey,
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
labelText: 'initializing.5'.tr(), labelText: 'initializing.5'.tr(),
@ -45,7 +45,7 @@ class RecoveryConfirmCloudflare extends StatelessWidget {
BrandButton.rised( BrandButton.rised(
onPressed: formCubitState.isSubmitting onPressed: formCubitState.isSubmitting
? null ? null
: () => context.read<CloudFlareFormCubit>().trySubmit(), : () => context.read<DnsProviderFormCubit>().trySubmit(),
text: 'basis.connect'.tr(), text: 'basis.connect'.tr(),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),