Compare commits

..

5 Commits

48 changed files with 1137 additions and 610 deletions

6
.gitignore vendored
View File

@ -40,9 +40,3 @@ app.*.symbols
# Obfuscation related
app.*.map.json
# Flatpak
.flatpak-builder/
flatpak-build/
flatpak-repo/
*.flatpak

View File

@ -606,16 +606,5 @@
"reset_onboarding": "Reset onboarding switch",
"reset_onboarding_description": "Reset onboarding switch to show onboarding screen again",
"cubit_statuses": "Cubit loading statuses"
},
"countries": {
"germany": "Germany",
"netherlands": "Netherlands",
"singapore": "Singapore",
"united_kingdom": "United Kingdom",
"canada": "Canada",
"india": "India",
"australia": "Australia",
"united_states": "United States",
"finland": "Finland"
}
}
}

View File

@ -1,6 +1,6 @@
app-id: org.selfprivacy.app
runtime: org.freedesktop.Platform
runtime-version: '23.08'
runtime-version: '22.08'
sdk: org.freedesktop.Sdk
command: selfprivacy
finish-args:
@ -11,7 +11,6 @@ finish-args:
- "--share=network"
- "--own-name=org.selfprivacy.app"
- "--device=dri"
- "--talk-name=org.freedesktop.secrets"
modules:
- name: selfprivacy
buildsystem: simple
@ -36,7 +35,7 @@ modules:
sources:
- type: git
url: https://gitlab.gnome.org/GNOME/libsecret.git
tag: 0.21.4
tag: 0.20.5
- name: libjsoncpp
buildsystem: meson
config-opts:

View File

@ -3,6 +3,7 @@ import 'package:selfprivacy/logic/get_it/api_config.dart';
import 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
import 'package:selfprivacy/logic/get_it/console.dart';
import 'package:selfprivacy/logic/get_it/navigation.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
export 'package:selfprivacy/logic/get_it/api_config.dart';
export 'package:selfprivacy/logic/get_it/api_connection_repository.dart';
@ -15,6 +16,8 @@ Future<void> getItSetup() async {
getIt.registerSingleton<NavigationService>(NavigationService());
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
getIt.registerSingleton<ResourcesModel>(ResourcesModel()..init());
getIt.registerSingleton<WizardDataModel>(WizardDataModel()..init());
getIt.registerSingleton<ApiConfigModel>(ApiConfigModel()..init());
getIt.registerSingleton<ApiConnectionRepository>(

View File

@ -1,13 +1,17 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart';
import 'package:selfprivacy/logic/models/hive/server.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart';
import 'package:selfprivacy/utils/platform_adapter.dart';
class HiveConfig {
@ -19,56 +23,118 @@ class HiveConfig {
Hive.registerAdapter(ServerHostingDetailsAdapter());
Hive.registerAdapter(ServerDomainAdapter());
Hive.registerAdapter(BackupsCredentialAdapter());
Hive.registerAdapter(BackblazeBucketAdapter());
Hive.registerAdapter(ServerProviderVolumeAdapter());
Hive.registerAdapter(UserTypeAdapter());
Hive.registerAdapter(BackblazeBucketAdapter());
Hive.registerAdapter(ServerProviderCredentialAdapter());
Hive.registerAdapter(DnsProviderCredentialAdapter());
Hive.registerAdapter(ServerAdapter());
Hive.registerAdapter(DnsProviderTypeAdapter());
Hive.registerAdapter(ServerProviderTypeAdapter());
Hive.registerAdapter(UserTypeAdapter());
Hive.registerAdapter(BackupsProviderTypeAdapter());
Hive.registerAdapter(ServerInstallationWizardDataAdapter());
await Hive.openBox(BNames.appSettingsBox);
try {
final HiveAesCipher cipher = HiveAesCipher(
await getEncryptedKey(BNames.serverInstallationEncryptionKey),
);
final HiveAesCipher cipher = HiveAesCipher(
await getEncryptedKey(BNames.serverInstallationEncryptionKey),
);
await Hive.openBox<User>(BNames.usersDeprecated);
await Hive.openBox<User>(BNames.usersBox, encryptionCipher: cipher);
await Hive.openBox(BNames.serverInstallationBox, encryptionCipher: cipher);
await Hive.openBox(BNames.resourcesBox, encryptionCipher: cipher);
await Hive.openBox(BNames.wizardDataBox, encryptionCipher: cipher);
final Box<User> deprecatedUsers = Hive.box<User>(BNames.usersDeprecated);
if (deprecatedUsers.isNotEmpty) {
final Box<User> users = Hive.box<User>(BNames.usersBox);
await users.addAll(deprecatedUsers.values.toList());
await deprecatedUsers.clear();
final Box resourcesBox = Hive.box(BNames.resourcesBox);
if (resourcesBox.isEmpty) {
final Box serverInstallationBox = Hive.box(BNames.serverInstallationBox);
final String? serverProviderKey =
serverInstallationBox.get(BNames.hetznerKey);
final String? serverLocation =
serverInstallationBox.get(BNames.serverLocation);
final String? dnsProviderKey =
serverInstallationBox.get(BNames.cloudFlareKey);
final BackupsCredential? backblazeCredential =
serverInstallationBox.get(BNames.backblazeCredential);
final ServerDomain? serverDomain =
serverInstallationBox.get(BNames.serverDomain);
final ServerHostingDetails? serverDetails =
serverInstallationBox.get(BNames.serverDetails);
final BackblazeBucket? backblazeBucket =
serverInstallationBox.get(BNames.backblazeBucket);
final String? serverType =
serverInstallationBox.get(BNames.serverTypeIdentifier);
final ServerProviderType? serverProvider =
serverInstallationBox.get(BNames.serverProvider);
final DnsProviderType? dnsProvider =
serverInstallationBox.get(BNames.dnsProvider);
if (serverProviderKey != null &&
(serverProvider != null ||
(serverDetails != null &&
serverDetails.provider != ServerProviderType.unknown))) {
final ServerProviderCredential serverProviderCredential =
ServerProviderCredential(
tokenId: null,
token: serverProviderKey,
provider: serverProvider ?? serverDetails!.provider,
associatedServerIds: serverDetails != null ? [serverDetails.id] : [],
);
await resourcesBox
.put(BNames.serverProviderTokens, [serverProviderCredential]);
}
await Hive.openBox(
BNames.serverInstallationBox,
encryptionCipher: cipher,
);
} on PlatformException catch (e) {
print('HiveConfig: Error while opening boxes: $e');
rethrow;
if (dnsProviderKey != null &&
(dnsProvider != null ||
(serverDomain != null &&
serverDomain.provider != DnsProviderType.unknown))) {
final DnsProviderCredential dnsProviderCredential =
DnsProviderCredential(
tokenId: null,
token: dnsProviderKey,
provider: dnsProvider ?? serverDomain!.provider,
associatedDomainNames:
serverDomain != null ? [serverDomain.domainName] : [],
);
await resourcesBox
.put(BNames.dnsProviderTokens, [dnsProviderCredential]);
}
if (backblazeCredential != null) {
await resourcesBox
.put(BNames.backupsProviderTokens, [backblazeCredential]);
}
if (backblazeBucket != null) {
await resourcesBox.put(BNames.backblazeBucket, backblazeBucket);
}
if (serverDetails != null && serverDomain != null) {
await resourcesBox.put(BNames.servers, [
Server(
domain: serverDomain,
hostingDetails: serverDetails.copyWith(
serverLocation: serverLocation,
serverType: serverType,
),
),
]);
}
}
}
static Future<Uint8List> getEncryptedKey(final String encKey) async {
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
try {
final bool hasEncryptionKey =
await secureStorage.containsKey(key: encKey);
if (!hasEncryptionKey) {
final List<int> key = Hive.generateSecureKey();
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
}
final String? string = await secureStorage.read(key: encKey);
return base64Url.decode(string!);
} on PlatformException catch (e) {
print('HiveConfig: Error while getting encryption key: $e');
rethrow;
final bool hasEncryptionKey = await secureStorage.containsKey(key: encKey);
if (!hasEncryptionKey) {
final List<int> key = Hive.generateSecureKey();
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
}
final String? string = await secureStorage.read(key: encKey);
return base64Url.decode(string!);
}
}
@ -86,7 +152,7 @@ class BNames {
/// A boolean field of [appSettingsBox] box.
static String isOnboardingShowing = 'isOnboardingShowing';
/// Encryption key to decrypt [serverInstallationBox] and [usersBox] box.
/// Encryption key to decrypt [serverInstallationBox] box.
static String serverInstallationEncryptionKey = 'key';
/// Server installation box. Contains server details and provider tokens.
@ -119,7 +185,7 @@ class BNames {
/// A String field of [serverInstallationBox] box.
static String cloudFlareKey = 'cloudFlareKey';
/// A String field of [serverTypeIdentifier] box.
/// A String field of [serverInstallationBox] box.
static String serverTypeIdentifier = 'serverTypeIdentifier';
/// A [User] field of [serverInstallationBox] box.
@ -146,9 +212,24 @@ class BNames {
/// A boolean field of [serverInstallationBox] box.
static String isRecoveringServer = 'isRecoveringServer';
/// Deprecated users box as it is unencrypted
static String usersDeprecated = 'users';
/// Resources and provider tokens box,
static String resourcesBox = 'resourcesBox';
/// Box with users
static String usersBox = 'usersEncrypted';
/// Server Provider Tokens of [resourcesBox] box.
static String serverProviderTokens = 'serverProviderTokens';
/// DNS Provider Tokens of [resourcesBox] box.
static String dnsProviderTokens = 'dnsProviderTokens';
/// Backups Provider Tokens of [resourcesBox] box.
static String backupsProviderTokens = 'backupsProviderTokens';
/// Servers of [resourcesBox] box.
static String servers = 'servers';
/// Wizard data box
static String wizardDataBox = 'wizardDataBox';
/// Server installation wizard data of [wizardDataBox] box.
static String serverInstallationWizardData = 'serverInstallationWizardData';
}

View File

@ -4,6 +4,7 @@ import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:http/io_client.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/message.dart';
void _logToAppConsole<T>(final T objectToLog) {
@ -117,9 +118,9 @@ abstract class GraphQLApiMap {
String get _token {
String token = '';
final serverDetails = getIt<ApiConfigModel>().serverDetails;
final serverDetails = getIt<ResourcesModel>().serverDetails;
if (serverDetails != null) {
token = getIt<ApiConfigModel>().serverDetails!.apiToken;
token = getIt<ResourcesModel>().serverDetails!.apiToken;
}
return token;
}

View File

@ -9,6 +9,7 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_api.graphq
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/server_settings.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
@ -57,7 +58,7 @@ class ServerApi extends GraphQLApiMap
String customToken;
@override
String? get rootAddress =>
overrideDomain ?? getIt<ApiConfigModel>().serverDomain?.domainName;
overrideDomain ?? getIt<ResourcesModel>().serverDomain?.domainName;
String? overrideDomain;
Future<String?> getApiVersion() async {

View File

@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
@ -39,7 +40,7 @@ class BackblazeApi extends RestApiMap {
);
if (isWithToken) {
final BackupsCredential? backblazeCredential =
getIt<ApiConfigModel>().backblazeCredential;
getIt<ResourcesModel>().backblazeCredential;
final String token = backblazeCredential!.applicationKey;
options.headers = {'Authorization': 'Basic $token'};
}
@ -59,7 +60,7 @@ class BackblazeApi extends RestApiMap {
Future<BackblazeApiAuth> getAuthorizationToken() async {
final Dio client = await getClient();
final BackupsCredential? backblazeCredential =
getIt<ApiConfigModel>().backblazeCredential;
getIt<ResourcesModel>().backblazeCredential;
if (backblazeCredential == null) {
throw Exception('Backblaze credential is null');
}
@ -124,7 +125,7 @@ class BackblazeApi extends RestApiMap {
Future<String> createBucket(final String bucketName) async {
final BackblazeApiAuth auth = await getAuthorizationToken();
final BackupsCredential? backblazeCredential =
getIt<ApiConfigModel>().backblazeCredential;
getIt<ResourcesModel>().backblazeCredential;
final Dio client = await getClient();
client.options.baseUrl = auth.apiUrl;
final Response response = await client.post(
@ -161,7 +162,7 @@ class BackblazeApi extends RestApiMap {
final Response response = await client.post(
'$apiPrefix/b2_create_key',
data: {
'accountId': getIt<ApiConfigModel>().backblazeCredential!.keyId,
'accountId': getIt<ResourcesModel>().backblazeCredential!.keyId,
'bucketId': bucketId,
'capabilities': ['listBuckets', 'listFiles', 'readFiles', 'writeFiles'],
'keyName': 'selfprivacy-restricted-server-key',
@ -192,7 +193,7 @@ class BackblazeApi extends RestApiMap {
final Response response = await client.get(
'$apiPrefix/b2_list_buckets',
queryParameters: {
'accountId': getIt<ApiConfigModel>().backblazeCredential!.keyId,
'accountId': getIt<ResourcesModel>().backblazeCredential!.keyId,
},
options: Options(
headers: {'Authorization': auth.authorizationToken},

View File

@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/json/dns_providers/cloudflare_dns_info.dart';
class CloudflareApi extends RestApiMap {
@ -27,7 +28,7 @@ class CloudflareApi extends RestApiMap {
responseType: ResponseType.json,
);
if (isWithToken) {
final String? token = getIt<ApiConfigModel>().dnsProviderKey;
final String? token = getIt<ResourcesModel>().dnsProviderKey;
assert(token != null);
options.headers = {'Authorization': 'Bearer $token'};
}

View File

@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/json/dns_providers/desec_dns_info.dart';
class DesecApi extends RestApiMap {
@ -27,7 +28,7 @@ class DesecApi extends RestApiMap {
responseType: ResponseType.json,
);
if (isWithToken) {
final String? token = getIt<ApiConfigModel>().dnsProviderKey;
final String? token = getIt<ResourcesModel>().dnsProviderKey;
assert(token != null);
options.headers = {'Authorization': 'Token $token'};
}

View File

@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/json/dns_providers/digital_ocean_dns_info.dart';
class DigitalOceanDnsApi extends RestApiMap {
@ -27,7 +28,7 @@ class DigitalOceanDnsApi extends RestApiMap {
responseType: ResponseType.json,
);
if (isWithToken) {
final String? token = getIt<ApiConfigModel>().dnsProviderKey;
final String? token = getIt<ResourcesModel>().dnsProviderKey;
assert(token != null);
options.headers = {'Authorization': 'Bearer $token'};
}

View File

@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/json/digital_ocean_server_info.dart';
import 'package:selfprivacy/utils/password_generator.dart';
@ -30,7 +31,7 @@ class DigitalOceanApi extends RestApiMap {
responseType: ResponseType.json,
);
if (isWithToken) {
final String? token = getIt<ApiConfigModel>().serverProviderKey;
final String? token = getIt<ResourcesModel>().serverProviderKey;
assert(token != null);
options.headers = {'Authorization': 'Bearer $token'};
}

View File

@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/generic_result.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/rest_api_map.dart';
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
@ -31,7 +32,7 @@ class HetznerApi extends RestApiMap {
responseType: ResponseType.json,
);
if (isWithToken) {
final String? token = getIt<ApiConfigModel>().serverProviderKey;
final String? token = getIt<ResourcesModel>().serverProviderKey;
assert(token != null);
options.headers = {'Authorization': 'Bearer $token'};
}

View File

@ -6,6 +6,7 @@ import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
@ -108,7 +109,7 @@ class BackupsBloc extends Bloc<BackupsEvent, BackupsState> {
final BackupsServerLoaded event,
final Emitter<BackupsState> emit,
) async {
BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
BackblazeBucket? bucket = getIt<ResourcesModel>().backblazeBucket;
final backups = getIt<ApiConnectionRepository>().apiData.backups;
final backupConfig = getIt<ApiConnectionRepository>().apiData.backupConfig;
if (backupConfig.data == null || backups.data == null) {
@ -227,7 +228,7 @@ class BackupsBloc extends Bloc<BackupsEvent, BackupsState> {
emit(BackupsUnititialized());
return;
}
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
final BackblazeBucket? bucket = getIt<ResourcesModel>().backblazeBucket;
emit(
BackupsInitialized(
backblazeBucket: bucket,

View File

@ -2,6 +2,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/providers/providers_controller.dart';
class MetricsLoadException implements Exception {
@ -30,7 +31,7 @@ class MetricsRepository {
break;
}
final serverId = getIt<ApiConfigModel>().serverDetails!.id;
final serverId = getIt<ResourcesModel>().serverDetails!.id;
final result = await ProvidersController.currentServerProvider!.getMetrics(
serverId,
start,

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/cubit/server_connection_dependent/server_connection_dependent_cubit.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/logic/models/ssh_settings.dart';
@ -43,7 +44,7 @@ class ServerDetailsCubit
final serverProviderApi = ProvidersController.currentServerProvider;
final dnsProviderApi = ProvidersController.currentDnsProvider;
if (serverProviderApi != null && dnsProviderApi != null) {
final serverId = getIt<ApiConfigModel>().serverDetails?.id ?? 0;
final serverId = getIt<ResourcesModel>().serverDetails?.id ?? 0;
final metadataResult = await serverProviderApi.getMetadata(serverId);
metadataResult.data.add(
ServerMetadataEntity(
@ -60,7 +61,7 @@ class ServerDetailsCubit
}
void check() async {
final bool isReadyToCheck = getIt<ApiConfigModel>().serverDetails != null;
final bool isReadyToCheck = getIt<ResourcesModel>().serverDetails != null;
try {
if (isReadyToCheck) {
emit(const ServerDetailsLoading());

View File

@ -15,6 +15,7 @@ import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart';
import 'package:selfprivacy/logic/models/launch_installation_data.dart';
import 'package:selfprivacy/logic/models/price.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
@ -222,12 +223,14 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
provider: BackupsProviderType.backblaze,
);
final BackblazeBucket? bucket;
await repository.saveBackblazeKey(backblazeCredential);
await repository.saveBackupsCredential(backblazeCredential);
if (state is ServerInstallationRecovery) {
final configuration = await ServerApi(
customToken:
(state as ServerInstallationRecovery).serverDetails!.apiToken,
isWithToken: true,
overrideDomain:
(state as ServerInstallationRecovery).serverDomain!.domainName,
).getBackupsConfiguration();
if (configuration != null) {
try {
@ -401,7 +404,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
);
timer = Timer(pauseDuration, () async {
final ServerHostingDetails serverDetails = await repository.restart();
await repository.saveIsServerResetedFirstTime(true);
await repository.saveIsServerRebootedFirstTime(true);
await repository.saveServerDetails(serverDetails);
final ServerInstallationNotFinished newState = dataState.copyWith(
@ -442,7 +445,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
);
timer = Timer(pauseDuration, () async {
final ServerHostingDetails serverDetails = await repository.restart();
await repository.saveIsServerResetedSecondTime(true);
await repository.saveIsServerRebootedSecondTime(true);
await repository.saveServerDetails(serverDetails);
final ServerInstallationNotFinished newState = dataState.copyWith(
@ -577,10 +580,12 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final ServerProviderType serverProvider = await ServerApi(
customToken: serverDetails.apiToken,
isWithToken: true,
overrideDomain: serverDomain.domainName,
).getServerProviderType();
final dnsProvider = await ServerApi(
customToken: serverDetails.apiToken,
isWithToken: true,
overrideDomain: serverDomain.domainName,
).getDnsProviderType();
if (serverProvider == ServerProviderType.unknown ||
dnsProvider == DnsProviderType.unknown) {
@ -762,6 +767,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final dnsProviderType = await ServerApi(
customToken: dataState.serverDetails!.apiToken,
isWithToken: true,
overrideDomain: serverDomain.domainName,
).getDnsProviderType();
await repository.saveDomain(
ServerDomain(
@ -769,6 +775,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
provider: dnsProviderType,
),
);
await repository.setDnsApiToken(token);
emit(
dataState.copyWith(
serverDomain: ServerDomain(
@ -785,8 +792,8 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final BackupsCredential backblazeCredential,
) async {
await repository.saveIsServerStarted(true);
await repository.saveIsServerResetedFirstTime(true);
await repository.saveIsServerResetedSecondTime(true);
await repository.saveIsServerRebootedFirstTime(true);
await repository.saveIsServerRebootedSecondTime(true);
await repository.saveHasFinalChecked(true);
await repository.saveIsRecoveringServer(false);
final serverType = await ProvidersController.currentServerProvider!
@ -794,12 +801,9 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
await repository.saveServerType(serverType.data!);
await ProvidersController.currentServerProvider!
.trySetServerLocation(serverType.data!.location.identifier);
final User mainUser = await repository.getMainUser();
await repository.saveRootUser(mainUser);
final ServerInstallationRecovery updatedState =
(state as ServerInstallationRecovery).copyWith(
backblazeCredential: backblazeCredential,
rootUser: mainUser,
serverTypeIdentificator: serverType.data!.identifier,
);
emit(updatedState.finish());

View File

@ -9,10 +9,15 @@ import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart';
import 'package:selfprivacy/logic/models/hive/server.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart';
import 'package:selfprivacy/logic/models/json/device_token.dart';
import 'package:selfprivacy/logic/models/json/dns_records.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
@ -34,22 +39,25 @@ class ServerAuthorizationException implements Exception {
class ServerInstallationRepository {
Box box = Hive.box(BNames.serverInstallationBox);
Box<User> usersBox = Hive.box(BNames.usersBox);
Future<ServerInstallationState> load() async {
final String? providerApiToken = getIt<ApiConfigModel>().serverProviderKey;
final String? location = getIt<ApiConfigModel>().serverLocation;
final String? dnsApiToken = getIt<ApiConfigModel>().dnsProviderKey;
final String? serverTypeIdentificator = getIt<ApiConfigModel>().serverType;
final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
final DnsProviderType? dnsProvider = getIt<ApiConfigModel>().dnsProvider;
final ServerInstallationWizardData? wizardData =
getIt<WizardDataModel>().serverInstallation;
final List<Server> servers = getIt<ResourcesModel>().servers;
final String? providerApiToken = getIt<ResourcesModel>().serverProviderKey;
final String? location = getIt<ResourcesModel>().serverLocation;
final String? dnsApiToken = getIt<ResourcesModel>().dnsProviderKey;
final String? serverTypeIdentificator = getIt<ResourcesModel>().serverType;
final ServerDomain? serverDomain = getIt<ResourcesModel>().serverDomain;
final DnsProviderType? dnsProvider = getIt<ResourcesModel>().dnsProvider;
final ServerProviderType? serverProvider =
getIt<ApiConfigModel>().serverProvider;
getIt<ResourcesModel>().serverProvider;
final BackupsCredential? backblazeCredential =
getIt<ApiConfigModel>().backblazeCredential;
getIt<ResourcesModel>().backblazeCredential;
final ServerHostingDetails? serverDetails =
getIt<ApiConfigModel>().serverDetails;
getIt<ResourcesModel>().serverDetails;
// TODO: Init server providers in another place
if (serverProvider != null ||
(serverDetails != null &&
serverDetails.provider != ServerProviderType.unknown)) {
@ -73,85 +81,48 @@ class ServerInstallationRepository {
);
}
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
TlsOptions.verifyCertificate = true;
if (serverTypeIdentificator == null && serverDetails != null) {
final finalServerType = await ProvidersController.currentServerProvider!
.getServerType(serverDetails.id);
await saveServerType(finalServerType.data!);
await ProvidersController.currentServerProvider!
.trySetServerLocation(finalServerType.data!.location.identifier);
return ServerInstallationFinished(
installationDialoguePopUp: null,
providerApiToken: providerApiToken!,
serverTypeIdentificator: finalServerType.data!.identifier,
dnsApiToken: dnsApiToken!,
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),
// If we don't have any wizard data, we either have a server set up, or we are starting from scratch
// This behaviour shall change when we introduce multitenancy
if (wizardData == null) {
if (servers.isEmpty) {
// We don't have anything set up, so we start from scratch
return ServerInstallationNotFinished.fromWizardData(
ServerInstallationWizardData.empty(),
);
} else {
// We have a server set up, so we load it
TlsOptions.verifyCertificate = true;
return ServerInstallationFinished(
installationDialoguePopUp: null,
providerApiToken: providerApiToken!,
serverTypeIdentificator: serverTypeIdentificator!,
dnsApiToken: dnsApiToken!,
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),
);
}
}
if (box.get(BNames.isRecoveringServer, defaultValue: false) &&
serverDomain != null) {
if (wizardData.isRecoveringServer && wizardData.serverDomain != null) {
return ServerInstallationRecovery(
providerApiToken: providerApiToken,
dnsApiToken: dnsApiToken,
serverDomain: serverDomain,
serverTypeIdentificator: serverTypeIdentificator,
backblazeCredential: backblazeCredential,
serverDetails: serverDetails,
rootUser: box.get(BNames.rootUser),
providerApiToken: wizardData.serverProviderKey,
dnsApiToken: wizardData.dnsProviderKey,
serverDomain: wizardData.serverDomain,
serverTypeIdentificator: wizardData.serverTypeIdentifier,
backblazeCredential: wizardData.backupsCredential,
serverDetails: wizardData.serverDetails,
currentStep: _getCurrentRecoveryStep(
providerApiToken,
dnsApiToken,
serverDomain,
serverDetails,
wizardData.serverProviderKey,
wizardData.dnsProviderKey,
wizardData.serverDomain!,
wizardData.serverDetails,
),
recoveryCapabilities: await getRecoveryCapabilities(serverDomain),
recoveryCapabilities:
await getRecoveryCapabilities(wizardData.serverDomain!),
);
}
return ServerInstallationNotFinished(
providerApiToken: providerApiToken,
dnsApiToken: dnsApiToken,
serverDomain: serverDomain,
serverTypeIdentificator: serverTypeIdentificator,
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,
customSshKey: null,
);
return ServerInstallationNotFinished.fromWizardData(wizardData);
}
RecoveryStep _getCurrentRecoveryStep(
@ -177,7 +148,9 @@ class ServerInstallationRepository {
void clearAppConfig() {
box.clear();
usersBox.clear();
getIt<ResourcesModel>().clear();
getIt<WizardDataModel>().clear();
getIt<ApiConnectionRepository>().clear();
}
Future<ServerHostingDetails> startServer(
@ -216,7 +189,12 @@ class ServerInstallationRepository {
}
Future<void> createDkimRecord(final ServerDomain domain) async {
final ServerApi api = ServerApi();
final ServerApi api = ServerApi(
overrideDomain: domain.domainName,
customToken:
getIt<WizardDataModel>().serverInstallation!.serverDetails!.apiToken,
isWithToken: true,
);
late DnsRecord record;
try {
@ -233,14 +211,26 @@ class ServerInstallationRepository {
}
Future<bool> isHttpServerWorking() async {
final ServerApi api = ServerApi();
final ServerApi api = ServerApi(
overrideDomain:
getIt<WizardDataModel>().serverInstallation!.serverDomain!.domainName,
customToken:
getIt<WizardDataModel>().serverInstallation!.serverDetails!.apiToken,
isWithToken: true,
);
return api.isHttpServerWorking();
}
Future<ServerHostingDetails> restart() async {
final server = getIt<ApiConfigModel>().serverDetails!;
final server = getIt<WizardDataModel>().serverInstallation!.serverDetails!;
final result = await ServerApi().reboot();
final result = await ServerApi(
overrideDomain:
getIt<WizardDataModel>().serverInstallation!.serverDomain!.domainName,
customToken:
getIt<WizardDataModel>().serverInstallation!.serverDetails!.apiToken,
isWithToken: true,
).reboot();
if (result.success && result.data != null) {
server.copyWith(startTime: result.data);
@ -252,7 +242,7 @@ class ServerInstallationRepository {
}
Future<ServerHostingDetails> powerOn() async {
final server = getIt<ApiConfigModel>().serverDetails!;
final server = getIt<ResourcesModel>().serverDetails!;
return startServer(server);
}
@ -436,174 +426,118 @@ class ServerInstallationRepository {
);
}
Future<User> getMainUser() async {
final ServerApi serverApi = ServerApi();
const User fallbackUser = User(
isFoundOnServer: false,
type: UserType.primary,
note: "Couldn't find main user on server, API is outdated",
login: 'UNKNOWN',
sshKeys: [],
);
final String? serverApiVersion = await serverApi.getApiVersion();
final users = await serverApi.getAllUsers();
if (serverApiVersion == null || users.isEmpty) {
return fallbackUser;
}
try {
final Version parsedVersion = Version.parse(serverApiVersion);
if (!VersionConstraint.parse('>=1.2.5').allows(parsedVersion)) {
return fallbackUser;
}
return users.firstWhere(
(final User user) => user.type == UserType.primary,
);
} on FormatException {
return fallbackUser;
}
}
Future<List<ServerBasicInfo>> getServersOnProviderAccount() async =>
(await ProvidersController.currentServerProvider!.getServers()).data;
Future<void> saveServerDetails(
final ServerHostingDetails serverDetails,
) async {
await getIt<ApiConfigModel>().setServerDetails(serverDetails);
await getIt<WizardDataModel>().setServerDetails(serverDetails);
}
Future<void> deleteServerDetails() async {
await box.delete(BNames.serverDetails);
getIt<ApiConfigModel>().init();
await getIt<WizardDataModel>().deleteServerDetails();
}
Future<void> saveServerProviderType(final ServerProviderType type) async {
await getIt<ApiConfigModel>().storeServerProviderType(type);
await getIt<WizardDataModel>().setServerProviderType(type);
}
Future<void> saveDnsProviderType(final DnsProviderType type) async {
await getIt<ApiConfigModel>().setDnsProviderType(type);
await getIt<WizardDataModel>().setDnsProviderType(type);
}
Future<void> saveServerProviderKey(final String key) async {
await getIt<ApiConfigModel>().setServerProviderKey(key);
await getIt<WizardDataModel>().setServerProviderKey(key);
await getIt<ResourcesModel>().addServerProviderToken(
ServerProviderCredential(
tokenId: null,
token: key,
provider:
getIt<WizardDataModel>().serverInstallation!.serverProviderType!,
associatedServerIds: [],
),
);
}
Future<void> saveServerType(final ServerType serverType) async {
await getIt<ApiConfigModel>().setServerTypeIdentifier(
await getIt<WizardDataModel>().setServerTypeIdentifier(
serverType.identifier,
);
await getIt<ApiConfigModel>().setServerLocation(
await getIt<WizardDataModel>().setServerLocation(
serverType.location.identifier,
);
}
Future<void> deleteServerProviderKey() async {
await box.delete(BNames.hetznerKey);
getIt<ApiConfigModel>().init();
}
Future<void> saveBackblazeKey(
final BackupsCredential backblazeCredential,
Future<void> saveBackupsCredential(
final BackupsCredential backupsCredential,
) async {
await getIt<ApiConfigModel>().setBackblazeCredential(backblazeCredential);
}
Future<void> deleteBackblazeKey() async {
await box.delete(BNames.backblazeCredential);
getIt<ApiConfigModel>().init();
await getIt<WizardDataModel>().setBackupsCredential(backupsCredential);
}
Future<void> setDnsApiToken(final String key) async {
await getIt<ApiConfigModel>().setDnsProviderKey(key);
}
Future<void> deleteDnsProviderKey() async {
await box.delete(BNames.cloudFlareKey);
getIt<ApiConfigModel>().init();
await getIt<WizardDataModel>().setDnsProviderKey(key);
await getIt<ResourcesModel>().addDnsProviderToken(
DnsProviderCredential(
tokenId: null,
token: key,
provider: getIt<WizardDataModel>().serverInstallation!.dnsProviderType!,
associatedDomainNames: [],
),
);
}
Future<void> saveDomain(final ServerDomain serverDomain) async {
await getIt<ApiConfigModel>().setServerDomain(serverDomain);
await getIt<WizardDataModel>().setServerDomain(serverDomain);
}
Future<void> deleteDomain() async {
await box.delete(BNames.serverDomain);
getIt<ApiConfigModel>().init();
await getIt<WizardDataModel>().deleteServerDomain();
}
Future<void> saveIsServerStarted(final bool value) async {
await box.put(BNames.isServerStarted, value);
await getIt<WizardDataModel>().setIsServerStarted(value);
}
Future<void> saveIsServerResetedFirstTime(final bool value) async {
await box.put(BNames.isServerResetedFirstTime, value);
Future<void> saveIsServerRebootedFirstTime(final bool value) async {
await getIt<WizardDataModel>().setIsServerRebootedFirstTime(value);
}
Future<void> saveIsServerResetedSecondTime(final bool value) async {
await box.put(BNames.isServerResetedSecondTime, value);
Future<void> saveIsServerRebootedSecondTime(final bool value) async {
await getIt<WizardDataModel>().setIsServerRebootedSecondTime(value);
}
Future<void> saveRootUser(final User rootUser) async {
await box.put(BNames.rootUser, rootUser);
await getIt<WizardDataModel>().setRootUser(rootUser);
}
Future<void> saveIsRecoveringServer(final bool value) async {
await box.put(BNames.isRecoveringServer, value);
await getIt<WizardDataModel>().setIsRecoveringServer(value);
}
Future<void> saveHasFinalChecked(final bool value) async {
await box.put(BNames.hasFinalChecked, value);
}
Future<bool> deleteServer(final ServerDomain serverDomain) async {
final ServerApi api = ServerApi();
final dnsRecords = await api.getDnsRecords();
final GenericResult<void> removalResult =
await ProvidersController.currentDnsProvider!.removeDomainRecords(
domain: serverDomain,
records: dnsRecords,
// We are finished here. Time to save the state and finish the wizard
// TODO: A lot of null checks are skipped here. Implication that every value exists might become false in the future.
// TODO: We would actually want to handle token creation elsewhere.
final ServerInstallationWizardData wizardData =
getIt<WizardDataModel>().serverInstallation!;
await getIt<ResourcesModel>().addServer(
Server(
hostingDetails: wizardData.serverDetails!,
domain: wizardData.serverDomain!,
),
);
if (!removalResult.success) {
getIt<NavigationService>().showSnackBar(
'modals.dns_removal_error'.tr(),
);
return false;
}
final deletionResult =
await ProvidersController.currentServerProvider!.deleteServer(
serverDomain.domainName,
await getIt<ResourcesModel>().associateServerWithToken(
wizardData.serverDetails!.id,
wizardData.serverProviderKey!,
);
if (!deletionResult.success) {
getIt<NavigationService>().showSnackBar(
'modals.server_validators_error'.tr(),
);
return false;
}
await box.put(BNames.hasFinalChecked, false);
await box.put(BNames.isServerStarted, false);
await box.put(BNames.isServerResetedFirstTime, false);
await box.put(BNames.isServerResetedSecondTime, false);
await box.put(BNames.isLoading, false);
await box.put(BNames.serverDetails, null);
return true;
}
Future<void> deleteServerRelatedRecords() async {
await box.deleteAll([
BNames.serverDetails,
BNames.isServerStarted,
BNames.isServerResetedFirstTime,
BNames.isServerResetedSecondTime,
BNames.hasFinalChecked,
BNames.isLoading,
]);
getIt<ApiConfigModel>().init();
await getIt<ResourcesModel>().associateDomainWithToken(
wizardData.serverDomain!.domainName,
wizardData.dnsProviderKey!,
);
await getIt<ResourcesModel>().addBackupsCredential(
wizardData.backupsCredential!,
);
await getIt<WizardDataModel>().clearServerInstallation();
}
}

View File

@ -49,11 +49,11 @@ abstract class ServerInstallationState extends Equatable {
bool get isPrimaryUserFilled => rootUser != null;
bool get isServerCreated => serverDetails != null;
bool get isFullyInitilized => _fulfilementList.every((final el) => el!);
bool get isFullyInitialized => _fulfillmentList.every((final el) => el!);
ServerSetupProgress get progress => ServerSetupProgress
.values[_fulfilementList.where((final el) => el!).length];
.values[_fulfillmentList.where((final el) => el!).length];
int get porgressBar {
int get progressBar {
if (progress.index < 6) {
return progress.index;
} else if (progress.index < 10) {
@ -63,7 +63,7 @@ abstract class ServerInstallationState extends Equatable {
}
}
List<bool?> get _fulfilementList {
List<bool?> get _fulfillmentList {
final List<bool> res = [
isServerProviderApiKeyFilled,
isServerTypeFilled,
@ -118,7 +118,7 @@ class TimerState extends ServerInstallationNotFinished {
enum ServerSetupProgress {
nothingYet,
serverProviderFilled,
servertTypeFilled,
serverTypeFilled,
dnsProviderFilled,
backblazeFilled,
domainFilled,
@ -146,6 +146,26 @@ class ServerInstallationNotFinished extends ServerInstallationState {
super.serverDetails,
super.installationDialoguePopUp,
});
ServerInstallationNotFinished.fromWizardData(
final ServerInstallationWizardData data,
) : this(
providerApiToken: data.serverProviderKey,
dnsApiToken: data.dnsProviderKey,
serverDomain: data.serverDomain,
serverTypeIdentificator: data.serverTypeIdentifier,
backblazeCredential: data.backupsCredential,
serverDetails: data.serverDetails,
rootUser: data.rootUser,
isServerStarted: data.isServerStarted,
isServerResetedFirstTime: data.isServerResetedFirstTime,
isServerResetedSecondTime: data.isServerResetedSecondTime,
isLoading: data.isLoading,
dnsMatches: null,
customSshKey: null,
installationDialoguePopUp: null,
);
final bool isLoading;
final Map<String, DnsRecordStatus>? dnsMatches;
final String? customSshKey;
@ -210,12 +230,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
dnsApiToken: dnsApiToken!,
backblazeCredential: backblazeCredential!,
serverDomain: serverDomain!,
rootUser: rootUser!,
serverDetails: serverDetails!,
isServerStarted: isServerStarted,
isServerResetedFirstTime: isServerResetedFirstTime,
isServerResetedSecondTime: isServerResetedSecondTime,
installationDialoguePopUp: installationDialoguePopUp,
);
}
@ -246,13 +261,14 @@ class ServerInstallationFinished extends ServerInstallationState {
required String super.dnsApiToken,
required BackupsCredential super.backblazeCredential,
required ServerDomain super.serverDomain,
required User super.rootUser,
required ServerHostingDetails super.serverDetails,
required super.isServerStarted,
required super.isServerResetedFirstTime,
required super.isServerResetedSecondTime,
required super.installationDialoguePopUp,
});
}) : super(
rootUser: null,
isServerStarted: true,
isServerResetedFirstTime: true,
isServerResetedSecondTime: true,
installationDialoguePopUp: null,
);
@override
List<Object?> get props => [
@ -301,9 +317,9 @@ class ServerInstallationRecovery extends ServerInstallationState {
super.dnsApiToken,
super.backblazeCredential,
super.serverDomain,
super.rootUser,
super.serverDetails,
}) : super(
rootUser: null,
isServerStarted: true,
isServerResetedFirstTime: true,
isServerResetedSecondTime: true,
@ -333,7 +349,6 @@ class ServerInstallationRecovery extends ServerInstallationState {
final String? dnsApiToken,
final BackupsCredential? backblazeCredential,
final ServerDomain? serverDomain,
final User? rootUser,
final ServerHostingDetails? serverDetails,
final RecoveryStep? currentStep,
final ServerRecoveryCapabilities? recoveryCapabilities,
@ -345,7 +360,6 @@ class ServerInstallationRecovery extends ServerInstallationState {
dnsApiToken: dnsApiToken ?? this.dnsApiToken,
backblazeCredential: backblazeCredential ?? this.backblazeCredential,
serverDomain: serverDomain ?? this.serverDomain,
rootUser: rootUser ?? this.rootUser,
serverDetails: serverDetails ?? this.serverDetails,
currentStep: currentStep ?? this.currentStep,
recoveryCapabilities: recoveryCapabilities ?? this.recoveryCapabilities,
@ -357,11 +371,6 @@ class ServerInstallationRecovery extends ServerInstallationState {
dnsApiToken: dnsApiToken!,
backblazeCredential: backblazeCredential!,
serverDomain: serverDomain!,
rootUser: rootUser!,
serverDetails: serverDetails!,
isServerStarted: true,
isServerResetedFirstTime: true,
isServerResetedSecondTime: true,
installationDialoguePopUp: null,
);
}

View File

@ -1,117 +1,27 @@
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
class ApiConfigModel {
final Box _box = Hive.box(BNames.serverInstallationBox);
ServerHostingDetails? get serverDetails => _serverDetails;
String? get localeCode => _localeCode;
String? get serverProviderKey => _serverProviderKey;
String? get serverLocation => _serverLocation;
String? get serverType => _serverType;
String? get dnsProviderKey => _dnsProviderKey;
ServerProviderType? get serverProvider => _serverProvider;
DnsProviderType? get dnsProvider => _dnsProvider;
BackupsCredential? get backblazeCredential => _backblazeCredential;
ServerDomain? get serverDomain => _serverDomain;
BackblazeBucket? get backblazeBucket => _backblazeBucket;
String? _localeCode;
String? _serverProviderKey;
String? _serverLocation;
String? _dnsProviderKey;
String? _serverType;
ServerProviderType? _serverProvider;
DnsProviderType? _dnsProvider;
ServerHostingDetails? _serverDetails;
BackupsCredential? _backblazeCredential;
ServerDomain? _serverDomain;
BackblazeBucket? _backblazeBucket;
Future<void> setLocaleCode(final String value) async {
_localeCode = value;
}
Future<void> storeServerProviderType(final ServerProviderType value) async {
await _box.put(BNames.serverProvider, value);
_serverProvider = value;
}
Future<void> setDnsProviderType(final DnsProviderType value) async {
await _box.put(BNames.dnsProvider, value);
_dnsProvider = value;
}
Future<void> setServerProviderKey(final String value) async {
await _box.put(BNames.hetznerKey, value);
_serverProviderKey = value;
}
Future<void> setDnsProviderKey(final String value) async {
await _box.put(BNames.cloudFlareKey, value);
_dnsProviderKey = value;
}
Future<void> setServerTypeIdentifier(final String typeIdentifier) async {
await _box.put(BNames.serverTypeIdentifier, typeIdentifier);
_serverType = typeIdentifier;
}
Future<void> setServerLocation(final String serverLocation) async {
await _box.put(BNames.serverLocation, serverLocation);
_serverLocation = serverLocation;
}
Future<void> setBackblazeCredential(final BackupsCredential value) async {
await _box.put(BNames.backblazeCredential, value);
_backblazeCredential = value;
}
Future<void> setServerDomain(final ServerDomain value) async {
await _box.put(BNames.serverDomain, value);
_serverDomain = value;
}
Future<void> setServerDetails(final ServerHostingDetails value) async {
await _box.put(BNames.serverDetails, value);
_serverDetails = value;
}
Future<void> setBackblazeBucket(final BackblazeBucket value) async {
await _box.put(BNames.backblazeBucket, value);
_backblazeBucket = value;
}
void clear() {
_localeCode = null;
_serverProviderKey = null;
_dnsProvider = null;
_serverLocation = null;
_dnsProviderKey = null;
_backblazeCredential = null;
_serverDomain = null;
_serverDetails = null;
_backblazeBucket = null;
_serverType = null;
_serverProvider = null;
}
void init() {
_localeCode = 'en';
_serverProviderKey = _box.get(BNames.hetznerKey);
_serverLocation = _box.get(BNames.serverLocation);
_dnsProviderKey = _box.get(BNames.cloudFlareKey);
_backblazeCredential = _box.get(BNames.backblazeCredential);
_serverDomain = _box.get(BNames.serverDomain);
_serverDetails = _box.get(BNames.serverDetails);
_backblazeBucket = _box.get(BNames.backblazeBucket);
_serverType = _box.get(BNames.serverTypeIdentifier);
_serverProvider = _box.get(BNames.serverProvider);
_dnsProvider = _box.get(BNames.dnsProvider);
}
}

View File

@ -6,6 +6,7 @@ import 'package:pub_semver/pub_semver.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/backup.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
@ -246,14 +247,12 @@ class ApiConnectionRepository {
}
ServerHostingDetails? get serverDetails =>
getIt<ApiConfigModel>().serverDetails;
ServerDomain? get serverDomain => getIt<ApiConfigModel>().serverDomain;
getIt<ResourcesModel>().serverDetails;
ServerDomain? get serverDomain => getIt<ResourcesModel>().serverDomain;
void init() async {
final serverDetails = getIt<ApiConfigModel>().serverDetails;
final hasFinalChecked =
box.get(BNames.hasFinalChecked, defaultValue: false);
if (serverDetails == null || !hasFinalChecked) {
final serverDetails = getIt<ResourcesModel>().serverDetails;
if (serverDetails == null) {
return;
}
connectionStatus = ConnectionStatus.reconnecting;
@ -281,6 +280,12 @@ class ApiConnectionRepository {
);
}
void clear() async {
connectionStatus = ConnectionStatus.nonexistent;
_connectionStatusStream.add(connectionStatus);
_timer?.cancel();
}
Future<void> _refetchEverything(final Version version) async {
await _apiData.serverJobs
.refetchData(version, () => _dataStream.add(_apiData));
@ -302,7 +307,7 @@ class ApiConnectionRepository {
}
Future<void> reload(final Timer? timer) async {
final serverDetails = getIt<ApiConfigModel>().serverDetails;
final serverDetails = getIt<ResourcesModel>().serverDetails;
if (serverDetails == null) {
return;
}

View File

@ -0,0 +1,314 @@
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/hive/dns_provider_credential.dart';
import 'package:selfprivacy/logic/models/hive/server.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/server_provider_credential.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/hive/wizards_data/server_installation_wizard_data.dart';
class ResourcesModel {
final Box _box = Hive.box(BNames.resourcesBox);
List<ServerProviderCredential> get serverProviderCredentials =>
_serverProviderTokens;
List<DnsProviderCredential> get dnsProviderCredentials => _dnsProviderTokens;
List<BackupsCredential> get backupsCredentials => _backupsCredentials;
List<Server> get servers => _servers;
BackblazeBucket? get backblazeBucket => _backblazeBucket;
List<ServerProviderCredential> _serverProviderTokens = [];
List<DnsProviderCredential> _dnsProviderTokens = [];
List<BackupsCredential> _backupsCredentials = [];
List<Server> _servers = [];
// TODO: As we will add support for other backup storages, we should
// refactor this.
BackblazeBucket? _backblazeBucket;
@Deprecated('Compatibility getter')
ServerHostingDetails? get serverDetails =>
_servers.firstOrNull?.hostingDetails;
@Deprecated('Compatibility getter')
String? get serverProviderKey => _serverProviderTokens.firstOrNull?.token;
@Deprecated('Compatibility getter')
String? get serverLocation =>
_servers.firstOrNull?.hostingDetails.serverLocation;
@Deprecated('Compatibility getter')
String? get serverType => _servers.firstOrNull?.hostingDetails.serverType;
@Deprecated('Compatibility getter')
String? get dnsProviderKey => _dnsProviderTokens.firstOrNull?.token;
@Deprecated('Compatibility getter')
ServerProviderType? get serverProvider =>
_serverProviderTokens.firstOrNull?.provider;
@Deprecated('Compatibility getter')
DnsProviderType? get dnsProvider => _dnsProviderTokens.firstOrNull?.provider;
@Deprecated('Compatibility getter')
BackupsCredential? get backblazeCredential => _backupsCredentials.firstOrNull;
@Deprecated('Compatibility getter')
ServerDomain? get serverDomain => _servers.firstOrNull?.domain;
Future<void> addServerProviderToken(
final ServerProviderCredential token,
) async {
_serverProviderTokens.add(token);
await _box.put(BNames.serverProviderTokens, _serverProviderTokens);
}
Future<void> associateServerWithToken(
final int serverId,
final String token,
) async {
_serverProviderTokens
.firstWhere(
(final credential) => credential.token == token,
)
.associatedServerIds
.add(serverId);
await _box.put(BNames.serverProviderTokens, _serverProviderTokens);
}
Future<void> removeServerProviderToken(
final ServerProviderCredential token,
) async {
_serverProviderTokens.remove(token);
await _box.put(BNames.serverProviderTokens, _serverProviderTokens);
}
Future<void> addDnsProviderToken(final DnsProviderCredential token) async {
_dnsProviderTokens.add(token);
await _box.put(BNames.dnsProviderTokens, _dnsProviderTokens);
}
Future<void> associateDomainWithToken(
final String domain,
final String token,
) async {
_dnsProviderTokens
.firstWhere(
(final credential) => credential.token == token,
)
.associatedDomainNames
.add(domain);
await _box.put(BNames.dnsProviderTokens, _dnsProviderTokens);
}
Future<void> removeDnsProviderToken(final DnsProviderCredential token) async {
_dnsProviderTokens.remove(token);
await _box.put(BNames.dnsProviderTokens, _dnsProviderTokens);
}
Future<void> addBackupsCredential(final BackupsCredential credential) async {
_backupsCredentials.add(credential);
await _box.put(BNames.backupsProviderTokens, _backupsCredentials);
}
Future<void> removeBackupsCredential(
final BackupsCredential credential,
) async {
_backupsCredentials.remove(credential);
await _box.put(BNames.backupsProviderTokens, _backupsCredentials);
}
Future<void> addServer(final Server server) async {
_servers.add(server);
await _box.put(BNames.servers, _servers);
}
Future<void> removeServer(final Server server) async {
_servers.remove(server);
await _box.put(BNames.servers, _servers);
}
Future<void> setBackblazeBucket(final BackblazeBucket bucket) async {
_backblazeBucket = bucket;
await _box.put(BNames.backblazeBucket, _backblazeBucket);
}
Future<void> removeBackblazeBucket() async {
_backblazeBucket = null;
await _box.delete(BNames.backblazeBucket);
}
void clear() {
_servers.clear();
_serverProviderTokens.clear();
_dnsProviderTokens.clear();
_backupsCredentials.clear();
_backblazeBucket = null;
_box.clear();
_box.compact();
}
void init() {
_serverProviderTokens = _box
.get(
BNames.serverProviderTokens,
defaultValue: <ServerProviderCredential>[],
)
.map<ServerProviderCredential>(
(final e) => e as ServerProviderCredential,
)
.toList();
_dnsProviderTokens = _box
.get(
BNames.dnsProviderTokens,
defaultValue: <DnsProviderCredential>[],
)
.map<DnsProviderCredential>((final e) => e as DnsProviderCredential)
.toList();
_backupsCredentials = _box
.get(
BNames.backupsProviderTokens,
defaultValue: <BackupsCredential>[],
)
.map<BackupsCredential>((final e) => e as BackupsCredential)
.toList();
_servers = _box
.get(
BNames.servers,
defaultValue: <Server>[],
)
.map<Server>((final e) => e as Server)
.toList();
_backblazeBucket = _box.get(BNames.backblazeBucket);
}
}
class WizardDataModel {
final Box _box = Hive.box(BNames.wizardDataBox);
ServerInstallationWizardData? get serverInstallation => _serverInstallation;
ServerInstallationWizardData? _serverInstallation;
Future<void> setServerProviderType(final ServerProviderType provider) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverProviderType: provider);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setServerProviderKey(final String key) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverProviderKey: key);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setDnsProviderType(final DnsProviderType provider) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(dnsProviderType: provider);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setDnsProviderKey(final String key) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(dnsProviderKey: key);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setServerTypeIdentifier(final String identifier) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverTypeIdentifier: identifier);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setServerLocation(final String location) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverLocation: location);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setServerDetails(final ServerHostingDetails details) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverDetails: () => details);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> deleteServerDetails() async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverDetails: () => null);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setBackupsCredential(final BackupsCredential credential) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(backupsCredential: credential);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setServerDomain(final ServerDomain domain) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverDomain: () => domain);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> deleteServerDomain() async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverDomain: () => null);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setIsServerStarted(final bool isStarted) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(isServerStarted: isStarted);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setIsServerRebootedFirstTime(final bool isRebooted) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(isServerResetedFirstTime: isRebooted);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setIsServerRebootedSecondTime(final bool isRebooted) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(isServerResetedSecondTime: isRebooted);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setRootUser(final User user) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(rootUser: user);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> setIsRecoveringServer(final bool isRecovering) async {
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(isRecoveringServer: isRecovering);
await _box.put(BNames.serverInstallationWizardData, _serverInstallation);
}
Future<void> clearServerInstallation() async {
_serverInstallation = null;
await _box.delete(BNames.serverInstallationWizardData);
}
Future<void> clear() async {
await _box.clear();
await _box.compact();
}
void init() {
_serverInstallation =
_box.get(BNames.serverInstallationWizardData, defaultValue: null);
}
}

View File

@ -3,13 +3,19 @@
1. User
2. ServerHostingDetails
3. ServerDomain
4. BackblazeCredential
5. ServerVolume
4. BackupsCredential
5. ServerProviderVolume
6. BackblazeBucket
7. ServerProviderCredential
8. DnsProviderCredential
9. Server
## Wizards store
60. ServerInstallationWizardData
## Enums
100. DnsProvider
101. ServerProvider
102. UserType
103. BackupsProvider
103. BackupsProviderType

View File

@ -5,6 +5,7 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.da
part 'backups_credential.g.dart';
// TODO: Make a constant type.
@HiveType(typeId: 4)
class BackupsCredential {
BackupsCredential({

View File

@ -0,0 +1,27 @@
import 'package:hive/hive.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
part 'dns_provider_credential.g.dart';
// TODO: Make a constant type.
@HiveType(typeId: 8)
class DnsProviderCredential {
DnsProviderCredential({
required this.tokenId,
required this.token,
required this.provider,
required this.associatedDomainNames,
});
@HiveField(0)
final String? tokenId;
@HiveField(1)
final String token;
@HiveField(2)
final DnsProviderType provider;
@HiveField(3)
final List<String> associatedDomainNames;
}

View File

@ -0,0 +1,50 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'dns_provider_credential.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class DnsProviderCredentialAdapter extends TypeAdapter<DnsProviderCredential> {
@override
final int typeId = 8;
@override
DnsProviderCredential read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return DnsProviderCredential(
tokenId: fields[0] as String?,
token: fields[1] as String,
provider: fields[2] as DnsProviderType,
associatedDomainNames: (fields[3] as List).cast<String>(),
);
}
@override
void write(BinaryWriter writer, DnsProviderCredential obj) {
writer
..writeByte(4)
..writeByte(0)
..write(obj.tokenId)
..writeByte(1)
..write(obj.token)
..writeByte(2)
..write(obj.provider)
..writeByte(3)
..write(obj.associatedDomainNames);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is DnsProviderCredentialAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -0,0 +1,20 @@
import 'package:hive/hive.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
part 'server.g.dart';
// TODO: Make a constant type.
@HiveType(typeId: 9)
class Server {
Server({
required this.hostingDetails,
required this.domain,
});
@HiveField(0)
final ServerHostingDetails hostingDetails;
@HiveField(1)
final ServerDomain domain;
}

View File

@ -0,0 +1,44 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ServerAdapter extends TypeAdapter<Server> {
@override
final int typeId = 9;
@override
Server read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return Server(
hostingDetails: fields[0] as ServerHostingDetails,
domain: fields[1] as ServerDomain,
);
}
@override
void write(BinaryWriter writer, Server obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.hostingDetails)
..writeByte(1)
..write(obj.domain);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ServerAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -12,6 +12,8 @@ class ServerHostingDetails {
required this.volume,
required this.apiToken,
required this.provider,
this.serverLocation,
this.serverType,
this.startTime,
});
@ -21,12 +23,13 @@ class ServerHostingDetails {
@HiveField(1)
final int id;
@HiveField(3)
final DateTime? createTime;
// TODO: Check if it is still needed
@HiveField(2)
final DateTime? startTime;
@HiveField(3)
final DateTime? createTime;
// TODO: Check if it is still needed
@HiveField(4)
final ServerProviderVolume volume;
@ -37,9 +40,21 @@ class ServerHostingDetails {
@HiveField(6, defaultValue: ServerProviderType.hetzner)
final ServerProviderType provider;
ServerHostingDetails copyWith({final DateTime? startTime}) =>
@HiveField(7)
final String? serverLocation;
@HiveField(8)
final String? serverType;
ServerHostingDetails copyWith({
final DateTime? startTime,
final String? serverLocation,
final String? serverType,
}) =>
ServerHostingDetails(
startTime: startTime ?? this.startTime,
serverLocation: serverLocation ?? this.serverLocation,
serverType: serverType ?? this.serverType,
createTime: createTime,
id: id,
ip4: ip4,

View File

@ -25,6 +25,8 @@ class ServerHostingDetailsAdapter extends TypeAdapter<ServerHostingDetails> {
provider: fields[6] == null
? ServerProviderType.hetzner
: fields[6] as ServerProviderType,
serverLocation: fields[7] as String?,
serverType: fields[8] as String?,
startTime: fields[2] as DateTime?,
);
}
@ -32,21 +34,25 @@ class ServerHostingDetailsAdapter extends TypeAdapter<ServerHostingDetails> {
@override
void write(BinaryWriter writer, ServerHostingDetails obj) {
writer
..writeByte(7)
..writeByte(9)
..writeByte(0)
..write(obj.ip4)
..writeByte(1)
..write(obj.id)
..writeByte(3)
..write(obj.createTime)
..writeByte(2)
..write(obj.startTime)
..writeByte(3)
..write(obj.createTime)
..writeByte(4)
..write(obj.volume)
..writeByte(5)
..write(obj.apiToken)
..writeByte(6)
..write(obj.provider);
..write(obj.provider)
..writeByte(7)
..write(obj.serverLocation)
..writeByte(8)
..write(obj.serverType);
}
@override

View File

@ -0,0 +1,27 @@
import 'package:hive/hive.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
part 'server_provider_credential.g.dart';
// TODO: Make a constant type.
@HiveType(typeId: 7)
class ServerProviderCredential {
ServerProviderCredential({
required this.tokenId,
required this.token,
required this.provider,
required this.associatedServerIds,
});
@HiveField(0)
final String? tokenId;
@HiveField(1)
final String token;
@HiveField(2)
final ServerProviderType provider;
@HiveField(3)
final List<int> associatedServerIds;
}

View File

@ -0,0 +1,51 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_provider_credential.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ServerProviderCredentialAdapter
extends TypeAdapter<ServerProviderCredential> {
@override
final int typeId = 7;
@override
ServerProviderCredential read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return ServerProviderCredential(
tokenId: fields[0] as String?,
token: fields[1] as String,
provider: fields[2] as ServerProviderType,
associatedServerIds: (fields[3] as List).cast<int>(),
);
}
@override
void write(BinaryWriter writer, ServerProviderCredential obj) {
writer
..writeByte(4)
..writeByte(0)
..write(obj.tokenId)
..writeByte(1)
..write(obj.token)
..writeByte(2)
..write(obj.provider)
..writeByte(3)
..write(obj.associatedServerIds);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ServerProviderCredentialAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -0,0 +1,130 @@
import 'package:flutter/cupertino.dart';
import 'package:hive/hive.dart';
import 'package:selfprivacy/logic/models/hive/backups_credential.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
part 'server_installation_wizard_data.g.dart';
@HiveType(typeId: 60)
class ServerInstallationWizardData {
const ServerInstallationWizardData({
required this.hasFinalChecked,
required this.isServerStarted,
required this.isServerResetedFirstTime,
required this.isServerResetedSecondTime,
required this.isLoading,
required this.isRecoveringServer,
required this.rootUser,
required this.serverProviderType,
required this.serverProviderKey,
required this.dnsProviderType,
required this.dnsProviderKey,
required this.backupsCredential,
required this.serverTypeIdentifier,
required this.serverLocation,
required this.serverDetails,
required this.serverDomain,
});
ServerInstallationWizardData.empty()
: this(
hasFinalChecked: false,
isServerStarted: false,
isServerResetedFirstTime: false,
isServerResetedSecondTime: false,
isLoading: false,
isRecoveringServer: false,
rootUser: null,
serverProviderType: null,
serverProviderKey: null,
dnsProviderType: null,
dnsProviderKey: null,
backupsCredential: null,
serverTypeIdentifier: null,
serverLocation: null,
serverDetails: null,
serverDomain: null,
);
// Bool flags used by installer
@HiveField(0)
final bool hasFinalChecked;
@HiveField(1)
final bool isServerStarted;
@HiveField(2)
final bool isServerResetedFirstTime;
@HiveField(3)
final bool isServerResetedSecondTime;
@HiveField(4)
final bool isLoading;
@HiveField(5)
final bool isRecoveringServer;
@HiveField(6)
final User? rootUser;
@HiveField(7)
final ServerProviderType? serverProviderType;
@HiveField(8)
final String? serverProviderKey;
@HiveField(9)
final DnsProviderType? dnsProviderType;
@HiveField(10)
final String? dnsProviderKey;
@HiveField(11)
final BackupsCredential? backupsCredential;
@HiveField(12)
final String? serverTypeIdentifier;
@HiveField(13)
final String? serverLocation;
@HiveField(14)
final ServerHostingDetails? serverDetails;
@HiveField(15)
final ServerDomain? serverDomain;
ServerInstallationWizardData copyWith({
final bool? hasFinalChecked,
final bool? isServerStarted,
final bool? isServerResetedFirstTime,
final bool? isServerResetedSecondTime,
final bool? isLoading,
final bool? isRecoveringServer,
final User? rootUser,
final ServerProviderType? serverProviderType,
final String? serverProviderKey,
final DnsProviderType? dnsProviderType,
final String? dnsProviderKey,
final BackupsCredential? backupsCredential,
final String? serverTypeIdentifier,
final String? serverLocation,
final ValueGetter<ServerHostingDetails?>? serverDetails,
final ValueGetter<ServerDomain?>? serverDomain,
}) =>
ServerInstallationWizardData(
hasFinalChecked: hasFinalChecked ?? this.hasFinalChecked,
isServerStarted: isServerStarted ?? this.isServerStarted,
isServerResetedFirstTime:
isServerResetedFirstTime ?? this.isServerResetedFirstTime,
isServerResetedSecondTime:
isServerResetedSecondTime ?? this.isServerResetedSecondTime,
isLoading: isLoading ?? this.isLoading,
isRecoveringServer: isRecoveringServer ?? this.isRecoveringServer,
rootUser: rootUser ?? this.rootUser,
serverProviderType: serverProviderType ?? this.serverProviderType,
serverProviderKey: serverProviderKey ?? this.serverProviderKey,
dnsProviderType: dnsProviderType ?? this.dnsProviderType,
dnsProviderKey: dnsProviderKey ?? this.dnsProviderKey,
backupsCredential: backupsCredential ?? this.backupsCredential,
serverTypeIdentifier: serverTypeIdentifier ?? this.serverTypeIdentifier,
serverLocation: serverLocation ?? this.serverLocation,
serverDetails:
serverDetails != null ? serverDetails() : this.serverDetails,
serverDomain: serverDomain != null ? serverDomain() : this.serverDomain,
);
}

View File

@ -0,0 +1,87 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'server_installation_wizard_data.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class ServerInstallationWizardDataAdapter
extends TypeAdapter<ServerInstallationWizardData> {
@override
final int typeId = 60;
@override
ServerInstallationWizardData read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return ServerInstallationWizardData(
hasFinalChecked: fields[0] as bool,
isServerStarted: fields[1] as bool,
isServerResetedFirstTime: fields[2] as bool,
isServerResetedSecondTime: fields[3] as bool,
isLoading: fields[4] as bool,
isRecoveringServer: fields[5] as bool,
rootUser: fields[6] as User?,
serverProviderType: fields[7] as ServerProviderType?,
serverProviderKey: fields[8] as String?,
dnsProviderType: fields[9] as DnsProviderType?,
dnsProviderKey: fields[10] as String?,
backupsCredential: fields[11] as BackupsCredential?,
serverTypeIdentifier: fields[12] as String?,
serverLocation: fields[13] as String?,
serverDetails: fields[14] as ServerHostingDetails?,
serverDomain: fields[15] as ServerDomain?,
);
}
@override
void write(BinaryWriter writer, ServerInstallationWizardData obj) {
writer
..writeByte(16)
..writeByte(0)
..write(obj.hasFinalChecked)
..writeByte(1)
..write(obj.isServerStarted)
..writeByte(2)
..write(obj.isServerResetedFirstTime)
..writeByte(3)
..write(obj.isServerResetedSecondTime)
..writeByte(4)
..write(obj.isLoading)
..writeByte(5)
..write(obj.isRecoveringServer)
..writeByte(6)
..write(obj.rootUser)
..writeByte(7)
..write(obj.serverProviderType)
..writeByte(8)
..write(obj.serverProviderKey)
..writeByte(9)
..write(obj.dnsProviderType)
..writeByte(10)
..write(obj.dnsProviderKey)
..writeByte(11)
..write(obj.backupsCredential)
..writeByte(12)
..write(obj.serverTypeIdentifier)
..writeByte(13)
..write(obj.serverLocation)
..writeByte(14)
..write(obj.serverDetails)
..writeByte(15)
..write(obj.serverDomain);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ServerInstallationWizardDataAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@ -65,59 +65,14 @@ class DigitalOceanLocation {
emoji = '🇮🇳';
break;
case 'syd':
emoji = '🇦🇺';
break;
case 'nyc':
case 'sfo':
emoji = '🇺🇸';
break;
}
return emoji;
}
String get countryDisplayKey {
String displayKey = 'countries.';
switch (slug.substring(0, 3)) {
case 'fra':
displayKey += 'germany';
break;
case 'ams':
displayKey += 'netherlands';
break;
case 'sgp':
displayKey += 'singapore';
break;
case 'lon':
displayKey += 'united_kingdom';
break;
case 'tor':
displayKey += 'canada';
break;
case 'blr':
displayKey += 'india';
break;
case 'syd':
displayKey += 'australia';
break;
case 'nyc':
case 'sfo':
displayKey += 'united_states';
break;
default:
displayKey = slug;
}
return displayKey;
}
}
@JsonSerializable()

View File

@ -155,27 +155,6 @@ class HetznerLocation {
}
return emoji;
}
String get countryDisplayKey {
String displayKey = 'countries.';
switch (country.substring(0, 2)) {
case 'DE':
displayKey += 'germany';
break;
case 'FI':
displayKey += 'finland';
break;
case 'US':
displayKey += 'united_states';
break;
default:
displayKey = country;
}
return displayKey;
}
}
/// A Volume is a highly-available, scalable, and SSD-based block storage for Servers.

View File

@ -11,7 +11,7 @@ class Price {
enum CurrencyType {
eur,
usd,
unkown,
unknown,
}
class Currency {

View File

@ -2,14 +2,12 @@ class ServerProviderLocation {
ServerProviderLocation({
required this.title,
required this.identifier,
required this.countryDisplayKey,
this.description,
this.flag = '',
});
final String title;
final String identifier;
final String countryDisplayKey;
final String? description;
final String flag;
}

View File

@ -438,7 +438,6 @@ class DigitalOceanServerProvider extends ServerProvider {
description: rawLocation.name,
flag: rawLocation.flag,
identifier: rawLocation.slug,
countryDisplayKey: rawLocation.countryDisplayKey,
);
} catch (e) {
continue;

View File

@ -156,7 +156,6 @@ class HetznerServerProvider extends ServerProvider {
description: server.location.description,
flag: server.location.flag,
identifier: server.location.name,
countryDisplayKey: server.location.countryDisplayKey,
),
),
);
@ -457,7 +456,6 @@ class HetznerServerProvider extends ServerProvider {
description: rawLocation.description,
flag: rawLocation.flag,
identifier: rawLocation.name,
countryDisplayKey: rawLocation.countryDisplayKey,
);
} catch (e) {
continue;

View File

@ -1,6 +1,5 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/bloc_config.dart';
import 'package:selfprivacy/config/bloc_observer.dart';
@ -10,20 +9,13 @@ import 'package:selfprivacy/config/hive_config.dart';
import 'package:selfprivacy/config/localization.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
import 'package:selfprivacy/ui/pages/errors/failed_to_init_secure_storage.dart';
import 'package:selfprivacy/ui/router/router.dart';
// import 'package:wakelock/wakelock.dart';
import 'package:timezone/data/latest.dart' as tz;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
try {
await HiveConfig.init();
} on PlatformException catch (e) {
runApp(
FailedToInitSecureStorageScreen(e: e),
);
}
await HiveConfig.init();
// await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
// try {

View File

@ -127,9 +127,7 @@ class _HeroSliverAppBarState extends State<HeroSliverAppBar> {
Widget build(final BuildContext context) {
final isMobile =
widget.ignoreBreakpoints ? true : Breakpoints.small.isActive(context);
final isJobsListEmpty = widget.hasFlashButton
? context.watch<JobsCubit>().state is JobsStateEmpty
: true;
final isJobsListEmpty = context.watch<JobsCubit>().state is JobsStateEmpty;
return SliverAppBar(
expandedHeight:
widget.hasHeroIcon ? 148.0 + _size.height : 72.0 + _size.height,

View File

@ -38,14 +38,8 @@ class BackupDetailsPage extends StatelessWidget {
: StateType.uninitialized;
final bool preventActions = backupsState.preventActions;
final List<Backup> backups = backupsState.backups;
final List<Service> services = context
.watch<ServicesBloc>()
.state
.servicesThatCanBeBackedUp
.where(
(final service) => service.isEnabled,
)
.toList();
final List<Service> services =
context.watch<ServicesBloc>().state.servicesThatCanBeBackedUp;
final Duration? autobackupPeriod = backupsState.autobackupPeriod;
final List<ServerJob> backupJobs = context
.watch<ServerJobsBloc>()

View File

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:selfprivacy/logic/bloc/backups/backups_bloc.dart';
import 'package:selfprivacy/logic/bloc/server_jobs/server_jobs_bloc.dart';
import 'package:selfprivacy/logic/bloc/volumes/volumes_bloc.dart';
import 'package:selfprivacy/logic/models/json/server_job.dart';
import 'package:selfprivacy/logic/models/service.dart';
@ -104,29 +103,6 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
...widget.services.map(
(final Service service) {
final bool busy = busyServices.contains(service.id);
final List<Widget> descriptionWidgets = [];
if (busy) {
descriptionWidgets.add(Text('backup.service_busy'.tr()));
} else {
descriptionWidgets.add(
Text(
'service_page.uses'.tr(
namedArgs: {
'usage': service.storageUsage.used.toString(),
'volume': context
.read<VolumesBloc>()
.state
.getVolume(service.storageUsage.volume ?? '')
.displayName,
},
),
style: Theme.of(context).textTheme.labelMedium,
),
);
descriptionWidgets.add(
Text(service.backupDescription),
);
}
return CheckboxListTile.adaptive(
onChanged: !busy
? (final bool? value) {
@ -146,9 +122,8 @@ class _CreateBackupsModalState extends State<CreateBackupsModal> {
title: Text(
service.displayName,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: descriptionWidgets,
subtitle: Text(
busy ? 'backup.service_busy'.tr() : service.backupDescription,
),
secondary: SvgPicture.string(
service.svgIcon,

View File

@ -5,6 +5,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/desired_dns_record.dart';
import 'package:selfprivacy/logic/cubit/dns_records/dns_records_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
import 'package:selfprivacy/logic/get_it/resources_model.dart';
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
import 'package:selfprivacy/ui/components/cards/filled_card.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
@ -86,7 +87,7 @@ class _DnsDetailsPageState extends State<DnsDetailsPage> {
final bool isReady = context.watch<ServerInstallationCubit>().state
is ServerInstallationFinished;
final String domain =
getIt<ApiConfigModel>().serverDomain?.domainName ?? '';
getIt<ResourcesModel>().serverDomain?.domainName ?? '';
final DnsRecordsState dnsCubit = context.watch<DnsRecordsCubit>().state;
print(dnsCubit.dnsState);

View File

@ -1,71 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:gap/gap.dart';
import 'package:selfprivacy/ui/layouts/brand_hero_screen.dart';
import 'package:selfprivacy/ui/pages/more/about_application.dart';
class FailedToInitSecureStorageScreen extends StatelessWidget {
const FailedToInitSecureStorageScreen({
required this.e,
super.key,
});
final PlatformException e;
@override
Widget build(final BuildContext context) => MaterialApp(
home: BrandHeroScreen(
heroIcon: Icons.error_outline,
heroTitle: 'Failed to initialize secure storage',
hasBackButton: false,
children: [
const Text(
'SelfPrivacy requires a secure storage provided by your operating system to encrypt sensitive data, but it failed to initialize.',
),
if (Platform.isLinux)
const Text(
'Please make sure that the libsecret library is installed.',
),
const Gap(16),
Text('Error: ${e.message}'),
const Gap(16),
const Divider(),
const Gap(16),
const LinkListTile(
title: 'Our website',
subtitle: 'selfprivacy.org',
uri: 'https://selfprivacy.org/',
icon: Icons.language_outlined,
),
const LinkListTile(
title: 'Documentation',
subtitle: 'selfprivacy.org/docs',
uri: 'https://selfprivacy.org/docs/',
icon: Icons.library_books_outlined,
),
const LinkListTile(
title: 'Privacy Policy',
subtitle: 'selfprivacy.org/privacy-policy',
uri: 'https://selfprivacy.org/privacy-policy/',
icon: Icons.policy_outlined,
),
const LinkListTile(
title: 'Matrix support chat',
subtitle: '#chat:selfprivacy.org',
uri: 'https://matrix.to/#/#chat:selfprivacy.org',
icon: Icons.question_answer_outlined,
longPressText: '#chat:selfprivacy.org',
),
const LinkListTile(
title: 'Telegram support chat',
subtitle: '@selfprivacy_chat',
uri: 'https://t.me/selfprivacy_chat',
icon: Icons.question_answer_outlined,
longPressText: '@selfprivacy_chat',
),
],
),
);
}

View File

@ -112,7 +112,7 @@ class InitializingPage extends StatelessWidget {
'Server',
'Installation',
],
activeIndex: cubit.state.porgressBar,
activeIndex: cubit.state.progressBar,
),
),
),

View File

@ -120,14 +120,6 @@ class SelectLocationPage extends StatelessWidget {
.titleMedium,
),
const SizedBox(height: 8),
Text(
location.countryDisplayKey.tr(),
style: Theme.of(context)
.textTheme
.bodyMedium,
),
if (location.description != null)
const SizedBox(height: 4),
if (location.description != null)
Text(
location.description!,