diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index b0ca4009..ab0b5368 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -47,7 +47,7 @@ class BNames { static String hetznerKey = 'hetznerKey'; static String cloudFlareKey = 'cloudFlareKey'; static String rootUser = 'rootUser'; - static String server = 'server'; + static String hetznerServer = 'server'; static String isDnsCheckedAndDkimSet = 'isDnsCheckedAndDkimSet'; static String serverInitStart = 'serverInitStart'; } diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index a14027c1..28c0a0d8 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -58,7 +58,7 @@ class HetznerApi extends ApiMap { return HetznerServerDetails( id: response.data['server']['id'], ip4: response.data['server']['public_net']['ipv4']['ip'], - serverInitializaionDateTime: DateTime.now(), + startTime: DateTime.now(), ); } } diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 56ca79cb..9ae4e76e 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:hive/hive.dart'; @@ -22,7 +24,7 @@ class AppConfigCubit extends Cubit { cloudFlareKey: box.get(BNames.cloudFlareKey), domain: box.get(BNames.domain), rootUser: box.get(BNames.rootUser), - hetznerServer: box.get(BNames.server), + hetznerServer: box.get(BNames.hetznerServer), isDnsCheckedAndDkimSet: box.get(BNames.isDnsCheckedAndDkimSet), ), ); @@ -53,9 +55,27 @@ class AppConfigCubit extends Cubit { emit(state.copyWith(rootUser: rootUser)); } - void setIsDnsCheckedAndDkimSet() { - box.put(BNames.isDnsCheckedAndDkimSet, true); - emit(state.copyWith(isDnsCheckedAndDkimSet: true)); + Future checkDns() async { + var ip4 = state.server.ip4; + var domainName = state.cloudFlareDomain.name; + var addresses = [ + '$domainName', + 'api.$domainName', + 'cloud.$domainName', + 'meet.$domainName', + 'password.$domainName' + ]; + var hasError = false; + for (var address in addresses) { + var res = await InternetAddress.lookup(address); + if (res.isEmpty || res[0].address != ip4) { + hasError = true; + break; + } + } + if (hasError) { + emit(state.copyWith(error: Exception('dns checking error'))); + } } void createServer() async { @@ -74,7 +94,7 @@ class AppConfigCubit extends Cubit { cloudFlareDomain: state.cloudFlareDomain, ) .then((_) => cloudflareApi.close()); - await box.put(BNames.server, serverDetails); + await box.put(BNames.hetznerServer, serverDetails); hetznerApi.close(); diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index 9cb4da35..8faab83b 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -9,6 +9,7 @@ class AppConfigState extends Equatable { this.server, this.isDnsCheckedAndDkimSet = false, this.isLoading = false, + this.error, }); @override @@ -20,6 +21,7 @@ class AppConfigState extends Equatable { server, isDnsCheckedAndDkimSet, isLoading, + error ]; final String hetznerKey; @@ -29,7 +31,8 @@ class AppConfigState extends Equatable { final HetznerServerDetails server; final bool isDnsCheckedAndDkimSet; - final isLoading; + final bool isLoading; + final Exception error; AppConfigState copyWith({ String hetznerKey, @@ -40,6 +43,7 @@ class AppConfigState extends Equatable { bool isDnsCheckedAndDkimSet, bool isLoading, DateTime serverInitStart, + Exception error, }) => AppConfigState( hetznerKey: hetznerKey ?? this.hetznerKey, @@ -47,8 +51,10 @@ class AppConfigState extends Equatable { cloudFlareDomain: domain ?? this.cloudFlareDomain, rootUser: rootUser ?? this.rootUser, server: hetznerServer ?? this.server, - isDnsCheckedAndDkimSet: isDnsCheckedAndDkimSet ?? this.isDnsCheckedAndDkimSet, + isDnsCheckedAndDkimSet: + isDnsCheckedAndDkimSet ?? this.isDnsCheckedAndDkimSet, isLoading: isLoading ?? this.isLoading, + error: error ?? this.error, ); bool get isHetznerFilled => hetznerKey != null; @@ -56,7 +62,7 @@ class AppConfigState extends Equatable { bool get isDomainFilled => cloudFlareDomain != null; bool get isUserFilled => rootUser != null; bool get isServerFilled => server != null; - + bool get isFullyInitilized => _fulfilementList.every((el) => el); int get progress => _fulfilementList.where((el) => el).length; diff --git a/lib/logic/models/server_details.dart b/lib/logic/models/server_details.dart index c628f073..0c887dda 100644 --- a/lib/logic/models/server_details.dart +++ b/lib/logic/models/server_details.dart @@ -8,7 +8,7 @@ class HetznerServerDetails { HetznerServerDetails({ @required this.ip4, @required this.id, - @required this.serverInitializaionDateTime, + @required this.startTime, }); @HiveField(0) @@ -18,7 +18,7 @@ class HetznerServerDetails { final int id; @HiveField(2) - final DateTime serverInitializaionDateTime; + final DateTime startTime; String toString() => id.toString(); } diff --git a/lib/logic/models/server_details.g.dart b/lib/logic/models/server_details.g.dart index a51e3140..4148911f 100644 --- a/lib/logic/models/server_details.g.dart +++ b/lib/logic/models/server_details.g.dart @@ -19,7 +19,7 @@ class HetznerServerDetailsAdapter extends TypeAdapter { return HetznerServerDetails( ip4: fields[0] as String, id: fields[1] as int, - serverInitializaionDateTime: fields[2] as DateTime, + startTime: fields[2] as DateTime, ); } @@ -32,7 +32,7 @@ class HetznerServerDetailsAdapter extends TypeAdapter { ..writeByte(1) ..write(obj.id) ..writeByte(2) - ..write(obj.serverInitializaionDateTime); + ..write(obj.startTime); } @override diff --git a/lib/ui/components/brand_timer/brand_timer.dart b/lib/ui/components/brand_timer/brand_timer.dart new file mode 100644 index 00000000..7dc21c86 --- /dev/null +++ b/lib/ui/components/brand_timer/brand_timer.dart @@ -0,0 +1,76 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:selfprivacy/utils/named_font_weight.dart'; + +class BrandTimer extends StatefulWidget { + const BrandTimer({ + Key key, + @required this.startDateTime, + @required this.duration, + @required this.callback, + }) : super(key: key); + + final DateTime startDateTime; + final Duration duration; + final VoidCallback callback; + + @override + _BrandTimerState createState() => _BrandTimerState(); +} + +class _BrandTimerState extends State { + String _timeString; + Timer timer; + + @override + void initState() { + _timeString = diffenceFromStart; + timer = Timer.periodic(Duration(seconds: 1), (Timer t) { + var timePassed = DateTime.now().difference(widget.startDateTime); + if (timePassed > widget.duration) { + t.cancel(); + widget.callback(); + } else { + _getTime(); + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return BrandText.medium( + _timeString, + style: TextStyle( + fontWeight: NamedFontWeight.demiBold, + ), + ); + } + + void _getTime() { + setState(() { + _timeString = diffenceFromStart; + }); + } + + String get diffenceFromStart => + _durationToString(DateTime.now().difference(widget.startDateTime)); + + String _durationToString(Duration duration) { + String twoDigits(int n) => n.toString().padLeft(2, "0"); + String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60)); + String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60)); + + return "$twoDigitMinutes:$twoDigitSeconds"; + } + + @override + void dispose() { + if (timer.isActive) { + timer.cancel(); + } + super.dispose(); + } +} diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index e2e68758..099f5e17 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -1,6 +1,9 @@ +import 'dart:io'; + import 'package:cubit_form/cubit_form.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/logic/cubit/forms/initializing/cloudflare_form_cubit.dart'; @@ -14,6 +17,7 @@ import 'package:selfprivacy/ui/components/brand_card/brand_card.dart'; import 'package:selfprivacy/ui/components/brand_modal_sheet/brand_modal_sheet.dart'; import 'package:selfprivacy/ui/components/brand_span_button/brand_span_button.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; +import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart'; import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; @@ -22,12 +26,14 @@ class InitializingPage extends StatelessWidget { @override Widget build(BuildContext context) { var cubit = context.watch(); + print(cubit.state.error); var actualPage = [ _stepHetzner(cubit), _stepCloudflare(cubit), _stepDomain(cubit), _stepUser(cubit), _stepServer(cubit), + _stepCheck(cubit), Container(child: Text('Everythigng is initialized')) ][cubit.state.progress]; return BlocListener( @@ -286,6 +292,51 @@ class InitializingPage extends StatelessWidget { }); } + Widget _stepCheck(AppConfigCubit appConfigCubit) { + var state = appConfigCubit.state; + var error = state.error; + print(error); + return Builder(builder: (context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Spacer(flex: 2), + BrandText.h2(error == null ? 'Создание началось' : 'Error'), + SizedBox(height: 10), + error == null + ? BrandText.body2( + 'Мы начали процесс инциализации сервера, через 10 минут, мы проверим правильность DNS записей, и закончим инциализацию', + ) + : BrandText.body2( + error.toString(), + style: TextStyle(color: BrandColors.red1), + ), + SizedBox(height: 10), + Row( + children: [ + BrandText.body2('Времени прошло: '), + BrandTimer( + startDateTime: state.server.startTime, + duration: Duration(minutes: 10), + callback: () { + appConfigCubit.checkDns(); + // print(state.server.ip4); + }, + ), + ], + ), + Spacer( + flex: 2, + ), + BrandButton.text( + onPressed: () => _showModal(context, _HowHetzner()), + title: 'Что это значит?', + ), + ], + ); + }); + } + Widget _addCard(Widget child) { return Container( height: 500,