mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-04 23:24:20 +00:00
feat: Implement infrastructure for new DNS provider deSEC
This commit is contained in:
parent
e180c23cb7
commit
234064ed72
89
assets/images/logos/desec.svg
Normal file
89
assets/images/logos/desec.svg
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="7.4053912mm"
|
||||
height="7.5173831mm"
|
||||
viewBox="0 0 7.4053913 7.5173831"
|
||||
version="1.1"
|
||||
id="svg1262"
|
||||
sodipodi:docname="logo.notext.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<defs
|
||||
id="defs1256" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="5.6"
|
||||
inkscape:cx="101.86078"
|
||||
inkscape:cy="8.9271745"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="g3885"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1365"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="38"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata1259">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-254.94057,-266.78298)">
|
||||
<g
|
||||
id="g3885"
|
||||
transform="matrix(0.26519825,0,0,0.26519825,228.89366,215.69135)"
|
||||
style="fill:#000000">
|
||||
<g
|
||||
style="fill:#000000;stroke:#ffffff;stroke-opacity:1"
|
||||
id="layer1-9"
|
||||
transform="matrix(0.22901929,0,0,0.22901929,26.296508,84.906304)"
|
||||
inkscape:export-filename="/home/nils/git/desec-stack/webapp/src/assets/logo.png"
|
||||
inkscape:export-xdpi="567.52002"
|
||||
inkscape:export-ydpi="567.52002">
|
||||
<g
|
||||
style="fill:#000000;stroke:#ffffff;stroke-opacity:1"
|
||||
transform="translate(-194.13584,150.8067)"
|
||||
id="g3933">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 509.13584,366.2239 c 8.87906,-33.13708 42.93987,-52.8021 76.07695,-43.92304 21.43594,5.74374 38.17931,22.48711 43.92305,43.92304 0,0 -6.09923,-6.07815 -10,-6.07815 -3.90077,0 -10,6.07815 -10,6.07815 0,0 -6.09923,-6.07815 -10,-6.07815 -3.90077,0 -10,6.07815 -10,6.07815 0,0 -6.09923,-6.07815 -10,-6.07815 -3.90077,0 -10,6.07815 -10,6.07815 0,0 -6.09923,-6.07815 -10,-6.07815 -3.90077,0 -10,6.07815 -10,6.07815 0,0 -6.09923,-6.07815 -10,-6.07815 -3.90077,0 -10,6.07815 -10,6.07815 0,0 -6.09923,-6.07815 -10,-6.07815 -3.90077,0 -10,6.07815 -10,6.07815 z"
|
||||
id="path2985-6-3"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:0.99999994;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 567.42674,364.89583 v 61.87321 c 0,9.34738 5.48085,16.17306 12.23879,16.17306 6.75795,0 12.23635,-6.83606 12.23635,-16.18344 0,0 -1.07806,-1.02674 -1.75904,-1.03964 -0.64261,-0.0122 -1.69589,0.91753 -1.69589,0.91753 0,6.70817 -3.93157,13.01592 -8.78142,13.01592 -4.84984,0 -8.78142,-6.30775 -8.78142,-13.01592 l -7.6e-4,-61.74072 z"
|
||||
id="path3775-7-4-6"
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:117.14173126;stroke-opacity:1;marker:none;enable-background:accumulate" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4 KiB |
|
@ -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';
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
287
lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart
Normal file
287
lib/logic/api_maps/rest_maps/dns_providers/desec/desec.dart
Normal file
|
@ -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<ApiConfigModel>().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<APIGenericResult<bool>> 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<String?> getZoneId(final String domain) async => domain;
|
||||
|
||||
@override
|
||||
Future<APIGenericResult<void>> 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<List<DnsRecord>> getDnsRecords({
|
||||
required final ServerDomain domain,
|
||||
}) async {
|
||||
Response response;
|
||||
final String domainName = domain.domainName;
|
||||
final List<DnsRecord> allRecords = <DnsRecord>[];
|
||||
|
||||
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<APIGenericResult<void>> createMultipleDnsRecords({
|
||||
required final ServerDomain domain,
|
||||
final String? ip4,
|
||||
}) async {
|
||||
final String domainName = domain.domainName;
|
||||
final List<DnsRecord> listDnsRecords = projectDnsRecords(domainName, ip4);
|
||||
final List<Future> allCreateFutures = <Future>[];
|
||||
|
||||
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<DnsRecord> 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 <DnsRecord>[
|
||||
domainA,
|
||||
apiA,
|
||||
cloudA,
|
||||
gitA,
|
||||
meetA,
|
||||
passwordA,
|
||||
socialA,
|
||||
mx,
|
||||
txt1,
|
||||
txt2,
|
||||
vpn
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> 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<List<String>> domainList() async {
|
||||
final String url = '$rootAddress/zones';
|
||||
List<String> domains = [];
|
||||
|
||||
final Dio client = await getClient();
|
||||
try {
|
||||
final Response response = await client.get(
|
||||
url,
|
||||
queryParameters: {'per_page': 50},
|
||||
);
|
||||
domains = response.data['result']
|
||||
.map<String>((final el) => el['name'] as String)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
print(e);
|
||||
} finally {
|
||||
close(client);
|
||||
}
|
||||
|
||||
return domains;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
);
|
||||
}
|
|
@ -66,6 +66,15 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
|||
);
|
||||
}
|
||||
|
||||
void setDnsProviderType(final DnsProvider providerType) async {
|
||||
await repository.saveDnsProviderType(providerType);
|
||||
ApiController.initDnsProviderApiFactory(
|
||||
DnsProviderApiFactorySettings(
|
||||
provider: providerType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ProviderApiTokenValidation serverProviderApiTokenValidation() =>
|
||||
ApiController.currentServerProviderApiFactory!
|
||||
.getServerProvider()
|
||||
|
|
|
@ -706,6 +706,10 @@ class ServerInstallationRepository {
|
|||
getIt<ApiConfigModel>().init();
|
||||
}
|
||||
|
||||
Future<void> saveDnsProviderType(final DnsProvider type) async {
|
||||
await getIt<ApiConfigModel>().storeDnsProviderType(type);
|
||||
}
|
||||
|
||||
Future<void> saveBackblazeKey(
|
||||
final BackblazeCredential backblazeCredential,
|
||||
) async {
|
||||
|
|
|
@ -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<void> storeDnsProviderType(final DnsProvider value) async {
|
||||
await _box.put(BNames.dnsProvider, value);
|
||||
_dnsProvider = value;
|
||||
}
|
||||
|
||||
Future<void> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,4 +29,6 @@ enum DnsProvider {
|
|||
unknown,
|
||||
@HiveField(1)
|
||||
cloudflare,
|
||||
@HiveField(2)
|
||||
desec
|
||||
}
|
||||
|
|
307
lib/ui/pages/setup/initializing/dns_provider_picker.dart
Normal file
307
lib/ui/pages/setup/initializing/dns_provider_picker.dart
Normal file
|
@ -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<DnsProviderPicker> createState() => _DnsProviderPickerState();
|
||||
}
|
||||
|
||||
class _DnsProviderPickerState extends State<DnsProviderPicker> {
|
||||
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<void>(
|
||||
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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
|
@ -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<DnsProviderFormCubit>().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<DnsProviderFormCubit>().trySubmit(),
|
||||
text: 'basis.connect'.tr(),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BrandOutlinedButton(
|
||||
onPressed: () {
|
||||
context.read<SupportSystemCubit>().showArticle(
|
||||
article: 'how_cloudflare',
|
||||
context: context,
|
||||
);
|
||||
Scaffold.of(context).openEndDrawer();
|
||||
},
|
||||
title: 'initializing.how'.tr(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
builder: (final context) {
|
||||
final providerCubit = context.watch<DnsProviderFormCubit>();
|
||||
return DnsProviderPicker(
|
||||
formCubit: providerCubit,
|
||||
serverInstallationCubit: initializingCubit,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue