Refactor infrastructure: cubits and endpoints

Co-authored-by: Inex Code <inex.code@selfprivacy.org>
pull/90/head
NaiJi ✨ 2022-05-13 16:57:56 +03:00
parent 01b1f7462d
commit 4a42733d31
25 changed files with 569 additions and 571 deletions

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -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";
}

View File

@ -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<void> 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<List<DnsRecord>> 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<void> 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<void> setDkim(
String dkimRecordString, CloudFlareDomain cloudFlareDomain) async {
String dkimRecordString, ServerDomain cloudFlareDomain) async {
final domainZoneId = cloudFlareDomain.zoneId;
final url = '$rootAddress/zones/$domainZoneId/dns_records';

View File

@ -68,7 +68,7 @@ class HetznerApi extends ApiMap {
return server == null;
}
Future<HetznerDataBase> createVolume() async {
Future<ServerVolume> 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<HetznerServerDetails> createServer({
Future<ServerHostingDetails> 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<HetznerServerDetails> reset() async {
var server = getIt<ApiConfigModel>().hetznerServer!;
Future<ServerHostingDetails> reset() async {
var server = getIt<ApiConfigModel>().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<HetznerServerDetails> powerOn() async {
var server = getIt<ApiConfigModel>().hetznerServer!;
Future<ServerHostingDetails> powerOn() async {
var server = getIt<ApiConfigModel>().serverDetails!;
var client = await getClient();
await client.post('/servers/${server.id}/actions/poweron');
@ -211,7 +211,7 @@ class HetznerApi extends ApiMap {
Future<Map<String, dynamic>> getMetrics(
DateTime start, DateTime end, String type) async {
var hetznerServer = getIt<ApiConfigModel>().hetznerServer;
var hetznerServer = getIt<ApiConfigModel>().serverDetails;
var client = await getClient();
Map<String, dynamic> queryParameters = {
@ -228,7 +228,7 @@ class HetznerApi extends ApiMap {
}
Future<HetznerServerInfo> getInfo() async {
var hetznerServer = getIt<ApiConfigModel>().hetznerServer;
var hetznerServer = getIt<ApiConfigModel>().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<ApiConfigModel>().hetznerServer;
var hetznerServer = getIt<ApiConfigModel>().serverDetails;
var client = await getClient();
await client.post(
'/servers/${hetznerServer!.id}/actions/change_dns_ptr',

View File

@ -33,65 +33,68 @@ class ApiResponse<D> {
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<ApiConfigModel>().cloudFlareDomain;
var cloudFlareDomain = getIt<ApiConfigModel>().serverDomain;
var domainName = cloudFlareDomain!.domainName;
var apiToken = getIt<ApiConfigModel>().hetznerServer?.apiToken;
var apiToken = getIt<ApiConfigModel>().serverDetails?.apiToken;
options = BaseOptions(baseUrl: 'https://api.$domainName', headers: {
'Authorization': 'Bearer $apiToken',
});
}
if (overrideDomain != null) {
options = BaseOptions(baseUrl: 'https://api.$overrideDomain');
}
return options;
}
Future<String?> 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<bool> 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<ApiResponse<User>> 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<dynamic> 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<ApiResponse<List<String>>> getUsersList() async {
List<String> 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<List<String>>(
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<void>(
statusCode: response.statusCode ?? HttpStatus.internalServerError,
data: null,

View File

@ -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<AppConfigState> {
AppConfigCubit() : super(AppConfigEmpty());
@ -55,180 +26,142 @@ class AppConfigCubit extends Cubit<AppConfigState> {
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<AppConfigState> {
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<AppConfigState> {
Future<void> serverDelete() async {
closeTimer();
if (state.hetznerServer != null) {
await repository.deleteServer(state.cloudFlareDomain!);
if (state.serverDetails != null) {
await repository.deleteServer(state.serverDomain!);
await getIt<SSHModel>().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<AppConfigState> {
.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<AppConfigState> {
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<AppConfigState> {
emit((state as AppConfigNotFinished).copyWith(isLoading: true));
await repository.createServer(
state.rootUser!,
state.cloudFlareDomain!.domainName,
state.serverDomain!.domainName,
state.cloudFlareKey!,
state.backblazeCredential!,
onCancel: onCancel,

View File

@ -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<AppConfigState> load() async {
late AppConfigState res;
final hetznerToken = getIt<ApiConfigModel>().hetznerKey;
final cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey;
final serverDomain = getIt<ApiConfigModel>().serverDomain;
final backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
final serverDetails = getIt<ApiConfigModel>().serverDetails;
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
res = AppConfigFinished(
hetznerKey: getIt<ApiConfigModel>().hetznerKey!,
cloudFlareKey: getIt<ApiConfigModel>().cloudFlareKey!,
cloudFlareDomain: getIt<ApiConfigModel>().cloudFlareDomain!,
backblazeCredential: getIt<ApiConfigModel>().backblazeCredential!,
hetznerServer: getIt<ApiConfigModel>().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<ApiConfigModel>().hetznerKey,
cloudFlareKey: getIt<ApiConfigModel>().cloudFlareKey,
cloudFlareDomain: getIt<ApiConfigModel>().cloudFlareDomain,
backblazeCredential: getIt<ApiConfigModel>().backblazeCredential,
hetznerServer: getIt<ApiConfigModel>().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<ApiConfigModel>().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<HetznerServerDetails> startServer(
HetznerServerDetails hetznerServer,
Future<ServerHostingDetails> 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<void> Function(HetznerServerDetails serverDetails)
required Future<void> 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<void> createDnsRecords(
String ip4,
CloudFlareDomain cloudFlareDomain,
ServerDomain cloudFlareDomain,
) async {
var cloudflareApi = CloudflareApi();
@ -200,7 +234,7 @@ class AppConfigRepository {
);
}
Future<void> createDkimRecord(CloudFlareDomain cloudFlareDomain) async {
Future<void> createDkimRecord(ServerDomain cloudFlareDomain) async {
var cloudflareApi = CloudflareApi();
var api = ServerApi();
@ -220,17 +254,17 @@ class AppConfigRepository {
return isHttpServerWorking;
}
Future<HetznerServerDetails> restart() async {
Future<ServerHostingDetails> restart() async {
var hetznerApi = HetznerApi();
return await hetznerApi.reset();
}
Future<HetznerServerDetails> powerOn() async {
Future<ServerHostingDetails> powerOn() async {
var hetznerApi = HetznerApi();
return await hetznerApi.powerOn();
}
Future<void> saveServerDetails(HetznerServerDetails serverDetails) async {
Future<void> saveServerDetails(ServerHostingDetails serverDetails) async {
await getIt<ApiConfigModel>().storeServerDetails(serverDetails);
}
@ -239,10 +273,6 @@ class AppConfigRepository {
await getIt<ApiConfigModel>().storeHetznerKey(key);
}
Future<void> saveServerDomain(String domain) async {
await getIt<ApiConfigModel>().storeServerDomain(domain);
}
Future<void> saveBackblazeKey(BackblazeCredential backblazeCredential) async {
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
}
@ -251,8 +281,8 @@ class AppConfigRepository {
await getIt<ApiConfigModel>().storeCloudFlareKey(key);
}
Future<void> saveDomain(CloudFlareDomain cloudFlareDomain) async {
await getIt<ApiConfigModel>().storeCloudFlareDomain(cloudFlareDomain);
Future<void> saveDomain(ServerDomain serverDomain) async {
await getIt<ApiConfigModel>().storeServerDomain(serverDomain);
}
Future<void> saveIsServerStarted(bool value) async {
@ -275,12 +305,12 @@ class AppConfigRepository {
await box.put(BNames.hasFinalChecked, value);
}
Future<void> deleteServer(CloudFlareDomain cloudFlareDomain) async {
Future<void> 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<void> deleteRecords() async {
await box.deleteAll([
BNames.hetznerServer,
BNames.serverDetails,
BNames.isServerStarted,
BNames.isServerResetedFirstTime,
BNames.isServerResetedSecondTime,

View File

@ -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<String, bool>? 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);
}

View File

@ -85,9 +85,9 @@ class BackupsCubit extends AppConfigDependendCubit<BackupsState> {
Future<void> 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) {

View File

@ -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<DnsRecordsState> {
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<DnsRecord> records =
await cloudflare.getDnsRecords(cloudFlareDomain: domain);
@ -95,8 +95,8 @@ class DnsRecordsCubit extends AppConfigDependendCubit<DnsRecordsState> {
Future<void> 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(

View File

@ -52,5 +52,14 @@ class FieldCubitFactory {
);
}
FieldCubit<String> createServerDomainField() {
return FieldCubit(
initalValue: '',
validations: [
RequiredStringValidation('validations.required'.tr()),
],
);
}
final BuildContext context;
}

View File

@ -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<DomainSetupState> {
DomainSetupCubit(this.initializingCubit) : super(Initial());
@ -36,9 +36,10 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
var zoneId = await api.getZoneId(domainName);
var domain = CloudFlareDomain(
var domain = ServerDomain(
domainName: domainName,
zoneId: zoneId,
provider: DnsProvider.Cloudflare,
);
initializingCubit.setDomain(domain);

View File

@ -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<String>(
(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<void> onSubmit() async {
initializingCubit.setHetznerKey(apiKey.state.value);
initializingCubit.setDomain(ServerDomain(
domainName: serverDomainField.state.value,
provider: DnsProvider.Unknown,
zoneId: ""));
}
// @override
// FutureOr<bool> asyncValidation() async {
// ; //var client =
// }
final AppConfigCubit initializingCubit;
late final FieldCubit<String> apiKey;
@override
FutureOr<bool> 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<String> serverDomainField;
}

View File

@ -14,7 +14,7 @@ class ServerDetailsCubit extends Cubit<ServerDetailsState> {
ServerDetailsRepository repository = ServerDetailsRepository();
void check() async {
var isReadyToCheck = getIt<ApiConfigModel>().hetznerServer != null;
var isReadyToCheck = getIt<ApiConfigModel>().serverDetails != null;
if (isReadyToCheck) {
emit(ServerDetailsLoading());
var data = await repository.load();

View File

@ -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<void> storeHetznerKey(String value) async {
@ -34,25 +32,20 @@ class ApiConfigModel {
_cloudFlareKey = value;
}
Future<void> storeServerDomain(String value) async {
await _box.put(BNames.serverDomain, value);
_serverDomain = value;
}
Future<void> storeBackblazeCredential(BackblazeCredential value) async {
await _box.put(BNames.backblazeKey, value);
_backblazeCredential = value;
}
Future<void> storeCloudFlareDomain(CloudFlareDomain value) async {
await _box.put(BNames.cloudFlareDomain, value);
_cloudFlareDomain = value;
Future<void> storeServerDomain(ServerDomain value) async {
await _box.put(BNames.serverDomain, value);
_serverDomain = value;
}
Future<void> storeServerDetails(HetznerServerDetails value) async {
await _box.put(BNames.hetznerServer, value);
_hetznerServer = value;
Future<void> storeServerDetails(ServerHostingDetails value) async {
await _box.put(BNames.serverDetails, value);
_serverDetails = value;
}
Future<void> 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);
}
}

View File

@ -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,
});

View File

@ -6,28 +6,28 @@ part of 'server_details.dart';
// TypeAdapterGenerator
// **************************************************************************
class HetznerServerDetailsAdapter extends TypeAdapter<HetznerServerDetails> {
class HetznerServerDetailsAdapter extends TypeAdapter<ServerHostingDetails> {
@override
final int typeId = 2;
@override
HetznerServerDetails read(BinaryReader reader) {
ServerHostingDetails read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
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<HetznerServerDetails> {
typeId == other.typeId;
}
class HetznerDataBaseAdapter extends TypeAdapter<HetznerDataBase> {
class HetznerDataBaseAdapter extends TypeAdapter<ServerVolume> {
@override
final int typeId = 5;
@override
HetznerDataBase read(BinaryReader reader) {
ServerVolume read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
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)

View File

@ -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';

View File

@ -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<CloudFlareDomain> {
class ServerDomainAdapter extends TypeAdapter<ServerDomain> {
@override
final int typeId = 3;
@override
CloudFlareDomain read(BinaryReader reader) {
ServerDomain read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
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<CloudFlareDomain> {
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is CloudFlareDomainAdapter &&
other is ServerDomainAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -63,7 +63,7 @@ class _DnsDetailsPageState extends State<DnsDetailsPage> {
@override
Widget build(BuildContext context) {
var isReady = context.watch<AppConfigCubit>().state is AppConfigFinished;
final domain = getIt<ApiConfigModel>().cloudFlareDomain?.domainName ?? '';
final domain = getIt<ApiConfigModel>().serverDomain?.domainName ?? '';
var dnsCubit = context.watch<DnsRecordsCubit>().state;
print(dnsCubit.dnsState);

View File

@ -129,7 +129,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
Widget deleteServer(BuildContext context) {
var isDisabled =
context.watch<AppConfigCubit>().state.hetznerServer == null;
context.watch<AppConfigCubit>().state.serverDetails == null;
return Container(
padding: EdgeInsets.only(top: 20, bottom: 5),
decoration: BoxDecoration(

View File

@ -100,7 +100,7 @@ class _Card extends StatelessWidget {
AppConfigState appConfig = context.watch<AppConfigCubit>().state;
var domainName =
appConfig.isDomainFilled ? appConfig.cloudFlareDomain!.domainName : '';
appConfig.isDomainFilled ? appConfig.serverDomain!.domainName : '';
switch (provider.type) {
case ProviderType.server:

View File

@ -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<RecoveryDomain> createState() => _RecoveryDomainState();
}
class _RecoveryDomainState extends State<RecoveryDomain> {
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<AppConfigCubit>();
/*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<RecoveryDomainFormCubit>().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<RecoveryDomainFormCubit>().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<RecoveryDomainFormCubit>().trySubmit(),
text: "more.continue".tr(),
),
],
),
),
heroTitle: "recovering.recovery_main_header".tr(),
heroSubtitle: "recovering.domain_recovery_description".tr(),
hasBackButton: true,
hasFlashButton: false,
heroIcon: Icons.link,
);
}),
);
}
}*/
}

View File

@ -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';
}