diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..3db14bb5 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 9cd19459..3eeb1219 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -5,7 +5,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:selfprivacy/logic/models/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; -import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; import 'package:selfprivacy/logic/models/user.dart'; @@ -14,7 +14,7 @@ class HiveConfig { await Hive.initFlutter(); Hive.registerAdapter(UserAdapter()); Hive.registerAdapter(HetznerServerDetailsAdapter()); - Hive.registerAdapter(CloudFlareDomainAdapter()); + Hive.registerAdapter(ServerDomainAdapter()); Hive.registerAdapter(BackblazeCredentialAdapter()); Hive.registerAdapter(BackblazeBucketAdapter()); Hive.registerAdapter(HetznerDataBaseAdapter()); @@ -56,13 +56,14 @@ class BNames { static String key = 'key'; static String sshEnckey = 'sshEngkey'; - static String cloudFlareDomain = 'cloudFlareDomain'; + static String hasFinalChecked = 'hasFinalChecked'; + static String isServerStarted = 'isServerStarted'; + + static String serverDomain = 'cloudFlareDomain'; static String hetznerKey = 'hetznerKey'; static String cloudFlareKey = 'cloudFlareKey'; static String rootUser = 'rootUser'; - static String hetznerServer = 'hetznerServer'; - static String hasFinalChecked = 'hasFinalChecked'; - static String isServerStarted = 'isServerStarted'; + static String serverDetails = 'hetznerServer'; static String backblazeKey = 'backblazeKey'; static String backblazeBucket = 'backblazeBucket'; static String isLoading = 'isLoading'; @@ -71,5 +72,4 @@ class BNames { static String sshConfig = 'sshConfig'; static String sshPrivateKey = "sshPrivateKey"; static String sshPublicKey = "sshPublicKey"; - static String serverDomain = "serverDomain"; } diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/cloudflare.dart index 5aaf9dc7..5b81fdba 100644 --- a/lib/logic/api_maps/cloudflare.dart +++ b/lib/logic/api_maps/cloudflare.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/api_map.dart'; -import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; import 'package:selfprivacy/logic/models/dns_records.dart'; class CloudflareApi extends ApiMap { @@ -63,7 +63,7 @@ class CloudflareApi extends ApiMap { Future removeSimilarRecords({ String? ip4, - required CloudFlareDomain cloudFlareDomain, + required ServerDomain cloudFlareDomain, }) async { var domainName = cloudFlareDomain.domainName; var domainZoneId = cloudFlareDomain.zoneId; @@ -89,7 +89,7 @@ class CloudflareApi extends ApiMap { } Future> getDnsRecords({ - required CloudFlareDomain cloudFlareDomain, + required ServerDomain cloudFlareDomain, }) async { var domainName = cloudFlareDomain.domainName; var domainZoneId = cloudFlareDomain.zoneId; @@ -120,7 +120,7 @@ class CloudflareApi extends ApiMap { Future createMultipleDnsRecords({ String? ip4, - required CloudFlareDomain cloudFlareDomain, + required ServerDomain cloudFlareDomain, }) async { var domainName = cloudFlareDomain.domainName; var domainZoneId = cloudFlareDomain.zoneId; @@ -186,7 +186,7 @@ class CloudflareApi extends ApiMap { } Future setDkim( - String dkimRecordString, CloudFlareDomain cloudFlareDomain) async { + String dkimRecordString, ServerDomain cloudFlareDomain) async { final domainZoneId = cloudFlareDomain.zoneId; final url = '$rootAddress/zones/$domainZoneId/dns_records'; diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 77afa5d8..304650f2 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -68,7 +68,7 @@ class HetznerApi extends ApiMap { return server == null; } - Future createVolume() async { + Future createVolume() async { var client = await getClient(); Response dbCreateResponse = await client.post( '/volumes', @@ -82,17 +82,17 @@ class HetznerApi extends ApiMap { }, ); var dbId = dbCreateResponse.data['volume']['id']; - return HetznerDataBase( + return ServerVolume( id: dbId, name: dbCreateResponse.data['volume']['name'], ); } - Future createServer({ + Future createServer({ required String cloudFlareKey, required User rootUser, required String domainName, - required HetznerDataBase dataBase, + required ServerVolume dataBase, }) async { var client = await getClient(); @@ -136,7 +136,7 @@ class HetznerApi extends ApiMap { print(serverCreateResponse.data); client.close(); - return HetznerServerDetails( + return ServerHostingDetails( id: serverCreateResponse.data['server']['id'], ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'], createTime: DateTime.now(), @@ -189,8 +189,8 @@ class HetznerApi extends ApiMap { close(client); } - Future reset() async { - var server = getIt().hetznerServer!; + Future reset() async { + var server = getIt().serverDetails!; var client = await getClient(); await client.post('/servers/${server.id}/actions/reset'); @@ -199,8 +199,8 @@ class HetznerApi extends ApiMap { return server.copyWith(startTime: DateTime.now()); } - Future powerOn() async { - var server = getIt().hetznerServer!; + Future powerOn() async { + var server = getIt().serverDetails!; var client = await getClient(); await client.post('/servers/${server.id}/actions/poweron'); @@ -211,7 +211,7 @@ class HetznerApi extends ApiMap { Future> getMetrics( DateTime start, DateTime end, String type) async { - var hetznerServer = getIt().hetznerServer; + var hetznerServer = getIt().serverDetails; var client = await getClient(); Map queryParameters = { @@ -228,7 +228,7 @@ class HetznerApi extends ApiMap { } Future getInfo() async { - var hetznerServer = getIt().hetznerServer; + var hetznerServer = getIt().serverDetails; var client = await getClient(); Response response = await client.get('/servers/${hetznerServer!.id}'); close(client); @@ -240,7 +240,7 @@ class HetznerApi extends ApiMap { required String ip4, required String domainName, }) async { - var hetznerServer = getIt().hetznerServer; + var hetznerServer = getIt().serverDetails; var client = await getClient(); await client.post( '/servers/${hetznerServer!.id}/actions/change_dns_ptr', diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index aac1add0..74c2137f 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -33,65 +33,68 @@ class ApiResponse { class ServerApi extends ApiMap { bool hasLogger; bool isWithToken; + String? overrideDomain; - ServerApi({this.hasLogger = false, this.isWithToken = true}); + ServerApi( + {this.hasLogger = false, this.isWithToken = true, this.overrideDomain}); BaseOptions get options { var options = BaseOptions(); if (isWithToken) { - var cloudFlareDomain = getIt().cloudFlareDomain; + var cloudFlareDomain = getIt().serverDomain; var domainName = cloudFlareDomain!.domainName; - var apiToken = getIt().hetznerServer?.apiToken; + var apiToken = getIt().serverDetails?.apiToken; options = BaseOptions(baseUrl: 'https://api.$domainName', headers: { 'Authorization': 'Bearer $apiToken', }); } + if (overrideDomain != null) { + options = BaseOptions(baseUrl: 'https://api.$overrideDomain'); + } + return options; } + Future getApiVersion() async { + Response response; + + var client = await getClient(); + String? apiVersion = null; + + try { + response = await client.get('/api/version'); + apiVersion = response.data['version']; + } on DioError catch (e) { + print(e.message); + } finally { + close(client); + return apiVersion; + } + } + Future isHttpServerWorking() async { - bool res; + bool res = false; Response response; var client = await getClient(); try { - response = await client.get( - '/services/status', - options: Options( - contentType: 'application/json', - receiveDataWhenStatusError: true, - followRedirects: false, - validateStatus: (status) { - return (status != null) && - (status < HttpStatus.internalServerError); - }), - ); + response = await client.get('/services/status'); res = response.statusCode == HttpStatus.ok; - } catch (e) { - res = false; + } on DioError catch (e) { + print(e.message); + } finally { + close(client); + return res; } - close(client); - return res; } Future> createUser(User user) async { var client = await getClient(); - var makeErrorApiReponse = (int status) { - return ApiResponse( - statusCode: status, - data: User( - login: user.login, - password: user.password, - isFoundOnServer: false, - ), - ); - }; - - late Response response; + Response response; try { response = await client.post( @@ -100,68 +103,75 @@ class ServerApi extends ApiMap { 'username': user.login, 'password': user.password, }, - options: Options( - contentType: 'application/json', - receiveDataWhenStatusError: true, - followRedirects: false, - validateStatus: (status) { - return (status != null) && - (status < HttpStatus.internalServerError); - }), ); - } catch (e) { - return makeErrorApiReponse(HttpStatus.internalServerError); + } on DioError catch (e) { + print(e.message); + return ApiResponse( + errorMessage: e.error.toString(), + statusCode: e.response?.statusCode ?? HttpStatus.internalServerError, + data: User( + login: user.login, + password: user.password, + isFoundOnServer: false, + ), + ); } finally { close(client); } - if ((response.statusCode != null) && - (response.statusCode == HttpStatus.created)) { - return ApiResponse( - statusCode: response.statusCode!, - data: User( - login: user.login, - password: user.password, - isFoundOnServer: true, - ), - ); + bool isFoundOnServer = false; + int statusCode = 0; + + final bool isUserCreated = (response.statusCode != null) && + (response.statusCode == HttpStatus.created); + + if (isUserCreated) { + isFoundOnServer = true; + statusCode = response.statusCode!; } else { - print(response.statusCode.toString() + - ": " + - (response.statusMessage ?? "")); - return makeErrorApiReponse( - response.statusCode ?? HttpStatus.internalServerError); + isFoundOnServer = false; + statusCode = HttpStatus.notAcceptable; } + + return ApiResponse( + statusCode: statusCode, + data: User( + login: user.login, + password: user.password, + isFoundOnServer: isFoundOnServer, + ), + ); } Future>> getUsersList() async { List res = []; Response response; + String? message; + int code = 0; var client = await getClient(); - response = await client.get( - '/users', - options: Options( - contentType: 'application/json', - receiveDataWhenStatusError: true, - followRedirects: false, - validateStatus: (status) { - return (status != null) && - (status < HttpStatus.internalServerError); - }), - ); try { + response = await client.get('/users'); for (var user in response.data) { res.add(user.toString()); } + } on DioError catch (e) { + print(e.message); + message = e.message; + code = e.response?.statusCode ?? HttpStatus.internalServerError; + res = []; } catch (e) { print(e); + message = e.toString(); + code = HttpStatus.internalServerError; res = []; + } finally { + close(client); } - close(client); - return ApiResponse>( - statusCode: response.statusCode ?? HttpStatus.internalServerError, + return ApiResponse( + errorMessage: message, + statusCode: code, data: res, ); } @@ -170,22 +180,23 @@ class ServerApi extends ApiMap { Response response; var client = await getClient(); - response = await client.post( - '/services/ssh/keys/${user.login}', - data: { - 'public_key': sshKey, - }, - options: Options( - contentType: 'application/json', - receiveDataWhenStatusError: true, - followRedirects: false, - validateStatus: (status) { - return (status != null) && - (status < HttpStatus.internalServerError); - }), - ); + try { + response = await client.post( + '/services/ssh/keys/${user.login}', + data: { + 'public_key': sshKey, + }, + ); + } on DioError catch (e) { + print(e.message); + return ApiResponse( + data: null, + errorMessage: e.message, + statusCode: e.response?.statusCode ?? HttpStatus.internalServerError); + } finally { + close(client); + } - close(client); return ApiResponse( statusCode: response.statusCode ?? HttpStatus.internalServerError, data: null, diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 69259976..030e4afe 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -5,7 +5,7 @@ import 'package:equatable/equatable.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/get_it/ssh.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; -import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; import 'package:selfprivacy/logic/models/user.dart'; @@ -15,35 +15,6 @@ export 'package:provider/provider.dart'; part 'app_config_state.dart'; -/// Initializing steps: -/// -/// The set phase. -/// 1.1. Hetzner key |setHetznerKey -/// 1.2. Cloudflare key |setCloudflareKey -/// 1.3. Backblaze Id + Key |setBackblazeKey -/// 1.4. Set Domain address |setDomain -/// 1.5. Set Root user name password |setRootUser -/// 1.6. Set Create server ans set DNS-Records |createServerAndSetDnsRecords -/// (without start) -/// -/// The check phase. -/// -/// 2.1. a. wait 60sec checkDnsAndStartServer |startServerIfDnsIsOkay -/// b. checkDns -/// c. if dns is okay start server -/// -/// 2.2. a. wait 60sec |resetServerIfServerIsOkay -/// b. checkServer -/// c. if server is ok wait 30 sec -/// d. reset server -/// -/// 2.3. a. wait 60sec |oneMoreReset() -/// d. reset server -/// -/// 2.4. a. wait 30sec |finishCheckIfServerIsOkay -/// b. checkServer -/// c. if server is okay set that fully checked - class AppConfigCubit extends Cubit { AppConfigCubit() : super(AppConfigEmpty()); @@ -55,180 +26,142 @@ class AppConfigCubit extends Cubit { if (state is AppConfigFinished) { emit(state); } else if (state is AppConfigNotFinished) { - if (state.progress == 6) { - startServerIfDnsIsOkay(state: state, isImmediate: true); - } else if (state.progress == 7) { - resetServerIfServerIsOkay(state: state, isImmediate: true); - } else if (state.progress == 8) { - oneMoreReset(state: state, isImmediate: true); - } else if (state.progress == 9) { - finishCheckIfServerIsOkay(state: state, isImmediate: true); + if (state.progress == ServerSetupProgress.serverCreated) { + startServerIfDnsIsOkay(state: state); + } else if (state.progress == ServerSetupProgress.serverStarted) { + resetServerIfServerIsOkay(state: state); + } else if (state.progress == ServerSetupProgress.serverResetedFirstTime) { + oneMoreReset(state: state); + } else if (state.progress == + ServerSetupProgress.serverResetedSecondTime) { + finishCheckIfServerIsOkay(state: state); } else { emit(state); } + } else if (state is AppConfigRecovery) { + emit(state); } else { throw 'wrong state'; } } - void startServerIfDnsIsOkay({ - AppConfigNotFinished? state, - bool isImmediate = false, - }) async { - state = state ?? this.state as AppConfigNotFinished; + void runDelayed( + void Function() work, Duration delay, AppConfigNotFinished? state) async { + final dataState = state ?? this.state as AppConfigNotFinished; - final work = () async { - emit(TimerState(dataState: state!, isLoading: true)); + emit(TimerState( + dataState: dataState, + timerStart: DateTime.now(), + duration: delay, + isLoading: false, + )); + timer = Timer(delay, work); + } - var ip4 = state.hetznerServer!.ip4; - var domainName = state.cloudFlareDomain!.domainName; + void startServerIfDnsIsOkay({AppConfigNotFinished? state}) async { + final dataState = state ?? this.state as AppConfigNotFinished; - var matches = await repository.isDnsAddressesMatch( - domainName, ip4, state.dnsMatches); + emit(TimerState(dataState: dataState, isLoading: true)); - if (matches.values.every((value) => value)) { - var server = await repository.startServer( - state.hetznerServer!, - ); - await repository.saveServerDetails(server); - await repository.saveIsServerStarted(true); + var ip4 = dataState.serverDetails!.ip4; + var domainName = dataState.serverDomain!.domainName; - emit( - state.copyWith( - isServerStarted: true, - isLoading: false, - hetznerServer: server, - ), - ); - resetServerIfServerIsOkay(); - } else { - emit( - state.copyWith( - isLoading: false, - dnsMatches: matches, - ), - ); - startServerIfDnsIsOkay(); - } - }; + var matches = await repository.isDnsAddressesMatch( + domainName, ip4, dataState.dnsMatches); - if (isImmediate) { - work(); + if (matches.values.every((value) => value)) { + var server = await repository.startServer( + dataState.serverDetails!, + ); + await repository.saveServerDetails(server); + await repository.saveIsServerStarted(true); + + emit( + dataState.copyWith( + isServerStarted: true, + isLoading: false, + serverDetails: server, + ), + ); + runDelayed(resetServerIfServerIsOkay, Duration(seconds: 60), dataState); } else { - var pauseDuration = Duration(seconds: 30); - emit(TimerState( - dataState: state, - timerStart: DateTime.now(), - duration: pauseDuration, - isLoading: false, - )); - timer = Timer(pauseDuration, work); + emit( + dataState.copyWith( + isLoading: false, + dnsMatches: matches, + ), + ); + runDelayed(startServerIfDnsIsOkay, Duration(seconds: 30), dataState); } } - void oneMoreReset({ - AppConfigNotFinished? state, - bool isImmediate = false, - }) async { - var dataState = state ?? this.state as AppConfigNotFinished; + void oneMoreReset({AppConfigNotFinished? state}) async { + final dataState = state ?? this.state as AppConfigNotFinished; - var work = () async { - emit(TimerState(dataState: dataState, isLoading: true)); + emit(TimerState(dataState: dataState, isLoading: true)); - var isServerWorking = await repository.isHttpServerWorking(); + var isServerWorking = await repository.isHttpServerWorking(); - if (isServerWorking) { - var pauseDuration = Duration(seconds: 30); - emit(TimerState( - dataState: dataState, - timerStart: DateTime.now(), - isLoading: false, - duration: pauseDuration, - )); - timer = Timer(pauseDuration, () async { - var hetznerServerDetails = await repository.restart(); - await repository.saveIsServerResetedSecondTime(true); - await repository.saveServerDetails(hetznerServerDetails); + if (isServerWorking) { + var pauseDuration = Duration(seconds: 30); + emit(TimerState( + dataState: dataState, + timerStart: DateTime.now(), + isLoading: false, + duration: pauseDuration, + )); + timer = Timer(pauseDuration, () async { + var hetznerServerDetails = await repository.restart(); + await repository.saveIsServerResetedSecondTime(true); + await repository.saveServerDetails(hetznerServerDetails); - emit( - dataState.copyWith( - isServerResetedSecondTime: true, - hetznerServer: hetznerServerDetails, - isLoading: false, - ), - ); - finishCheckIfServerIsOkay(); - }); - } else { - oneMoreReset(); - } - }; - if (isImmediate) { - work(); + emit( + dataState.copyWith( + isServerResetedSecondTime: true, + serverDetails: hetznerServerDetails, + isLoading: false, + ), + ); + runDelayed(finishCheckIfServerIsOkay, Duration(seconds: 60), dataState); + }); } else { - var pauseDuration = Duration(seconds: 60); - emit( - TimerState( - dataState: dataState, - timerStart: DateTime.now(), - duration: pauseDuration, - isLoading: false, - ), - ); - timer = Timer(pauseDuration, work); + runDelayed(oneMoreReset, Duration(seconds: 60), dataState); } } void resetServerIfServerIsOkay({ AppConfigNotFinished? state, - bool isImmediate = false, }) async { - var dataState = state ?? this.state as AppConfigNotFinished; + final dataState = state ?? this.state as AppConfigNotFinished; - var work = () async { - emit(TimerState(dataState: dataState, isLoading: true)); + emit(TimerState(dataState: dataState, isLoading: true)); - var isServerWorking = await repository.isHttpServerWorking(); + var isServerWorking = await repository.isHttpServerWorking(); - if (isServerWorking) { - var pauseDuration = Duration(seconds: 30); - emit(TimerState( - dataState: dataState, - timerStart: DateTime.now(), - isLoading: false, - duration: pauseDuration, - )); - timer = Timer(pauseDuration, () async { - var hetznerServerDetails = await repository.restart(); - await repository.saveIsServerResetedFirstTime(true); - await repository.saveServerDetails(hetznerServerDetails); + if (isServerWorking) { + var pauseDuration = Duration(seconds: 30); + emit(TimerState( + dataState: dataState, + timerStart: DateTime.now(), + isLoading: false, + duration: pauseDuration, + )); + timer = Timer(pauseDuration, () async { + var hetznerServerDetails = await repository.restart(); + await repository.saveIsServerResetedFirstTime(true); + await repository.saveServerDetails(hetznerServerDetails); - emit( - dataState.copyWith( - isServerResetedFirstTime: true, - hetznerServer: hetznerServerDetails, - isLoading: false, - ), - ); - oneMoreReset(); - }); - } else { - resetServerIfServerIsOkay(); - } - }; - if (isImmediate) { - work(); + emit( + dataState.copyWith( + isServerResetedFirstTime: true, + serverDetails: hetznerServerDetails, + isLoading: false, + ), + ); + runDelayed(oneMoreReset, Duration(seconds: 60), dataState); + }); } else { - var pauseDuration = Duration(seconds: 60); - emit( - TimerState( - dataState: dataState, - timerStart: DateTime.now(), - duration: pauseDuration, - isLoading: false, - ), - ); - timer = Timer(pauseDuration, work); + runDelayed(resetServerIfServerIsOkay, Duration(seconds: 60), dataState); } } @@ -236,37 +169,20 @@ class AppConfigCubit extends Cubit { void finishCheckIfServerIsOkay({ AppConfigNotFinished? state, - bool isImmediate = false, }) async { - state = state ?? this.state as AppConfigNotFinished; + final dataState = state ?? this.state as AppConfigNotFinished; - var work = () async { - emit(TimerState(dataState: state!, isLoading: true)); + emit(TimerState(dataState: dataState, isLoading: true)); - var isServerWorking = await repository.isHttpServerWorking(); + var isServerWorking = await repository.isHttpServerWorking(); - if (isServerWorking) { - await repository.createDkimRecord(state.cloudFlareDomain!); - await repository.saveHasFinalChecked(true); + if (isServerWorking) { + await repository.createDkimRecord(dataState.serverDomain!); + await repository.saveHasFinalChecked(true); - emit(state.finish()); - } else { - finishCheckIfServerIsOkay(); - } - }; - if (isImmediate) { - work(); + emit(dataState.finish()); } else { - var pauseDuration = Duration(seconds: 60); - emit( - TimerState( - dataState: state, - timerStart: DateTime.now(), - duration: pauseDuration, - isLoading: false, - ), - ); - timer = Timer(pauseDuration, work); + runDelayed(finishCheckIfServerIsOkay, Duration(seconds: 60), dataState); } } @@ -280,18 +196,18 @@ class AppConfigCubit extends Cubit { Future serverDelete() async { closeTimer(); - if (state.hetznerServer != null) { - await repository.deleteServer(state.cloudFlareDomain!); + if (state.serverDetails != null) { + await repository.deleteServer(state.serverDomain!); await getIt().clear(); } await repository.deleteRecords(); emit(AppConfigNotFinished( hetznerKey: state.hetznerKey, - cloudFlareDomain: state.cloudFlareDomain, + serverDomain: state.serverDomain, cloudFlareKey: state.cloudFlareKey, backblazeCredential: state.backblazeCredential, rootUser: state.rootUser, - hetznerServer: null, + serverDetails: null, isServerStarted: false, isServerResetedFirstTime: false, isServerResetedSecondTime: false, @@ -321,10 +237,9 @@ class AppConfigCubit extends Cubit { .copyWith(backblazeCredential: backblazeCredential)); } - void setDomain(CloudFlareDomain cloudFlareDomain) async { - await repository.saveDomain(cloudFlareDomain); - emit((state as AppConfigNotFinished) - .copyWith(cloudFlareDomain: cloudFlareDomain)); + void setDomain(ServerDomain serverDomain) async { + await repository.saveDomain(serverDomain); + emit((state as AppConfigNotFinished).copyWith(serverDomain: serverDomain)); } void setRootUser(User rootUser) async { @@ -334,17 +249,17 @@ class AppConfigCubit extends Cubit { void createServerAndSetDnsRecords() async { AppConfigNotFinished _stateCopy = state as AppConfigNotFinished; - var onSuccess = (HetznerServerDetails serverDetails) async { + var onSuccess = (ServerHostingDetails serverDetails) async { await repository.createDnsRecords( serverDetails.ip4, - state.cloudFlareDomain!, + state.serverDomain!, ); emit((state as AppConfigNotFinished).copyWith( isLoading: false, - hetznerServer: serverDetails, + serverDetails: serverDetails, )); - startServerIfDnsIsOkay(); + runDelayed(startServerIfDnsIsOkay, Duration(seconds: 30), null); }; var onCancel = @@ -354,7 +269,7 @@ class AppConfigCubit extends Cubit { emit((state as AppConfigNotFinished).copyWith(isLoading: true)); await repository.createServer( state.rootUser!, - state.cloudFlareDomain!.domainName, + state.serverDomain!.domainName, state.cloudFlareKey!, state.backblazeCredential!, onCancel: onCancel, diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index c4095cae..363f1134 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -8,7 +8,7 @@ import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; -import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; import 'package:selfprivacy/logic/models/message.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; import 'package:selfprivacy/logic/models/user.dart'; @@ -21,14 +21,19 @@ class AppConfigRepository { Box box = Hive.box(BNames.appConfig); Future load() async { - late AppConfigState res; + final hetznerToken = getIt().hetznerKey; + final cloudflareToken = getIt().cloudFlareKey; + final serverDomain = getIt().serverDomain; + final backblazeCredential = getIt().backblazeCredential; + final serverDetails = getIt().serverDetails; + if (box.get(BNames.hasFinalChecked, defaultValue: false)) { - res = AppConfigFinished( - hetznerKey: getIt().hetznerKey!, - cloudFlareKey: getIt().cloudFlareKey!, - cloudFlareDomain: getIt().cloudFlareDomain!, - backblazeCredential: getIt().backblazeCredential!, - hetznerServer: getIt().hetznerServer!, + return AppConfigFinished( + hetznerKey: hetznerToken!, + cloudFlareKey: cloudflareToken!, + serverDomain: serverDomain!, + backblazeCredential: backblazeCredential!, + serverDetails: serverDetails!, rootUser: box.get(BNames.rootUser), isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), isServerResetedFirstTime: @@ -36,33 +41,62 @@ class AppConfigRepository { isServerResetedSecondTime: box.get(BNames.isServerResetedSecondTime, defaultValue: false), ); - } else { - res = AppConfigNotFinished( - hetznerKey: getIt().hetznerKey, - cloudFlareKey: getIt().cloudFlareKey, - cloudFlareDomain: getIt().cloudFlareDomain, - backblazeCredential: getIt().backblazeCredential, - hetznerServer: getIt().hetznerServer, - rootUser: box.get(BNames.rootUser), - isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), - isServerResetedFirstTime: - box.get(BNames.isServerResetedFirstTime, defaultValue: false), - isServerResetedSecondTime: - box.get(BNames.isServerResetedSecondTime, defaultValue: false), - isLoading: box.get(BNames.isLoading, defaultValue: false), - dnsMatches: null, - ); } - return res; + if (getIt().serverDomain?.provider == DnsProvider.Unknown) { + return AppConfigRecovery( + hetznerKey: hetznerToken, + cloudFlareKey: cloudflareToken, + serverDomain: serverDomain, + backblazeCredential: backblazeCredential, + serverDetails: serverDetails, + rootUser: box.get(BNames.rootUser), + currentStep: getCurrentRecoveryStep( + hetznerToken, cloudflareToken, serverDomain!, serverDetails), + ); + } + + return AppConfigNotFinished( + hetznerKey: hetznerToken, + cloudFlareKey: cloudflareToken, + serverDomain: serverDomain, + backblazeCredential: backblazeCredential, + serverDetails: serverDetails, + rootUser: box.get(BNames.rootUser), + isServerStarted: box.get(BNames.isServerStarted, defaultValue: false), + isServerResetedFirstTime: + box.get(BNames.isServerResetedFirstTime, defaultValue: false), + isServerResetedSecondTime: + box.get(BNames.isServerResetedSecondTime, defaultValue: false), + isLoading: box.get(BNames.isLoading, defaultValue: false), + dnsMatches: null, + ); + } + + RecoveryStep getCurrentRecoveryStep( + String? hetznerToken, + String? cloudflareToken, + ServerDomain serverDomain, + ServerHostingDetails? serverDetails, + ) { + if (serverDetails != null) { + if (hetznerToken != null) { + if (cloudflareToken != null) { + return RecoveryStep.BackblazeToken; + } + return RecoveryStep.CloudflareToken; + } + return RecoveryStep.HetznerToken; + } + return RecoveryStep.Selecting; } void clearAppConfig() { box.clear(); } - Future startServer( - HetznerServerDetails hetznerServer, + Future startServer( + ServerHostingDetails hetznerServer, ) async { var hetznerApi = HetznerApi(); var serverDetails = await hetznerApi.powerOn(); @@ -122,11 +156,11 @@ class AppConfigRepository { String cloudFlareKey, BackblazeCredential backblazeCredential, { required void Function() onCancel, - required Future Function(HetznerServerDetails serverDetails) + required Future Function(ServerHostingDetails serverDetails) onSuccess, }) async { var hetznerApi = HetznerApi(); - late HetznerDataBase dataBase; + late ServerVolume dataBase; try { dataBase = await hetznerApi.createVolume(); @@ -180,7 +214,7 @@ class AppConfigRepository { Future createDnsRecords( String ip4, - CloudFlareDomain cloudFlareDomain, + ServerDomain cloudFlareDomain, ) async { var cloudflareApi = CloudflareApi(); @@ -200,7 +234,7 @@ class AppConfigRepository { ); } - Future createDkimRecord(CloudFlareDomain cloudFlareDomain) async { + Future createDkimRecord(ServerDomain cloudFlareDomain) async { var cloudflareApi = CloudflareApi(); var api = ServerApi(); @@ -220,17 +254,17 @@ class AppConfigRepository { return isHttpServerWorking; } - Future restart() async { + Future restart() async { var hetznerApi = HetznerApi(); return await hetznerApi.reset(); } - Future powerOn() async { + Future powerOn() async { var hetznerApi = HetznerApi(); return await hetznerApi.powerOn(); } - Future saveServerDetails(HetznerServerDetails serverDetails) async { + Future saveServerDetails(ServerHostingDetails serverDetails) async { await getIt().storeServerDetails(serverDetails); } @@ -239,10 +273,6 @@ class AppConfigRepository { await getIt().storeHetznerKey(key); } - Future saveServerDomain(String domain) async { - await getIt().storeServerDomain(domain); - } - Future saveBackblazeKey(BackblazeCredential backblazeCredential) async { await getIt().storeBackblazeCredential(backblazeCredential); } @@ -251,8 +281,8 @@ class AppConfigRepository { await getIt().storeCloudFlareKey(key); } - Future saveDomain(CloudFlareDomain cloudFlareDomain) async { - await getIt().storeCloudFlareDomain(cloudFlareDomain); + Future saveDomain(ServerDomain serverDomain) async { + await getIt().storeServerDomain(serverDomain); } Future saveIsServerStarted(bool value) async { @@ -275,12 +305,12 @@ class AppConfigRepository { await box.put(BNames.hasFinalChecked, value); } - Future deleteServer(CloudFlareDomain cloudFlareDomain) async { + Future deleteServer(ServerDomain serverDomain) async { var hetznerApi = HetznerApi(); var cloudFlare = CloudflareApi(); await hetznerApi.deleteSelfprivacyServerAndAllVolumes( - domainName: cloudFlareDomain.domainName, + domainName: serverDomain.domainName, ); await box.put(BNames.hasFinalChecked, false); @@ -288,14 +318,14 @@ class AppConfigRepository { await box.put(BNames.isServerResetedFirstTime, false); await box.put(BNames.isServerResetedSecondTime, false); await box.put(BNames.isLoading, false); - await box.put(BNames.hetznerServer, null); + await box.put(BNames.serverDetails, null); - await cloudFlare.removeSimilarRecords(cloudFlareDomain: cloudFlareDomain); + await cloudFlare.removeSimilarRecords(cloudFlareDomain: serverDomain); } Future deleteRecords() async { await box.deleteAll([ - BNames.hetznerServer, + BNames.serverDetails, BNames.isServerStarted, BNames.isServerResetedFirstTime, BNames.isServerResetedSecondTime, diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index ac456e17..9d620e9e 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -5,9 +5,9 @@ abstract class AppConfigState extends Equatable { required this.hetznerKey, required this.cloudFlareKey, required this.backblazeCredential, - required this.cloudFlareDomain, + required this.serverDomain, required this.rootUser, - required this.hetznerServer, + required this.serverDetails, required this.isServerStarted, required this.isServerResetedFirstTime, required this.isServerResetedSecondTime, @@ -18,9 +18,9 @@ abstract class AppConfigState extends Equatable { hetznerKey, cloudFlareKey, backblazeCredential, - cloudFlareDomain, + serverDomain, rootUser, - hetznerServer, + serverDetails, isServerStarted, isServerResetedFirstTime, ]; @@ -28,9 +28,9 @@ abstract class AppConfigState extends Equatable { final String? hetznerKey; final String? cloudFlareKey; final BackblazeCredential? backblazeCredential; - final CloudFlareDomain? cloudFlareDomain; + final ServerDomain? serverDomain; final User? rootUser; - final HetznerServerDetails? hetznerServer; + final ServerHostingDetails? serverDetails; final bool isServerStarted; final bool isServerResetedFirstTime; final bool isServerResetedSecondTime; @@ -38,17 +38,18 @@ abstract class AppConfigState extends Equatable { bool get isHetznerFilled => hetznerKey != null; bool get isCloudFlareFilled => cloudFlareKey != null; bool get isBackblazeFilled => backblazeCredential != null; - bool get isDomainFilled => cloudFlareDomain != null; + bool get isDomainFilled => serverDomain != null; bool get isUserFilled => rootUser != null; - bool get isServerCreated => hetznerServer != null; + bool get isServerCreated => serverDetails != null; bool get isFullyInitilized => _fulfilementList.every((el) => el!); - int get progress => _fulfilementList.where((el) => el!).length; + ServerSetupProgress get progress => + ServerSetupProgress.values[_fulfilementList.where((el) => el!).length]; int get porgressBar { - if (progress < 6) { - return progress; - } else if (progress < 10) { + if (progress.index < 6) { + return progress.index; + } else if (progress.index < 10) { return 6; } else { return 7; @@ -82,9 +83,9 @@ class TimerState extends AppConfigNotFinished { hetznerKey: dataState.hetznerKey, cloudFlareKey: dataState.cloudFlareKey, backblazeCredential: dataState.backblazeCredential, - cloudFlareDomain: dataState.cloudFlareDomain, + serverDomain: dataState.serverDomain, rootUser: dataState.rootUser, - hetznerServer: dataState.hetznerServer, + serverDetails: dataState.serverDetails, isServerStarted: dataState.isServerStarted, isServerResetedFirstTime: dataState.isServerResetedFirstTime, isServerResetedSecondTime: dataState.isServerResetedSecondTime, @@ -104,6 +105,19 @@ class TimerState extends AppConfigNotFinished { ]; } +enum ServerSetupProgress { + nothingYet, + hetznerFilled, + cloudFlareFilled, + backblazeFilled, + domainFilled, + userFilled, + serverCreated, + serverStarted, + serverResetedFirstTime, + serverResetedSecondTime, +} + class AppConfigNotFinished extends AppConfigState { final bool isLoading; final Map? dnsMatches; @@ -112,9 +126,9 @@ class AppConfigNotFinished extends AppConfigState { String? hetznerKey, String? cloudFlareKey, BackblazeCredential? backblazeCredential, - CloudFlareDomain? cloudFlareDomain, + ServerDomain? serverDomain, User? rootUser, - HetznerServerDetails? hetznerServer, + ServerHostingDetails? serverDetails, required bool isServerStarted, required bool isServerResetedFirstTime, required bool isServerResetedSecondTime, @@ -124,9 +138,9 @@ class AppConfigNotFinished extends AppConfigState { hetznerKey: hetznerKey, cloudFlareKey: cloudFlareKey, backblazeCredential: backblazeCredential, - cloudFlareDomain: cloudFlareDomain, + serverDomain: serverDomain, rootUser: rootUser, - hetznerServer: hetznerServer, + serverDetails: serverDetails, isServerStarted: isServerStarted, isServerResetedFirstTime: isServerResetedFirstTime, isServerResetedSecondTime: isServerResetedSecondTime, @@ -137,9 +151,9 @@ class AppConfigNotFinished extends AppConfigState { hetznerKey, cloudFlareKey, backblazeCredential, - cloudFlareDomain, + serverDomain, rootUser, - hetznerServer, + serverDetails, isServerStarted, isServerResetedFirstTime, isLoading, @@ -150,9 +164,9 @@ class AppConfigNotFinished extends AppConfigState { String? hetznerKey, String? cloudFlareKey, BackblazeCredential? backblazeCredential, - CloudFlareDomain? cloudFlareDomain, + ServerDomain? serverDomain, User? rootUser, - HetznerServerDetails? hetznerServer, + ServerHostingDetails? serverDetails, bool? isServerStarted, bool? isServerResetedFirstTime, bool? isServerResetedSecondTime, @@ -163,9 +177,9 @@ class AppConfigNotFinished extends AppConfigState { hetznerKey: hetznerKey ?? this.hetznerKey, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, backblazeCredential: backblazeCredential ?? this.backblazeCredential, - cloudFlareDomain: cloudFlareDomain ?? this.cloudFlareDomain, + serverDomain: serverDomain ?? this.serverDomain, rootUser: rootUser ?? this.rootUser, - hetznerServer: hetznerServer ?? this.hetznerServer, + serverDetails: serverDetails ?? this.serverDetails, isServerStarted: isServerStarted ?? this.isServerStarted, isServerResetedFirstTime: isServerResetedFirstTime ?? this.isServerResetedFirstTime, @@ -179,9 +193,9 @@ class AppConfigNotFinished extends AppConfigState { hetznerKey: hetznerKey!, cloudFlareKey: cloudFlareKey!, backblazeCredential: backblazeCredential!, - cloudFlareDomain: cloudFlareDomain!, + serverDomain: serverDomain!, rootUser: rootUser!, - hetznerServer: hetznerServer!, + serverDetails: serverDetails!, isServerStarted: isServerStarted, isServerResetedFirstTime: isServerResetedFirstTime, isServerResetedSecondTime: isServerResetedSecondTime, @@ -194,9 +208,9 @@ class AppConfigEmpty extends AppConfigNotFinished { hetznerKey: null, cloudFlareKey: null, backblazeCredential: null, - cloudFlareDomain: null, + serverDomain: null, rootUser: null, - hetznerServer: null, + serverDetails: null, isServerStarted: false, isServerResetedFirstTime: false, isServerResetedSecondTime: false, @@ -210,9 +224,9 @@ class AppConfigFinished extends AppConfigState { required String hetznerKey, required String cloudFlareKey, required BackblazeCredential backblazeCredential, - required CloudFlareDomain cloudFlareDomain, + required ServerDomain serverDomain, required User rootUser, - required HetznerServerDetails hetznerServer, + required ServerHostingDetails serverDetails, required bool isServerStarted, required bool isServerResetedFirstTime, required bool isServerResetedSecondTime, @@ -220,9 +234,9 @@ class AppConfigFinished extends AppConfigState { hetznerKey: hetznerKey, cloudFlareKey: cloudFlareKey, backblazeCredential: backblazeCredential, - cloudFlareDomain: cloudFlareDomain, + serverDomain: serverDomain, rootUser: rootUser, - hetznerServer: hetznerServer, + serverDetails: serverDetails, isServerStarted: isServerStarted, isServerResetedFirstTime: isServerResetedFirstTime, isServerResetedSecondTime: isServerResetedSecondTime, @@ -233,35 +247,45 @@ class AppConfigFinished extends AppConfigState { hetznerKey, cloudFlareKey, backblazeCredential, - cloudFlareDomain, + serverDomain, rootUser, - hetznerServer, + serverDetails, isServerStarted, isServerResetedFirstTime, ]; } -class AppRecovery extends AppConfigState { - const AppRecovery({ - required String hetznerKey, - required String cloudFlareKey, - required BackblazeCredential backblazeCredential, - required CloudFlareDomain cloudFlareDomain, - required User rootUser, - required HetznerServerDetails hetznerServer, - required bool isServerStarted, - required bool isServerResetedFirstTime, - required bool isServerResetedSecondTime, +enum RecoveryStep { + Selecting, + RecoveryKey, + NewDeviceKey, + OldToken, + HetznerToken, + CloudflareToken, + BackblazeToken, +} + +class AppConfigRecovery extends AppConfigState { + final RecoveryStep currentStep; + + const AppConfigRecovery({ + String? hetznerKey, + String? cloudFlareKey, + BackblazeCredential? backblazeCredential, + ServerDomain? serverDomain, + User? rootUser, + ServerHostingDetails? serverDetails, + required RecoveryStep this.currentStep, }) : super( hetznerKey: hetznerKey, cloudFlareKey: cloudFlareKey, backblazeCredential: backblazeCredential, - cloudFlareDomain: cloudFlareDomain, + serverDomain: serverDomain, rootUser: rootUser, - hetznerServer: hetznerServer, - isServerStarted: isServerStarted, - isServerResetedFirstTime: isServerResetedFirstTime, - isServerResetedSecondTime: isServerResetedSecondTime, + serverDetails: serverDetails, + isServerStarted: true, + isServerResetedFirstTime: true, + isServerResetedSecondTime: true, ); @override @@ -269,10 +293,29 @@ class AppRecovery extends AppConfigState { hetznerKey, cloudFlareKey, backblazeCredential, - cloudFlareDomain, + serverDomain, rootUser, - hetznerServer, + serverDetails, isServerStarted, isServerResetedFirstTime, + currentStep ]; + + AppConfigRecovery copyWith({ + String? hetznerKey, + String? cloudFlareKey, + BackblazeCredential? backblazeCredential, + ServerDomain? serverDomain, + User? rootUser, + ServerHostingDetails? serverDetails, + RecoveryStep? currentStep, + }) => + AppConfigRecovery( + hetznerKey: hetznerKey ?? this.hetznerKey, + cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, + backblazeCredential: backblazeCredential ?? this.backblazeCredential, + serverDomain: serverDomain ?? this.serverDomain, + rootUser: rootUser ?? this.rootUser, + serverDetails: serverDetails ?? this.serverDetails, + currentStep: currentStep ?? this.currentStep); } diff --git a/lib/logic/cubit/backups/backups_cubit.dart b/lib/logic/cubit/backups/backups_cubit.dart index e79978c2..a867dc87 100644 --- a/lib/logic/cubit/backups/backups_cubit.dart +++ b/lib/logic/cubit/backups/backups_cubit.dart @@ -85,9 +85,9 @@ class BackupsCubit extends AppConfigDependendCubit { Future createBucket() async { emit(state.copyWith(preventActions: true)); - final domain = appConfigCubit.state.cloudFlareDomain!.domainName + final domain = appConfigCubit.state.serverDomain!.domainName .replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-'); - final serverId = appConfigCubit.state.hetznerServer!.id; + final serverId = appConfigCubit.state.serverDetails!.id; var bucketName = 'selfprivacy-$domain-$serverId'; // If bucket name is too long, shorten it if (bucketName.length > 49) { diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index de03e356..8efc7bfc 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -1,6 +1,6 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart'; -import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; import 'package:selfprivacy/logic/models/dns_records.dart'; import '../../api_maps/cloudflare.dart'; @@ -20,11 +20,11 @@ class DnsRecordsCubit extends AppConfigDependendCubit { emit(DnsRecordsState( dnsState: DnsRecordsStatus.refreshing, dnsRecords: _getDesiredDnsRecords( - appConfigCubit.state.cloudFlareDomain?.domainName, "", ""))); + appConfigCubit.state.serverDomain?.domainName, "", ""))); print('Loading DNS status'); if (appConfigCubit.state is AppConfigFinished) { - final CloudFlareDomain? domain = appConfigCubit.state.cloudFlareDomain; - final String? ipAddress = appConfigCubit.state.hetznerServer?.ip4; + final ServerDomain? domain = appConfigCubit.state.serverDomain; + final String? ipAddress = appConfigCubit.state.serverDetails?.ip4; if (domain != null && ipAddress != null) { final List records = await cloudflare.getDnsRecords(cloudFlareDomain: domain); @@ -95,8 +95,8 @@ class DnsRecordsCubit extends AppConfigDependendCubit { Future fix() async { emit(state.copyWith(dnsState: DnsRecordsStatus.refreshing)); - final CloudFlareDomain? domain = appConfigCubit.state.cloudFlareDomain; - final String? ipAddress = appConfigCubit.state.hetznerServer?.ip4; + final ServerDomain? domain = appConfigCubit.state.serverDomain; + final String? ipAddress = appConfigCubit.state.serverDetails?.ip4; final String? dkimPublicKey = await api.getDkim(); await cloudflare.removeSimilarRecords(cloudFlareDomain: domain!); await cloudflare.createMultipleDnsRecords( diff --git a/lib/logic/cubit/forms/factories/field_cubit_factory.dart b/lib/logic/cubit/forms/factories/field_cubit_factory.dart index d3255a5f..ac75c2c9 100644 --- a/lib/logic/cubit/forms/factories/field_cubit_factory.dart +++ b/lib/logic/cubit/forms/factories/field_cubit_factory.dart @@ -52,5 +52,14 @@ class FieldCubitFactory { ); } + FieldCubit createServerDomainField() { + return FieldCubit( + initalValue: '', + validations: [ + RequiredStringValidation('validations.required'.tr()), + ], + ); + } + final BuildContext context; } diff --git a/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart b/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart index 78a244c8..1582a4cf 100644 --- a/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart +++ b/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart @@ -1,7 +1,7 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; -import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; class DomainSetupCubit extends Cubit { DomainSetupCubit(this.initializingCubit) : super(Initial()); @@ -36,9 +36,10 @@ class DomainSetupCubit extends Cubit { var zoneId = await api.getZoneId(domainName); - var domain = CloudFlareDomain( + var domain = ServerDomain( domainName: domainName, zoneId: zoneId, + provider: DnsProvider.Cloudflare, ); initializingCubit.setDomain(domain); diff --git a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart index 8ab5cb88..1b56b610 100644 --- a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart @@ -1,51 +1,32 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; +import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; -import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; +import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; class RecoveryDomainFormCubit extends FormCubit { - RecoveryDomainFormCubit(this.initializingCubit) { - var regExp = RegExp(r"\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]"); - apiKey = FieldCubit( - initalValue: '', - validations: [ - RequiredStringValidation('validations.required'.tr()), - ValidationModel( - (s) => regExp.hasMatch(s), 'validations.key_format'.tr()), - LengthStringNotEqualValidation(64) - ], - ); + RecoveryDomainFormCubit( + this.initializingCubit, final FieldCubitFactory fieldFactory) { + serverDomainField = fieldFactory.createServerDomainField(); - super.addFields([apiKey]); + super.addFields([serverDomainField]); } @override FutureOr onSubmit() async { - initializingCubit.setHetznerKey(apiKey.state.value); + initializingCubit.setDomain(ServerDomain( + domainName: serverDomainField.state.value, + provider: DnsProvider.Unknown, + zoneId: "")); } + // @override + // FutureOr asyncValidation() async { + // ; //var client = + // } + final AppConfigCubit initializingCubit; - - late final FieldCubit apiKey; - - @override - FutureOr asyncValidation() async { - late bool isKeyValid; - HetznerApi apiClient = HetznerApi(isWithToken: false); - - try { - isKeyValid = await apiClient.isValid(apiKey.state.value); - } catch (e) { - addError(e); - } - - if (!isKeyValid) { - apiKey.setError('bad key'); - return false; - } - return true; - } + late final FieldCubit serverDomainField; } diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart index b4969037..86aae1a9 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart @@ -14,7 +14,7 @@ class ServerDetailsCubit extends Cubit { ServerDetailsRepository repository = ServerDetailsRepository(); void check() async { - var isReadyToCheck = getIt().hetznerServer != null; + var isReadyToCheck = getIt().serverDetails != null; if (isReadyToCheck) { emit(ServerDetailsLoading()); var data = await repository.load(); diff --git a/lib/logic/get_it/api_config.dart b/lib/logic/get_it/api_config.dart index 5a2d622a..6ab1b564 100644 --- a/lib/logic/get_it/api_config.dart +++ b/lib/logic/get_it/api_config.dart @@ -2,26 +2,24 @@ import 'package:hive/hive.dart'; import 'package:selfprivacy/config/hive_config.dart'; import 'package:selfprivacy/logic/models/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; -import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; +import 'package:selfprivacy/logic/models/server_domain.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; class ApiConfigModel { Box _box = Hive.box(BNames.appConfig); - HetznerServerDetails? get hetznerServer => _hetznerServer; + ServerHostingDetails? get serverDetails => _serverDetails; String? get hetznerKey => _hetznerKey; String? get cloudFlareKey => _cloudFlareKey; - String? get serverDomain => _serverDomain; BackblazeCredential? get backblazeCredential => _backblazeCredential; - CloudFlareDomain? get cloudFlareDomain => _cloudFlareDomain; + ServerDomain? get serverDomain => _serverDomain; BackblazeBucket? get backblazeBucket => _backblazeBucket; String? _hetznerKey; String? _cloudFlareKey; - String? _serverDomain; - HetznerServerDetails? _hetznerServer; + ServerHostingDetails? _serverDetails; BackblazeCredential? _backblazeCredential; - CloudFlareDomain? _cloudFlareDomain; + ServerDomain? _serverDomain; BackblazeBucket? _backblazeBucket; Future storeHetznerKey(String value) async { @@ -34,25 +32,20 @@ class ApiConfigModel { _cloudFlareKey = value; } - Future storeServerDomain(String value) async { - await _box.put(BNames.serverDomain, value); - _serverDomain = value; - } - Future storeBackblazeCredential(BackblazeCredential value) async { await _box.put(BNames.backblazeKey, value); _backblazeCredential = value; } - Future storeCloudFlareDomain(CloudFlareDomain value) async { - await _box.put(BNames.cloudFlareDomain, value); - _cloudFlareDomain = value; + Future storeServerDomain(ServerDomain value) async { + await _box.put(BNames.serverDomain, value); + _serverDomain = value; } - Future storeServerDetails(HetznerServerDetails value) async { - await _box.put(BNames.hetznerServer, value); - _hetznerServer = value; + Future storeServerDetails(ServerHostingDetails value) async { + await _box.put(BNames.serverDetails, value); + _serverDetails = value; } Future storeBackblazeBucket(BackblazeBucket value) async { @@ -64,8 +57,8 @@ class ApiConfigModel { _hetznerKey = null; _cloudFlareKey = null; _backblazeCredential = null; - _cloudFlareDomain = null; - _hetznerServer = null; + _serverDomain = null; + _serverDetails = null; _backblazeBucket = null; } @@ -74,8 +67,8 @@ class ApiConfigModel { _cloudFlareKey = _box.get(BNames.cloudFlareKey); _backblazeCredential = _box.get(BNames.backblazeKey); - _cloudFlareDomain = _box.get(BNames.cloudFlareDomain); - _hetznerServer = _box.get(BNames.hetznerServer); + _serverDomain = _box.get(BNames.serverDomain); + _serverDetails = _box.get(BNames.serverDetails); _backblazeBucket = _box.get(BNames.backblazeBucket); } } diff --git a/lib/logic/models/server_details.dart b/lib/logic/models/server_details.dart index f928ebc8..d12c1b36 100644 --- a/lib/logic/models/server_details.dart +++ b/lib/logic/models/server_details.dart @@ -3,8 +3,8 @@ import 'package:hive/hive.dart'; part 'server_details.g.dart'; @HiveType(typeId: 2) -class HetznerServerDetails { - HetznerServerDetails({ +class ServerHostingDetails { + ServerHostingDetails({ required this.ip4, required this.id, required this.createTime, @@ -26,13 +26,13 @@ class HetznerServerDetails { final DateTime? startTime; @HiveField(4) - final HetznerDataBase dataBase; + final ServerVolume dataBase; @HiveField(5) final String apiToken; - HetznerServerDetails copyWith({DateTime? startTime}) { - return HetznerServerDetails( + ServerHostingDetails copyWith({DateTime? startTime}) { + return ServerHostingDetails( startTime: startTime ?? this.startTime, createTime: createTime, id: id, @@ -46,8 +46,8 @@ class HetznerServerDetails { } @HiveType(typeId: 5) -class HetznerDataBase { - HetznerDataBase({ +class ServerVolume { + ServerVolume({ required this.id, required this.name, }); diff --git a/lib/logic/models/server_details.g.dart b/lib/logic/models/server_details.g.dart index cba8848c..61252506 100644 --- a/lib/logic/models/server_details.g.dart +++ b/lib/logic/models/server_details.g.dart @@ -6,28 +6,28 @@ part of 'server_details.dart'; // TypeAdapterGenerator // ************************************************************************** -class HetznerServerDetailsAdapter extends TypeAdapter { +class HetznerServerDetailsAdapter extends TypeAdapter { @override final int typeId = 2; @override - HetznerServerDetails read(BinaryReader reader) { + ServerHostingDetails read(BinaryReader reader) { final numOfFields = reader.readByte(); final fields = { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; - return HetznerServerDetails( + return ServerHostingDetails( ip4: fields[0] as String, id: fields[1] as int, createTime: fields[3] as DateTime?, - dataBase: fields[4] as HetznerDataBase, + dataBase: fields[4] as ServerVolume, apiToken: fields[5] as String, startTime: fields[2] as DateTime?, ); } @override - void write(BinaryWriter writer, HetznerServerDetails obj) { + void write(BinaryWriter writer, ServerHostingDetails obj) { writer ..writeByte(6) ..writeByte(0) @@ -55,24 +55,24 @@ class HetznerServerDetailsAdapter extends TypeAdapter { typeId == other.typeId; } -class HetznerDataBaseAdapter extends TypeAdapter { +class HetznerDataBaseAdapter extends TypeAdapter { @override final int typeId = 5; @override - HetznerDataBase read(BinaryReader reader) { + ServerVolume read(BinaryReader reader) { final numOfFields = reader.readByte(); final fields = { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; - return HetznerDataBase( + return ServerVolume( id: fields[1] as int, name: fields[2] as String, ); } @override - void write(BinaryWriter writer, HetznerDataBase obj) { + void write(BinaryWriter writer, ServerVolume obj) { writer ..writeByte(2) ..writeByte(1) diff --git a/lib/logic/models/cloudflare_domain.dart b/lib/logic/models/server_domain.dart similarity index 55% rename from lib/logic/models/cloudflare_domain.dart rename to lib/logic/models/server_domain.dart index 9d85bfb1..81525d2f 100644 --- a/lib/logic/models/cloudflare_domain.dart +++ b/lib/logic/models/server_domain.dart @@ -1,12 +1,18 @@ import 'package:hive/hive.dart'; -part 'cloudflare_domain.g.dart'; +part 'server_domain.g.dart'; + +enum DnsProvider { + Unknown, + Cloudflare, +} @HiveType(typeId: 3) -class CloudFlareDomain { - CloudFlareDomain({ +class ServerDomain { + ServerDomain({ required this.domainName, required this.zoneId, + required this.provider, }); @HiveField(0) @@ -15,6 +21,9 @@ class CloudFlareDomain { @HiveField(1) final String zoneId; + @HiveField(2, defaultValue: DnsProvider.Cloudflare) + final DnsProvider provider; + @override String toString() { return '$domainName: $zoneId'; diff --git a/lib/logic/models/cloudflare_domain.g.dart b/lib/logic/models/server_domain.g.dart similarity index 66% rename from lib/logic/models/cloudflare_domain.g.dart rename to lib/logic/models/server_domain.g.dart index dcd95317..c154fba1 100644 --- a/lib/logic/models/cloudflare_domain.g.dart +++ b/lib/logic/models/server_domain.g.dart @@ -1,35 +1,39 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'cloudflare_domain.dart'; +part of 'server_domain.dart'; // ************************************************************************** // TypeAdapterGenerator // ************************************************************************** -class CloudFlareDomainAdapter extends TypeAdapter { +class ServerDomainAdapter extends TypeAdapter { @override final int typeId = 3; @override - CloudFlareDomain read(BinaryReader reader) { + ServerDomain read(BinaryReader reader) { final numOfFields = reader.readByte(); final fields = { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; - return CloudFlareDomain( + return ServerDomain( domainName: fields[0] as String, zoneId: fields[1] as String, + provider: + fields[2] == null ? DnsProvider.Cloudflare : fields[2] as DnsProvider, ); } @override - void write(BinaryWriter writer, CloudFlareDomain obj) { + void write(BinaryWriter writer, ServerDomain obj) { writer - ..writeByte(2) + ..writeByte(3) ..writeByte(0) ..write(obj.domainName) ..writeByte(1) - ..write(obj.zoneId); + ..write(obj.zoneId) + ..writeByte(2) + ..write(obj.provider); } @override @@ -38,7 +42,7 @@ class CloudFlareDomainAdapter extends TypeAdapter { @override bool operator ==(Object other) => identical(this, other) || - other is CloudFlareDomainAdapter && + other is ServerDomainAdapter && runtimeType == other.runtimeType && typeId == other.typeId; } diff --git a/lib/ui/pages/dns_details/dns_details.dart b/lib/ui/pages/dns_details/dns_details.dart index 8973e963..e07cc445 100644 --- a/lib/ui/pages/dns_details/dns_details.dart +++ b/lib/ui/pages/dns_details/dns_details.dart @@ -63,7 +63,7 @@ class _DnsDetailsPageState extends State { @override Widget build(BuildContext context) { var isReady = context.watch().state is AppConfigFinished; - final domain = getIt().cloudFlareDomain?.domainName ?? ''; + final domain = getIt().serverDomain?.domainName ?? ''; var dnsCubit = context.watch().state; print(dnsCubit.dnsState); diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index 895294c7..8c5a78c8 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -129,7 +129,7 @@ class _AppSettingsPageState extends State { Widget deleteServer(BuildContext context) { var isDisabled = - context.watch().state.hetznerServer == null; + context.watch().state.serverDetails == null; return Container( padding: EdgeInsets.only(top: 20, bottom: 5), decoration: BoxDecoration( diff --git a/lib/ui/pages/providers/providers.dart b/lib/ui/pages/providers/providers.dart index dc53ed2f..974f29ce 100644 --- a/lib/ui/pages/providers/providers.dart +++ b/lib/ui/pages/providers/providers.dart @@ -100,7 +100,7 @@ class _Card extends StatelessWidget { AppConfigState appConfig = context.watch().state; var domainName = - appConfig.isDomainFilled ? appConfig.cloudFlareDomain!.domainName : ''; + appConfig.isDomainFilled ? appConfig.serverDomain!.domainName : ''; switch (provider.type) { case ProviderType.server: diff --git a/lib/ui/pages/setup/recovering/recovery_domain.dart b/lib/ui/pages/setup/recovering/recovery_domain.dart index c9546ccc..8ba452d7 100644 --- a/lib/ui/pages/setup/recovering/recovery_domain.dart +++ b/lib/ui/pages/setup/recovering/recovery_domain.dart @@ -1,75 +1,48 @@ +import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart'; +import 'package:selfprivacy/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart'; -class RecoveryDomain extends StatefulWidget { - @override - State createState() => _RecoveryDomainState(); -} - -class _RecoveryDomainState extends State { +class RecoveryDomain extends StatelessWidget { @override Widget build(BuildContext context) { - return BrandHeroScreen( - children: [ - TextField( - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: "recovering.domain_recover_placeholder".tr(), - ), - ), - SizedBox(height: 16), - BrandButton.rised( - onPressed: () {}, - text: "more.continue".tr(), - ), - ], - heroTitle: "recovering.recovery_main_header".tr(), - heroSubtitle: "recovering.domain_recovery_description".tr(), - hasBackButton: true, - hasFlashButton: false, - heroIcon: Icons.link, - ); - } -} + var appConfig = context.watch(); -/*class RecoveryDomain extends StatelessWidget { - @override - Widget build(BuildContext context) { - return SafeArea( - child: Scaffold( - appBar: PreferredSize( - preferredSize: Size.fromHeight(52), - child: BrandHeader(hasBackButton: true), - ), - body: ListView( - padding: EdgeInsets.all(16), + return BlocProvider( + create: (context) => + RecoveryDomainFormCubit(appConfig, FieldCubitFactory(context)), + child: Builder(builder: (context) { + var formCubitState = context.watch().state; + + return BrandHeroScreen( children: [ - Text( - "recovering.recovery_main_header".tr(), - style: Theme.of(context).textTheme.headlineMedium, - ), - SizedBox(height: 18), - Text( - "recovering.domain_recovery_description".tr(), - style: Theme.of(context).textTheme.bodyLarge, - ), - SizedBox(height: 18), - TextField( + CubitFormTextField( + formFieldCubit: + context.read().serverDomainField, decoration: InputDecoration( border: OutlineInputBorder(), labelText: "recovering.domain_recover_placeholder".tr(), ), ), - SizedBox(height: 18), + SizedBox(height: 16), BrandButton.rised( - onPressed: () {}, + onPressed: formCubitState.isSubmitting + ? null + : () => context.read().trySubmit(), text: "more.continue".tr(), ), ], - ), - ), + heroTitle: "recovering.recovery_main_header".tr(), + heroSubtitle: "recovering.domain_recovery_description".tr(), + hasBackButton: true, + hasFlashButton: false, + heroIcon: Icons.link, + ); + }), ); } -}*/ \ No newline at end of file +} diff --git a/lib/utils/ui_helpers.dart b/lib/utils/ui_helpers.dart index 9ce41d0c..734bf4f9 100644 --- a/lib/utils/ui_helpers.dart +++ b/lib/utils/ui_helpers.dart @@ -3,7 +3,6 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; /// it's ui helpers use only for ui components, don't use for logic components. class UiHelpers { - static String getDomainName(AppConfigState config) => config.isDomainFilled - ? config.cloudFlareDomain!.domainName - : 'example.com'; + static String getDomainName(AppConfigState config) => + config.isDomainFilled ? config.serverDomain!.domainName : 'example.com'; }