Merge pull request 'refactor: Introduce a new storage for tokens' (#505) from inex/april-refactor into master

Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/pulls/505
This commit is contained in:
Inex Code 2024-07-09 20:06:18 +03:00
commit 59cf273fa9
44 changed files with 1400 additions and 483 deletions

4
.vscode/launch.json vendored
View file

@ -31,7 +31,7 @@
]
},
{
"name": "debug (production)",
"name": "debug (production flavor)",
"request": "launch",
"type": "dart",
"args": [
@ -41,7 +41,7 @@
},
{
"name": "debug (nightly)",
"name": "debug (nightly flavor)",
"request": "launch",
"type": "dart",
"args": [

View file

@ -46,12 +46,10 @@
},
"console_page": {
"title": "Console",
"waiting": "Waiting for initialization…",
"copy": "Copy",
"copy_raw": "Raw response",
"history_empty": "No data yet",
"error": "Error",
"log": "Log",
"rest_api_request": "Rest API Request",
"rest_api_response": "Rest API Response",
"graphql_request": "GraphQL Request",

18
lib/config/config.dart Normal file
View file

@ -0,0 +1,18 @@
import 'package:flutter/foundation.dart';
/// internal app configuration
const config = InternalConfig(
shouldDebugPrint: kDebugMode,
);
class InternalConfig {
const InternalConfig({
required this.shouldDebugPrint,
});
final bool shouldDebugPrint;
// example of other possible fields
// final String appVersion;
//
}

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_model.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';
@ -14,9 +15,10 @@ final GetIt getIt = GetIt.instance;
Future<void> getItSetup() async {
getIt.registerSingleton<NavigationService>(NavigationService());
getIt.registerSingleton<ConsoleModel>(ConsoleModel());
getIt.registerSingleton<ResourcesModel>(ResourcesModel()..init());
getIt.registerSingleton<WizardDataModel>(WizardDataModel()..init());
final apiConfigModel = ApiConfigModel();
await apiConfigModel.init();
getIt.registerSingleton<ApiConfigModel>(apiConfigModel);
getIt.registerSingleton<ApiConnectionRepository>(

View file

@ -1,75 +1,227 @@
import 'dart:convert';
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/app_logger.dart';
import 'package:selfprivacy/utils/platform_adapter.dart';
import 'package:selfprivacy/utils/secure_storage.dart';
class HiveConfig {
static final log = const AppLogger(name: 'hive_config').log;
/// bump on schema changes
static const version = 2;
static Future<void> init() async {
final String? storagePath = PlatformAdapter.storagePath;
print('HiveConfig: Custom storage path: $storagePath');
log('set custom storage path to: "$storagePath"');
await Hive.initFlutter(storagePath);
registerAdapters();
await decryptBoxes();
await performMigrations();
}
static void registerAdapters() {
try {
Hive.registerAdapter(UserAdapter());
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());
await Hive.openBox(BNames.appSettingsBox);
try {
final HiveAesCipher cipher = HiveAesCipher(
await getEncryptedKey(BNames.serverInstallationEncryptionKey),
Hive.registerAdapter(ServerInstallationWizardDataAdapter());
log('successfully registered every adapter');
} catch (error, stackTrace) {
log(
'error registering adapters',
error: error,
stackTrace: stackTrace,
);
await Hive.openBox<User>(BNames.usersDeprecated);
await Hive.openBox<User>(BNames.usersBox, 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();
rethrow;
}
}
static Future<HiveAesCipher> getCipher() async {
List<int>? key = await SecureStorage.getKey();
if (key == null) {
key = Hive.generateSecureKey();
await SecureStorage.setKey(key);
}
return HiveAesCipher(key);
}
static Future<void> decryptBoxes() async {
try {
// load encrypted boxes into memory
final HiveAesCipher cipher = await getCipher();
await Hive.openBox(
BNames.serverInstallationBox,
encryptionCipher: cipher,
);
} on PlatformException catch (e) {
print('HiveConfig: Error while opening boxes: $e');
await Hive.openBox(
BNames.resourcesBox,
encryptionCipher: cipher,
);
await Hive.openBox(
BNames.wizardDataBox,
encryptionCipher: cipher,
);
log('successfully decrypted boxes');
} catch (error, stackTrace) {
log(
'error initializing encrypted boxes',
error: error,
stackTrace: stackTrace,
);
rethrow;
}
}
static Future<Uint8List> getEncryptedKey(final String encKey) async {
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
// migrations
static Future<void> performMigrations() async {
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));
// perform migration check
final localSettingsBox = await Hive.openBox(BNames.appSettingsBox);
// if it is an initial app launch, we do not need to perform any migrations
final savedVersion = localSettingsBox.isEmpty
? version
// if box was initialized, but database version was not introduced in
// it yet, it means that we have initial value
: await localSettingsBox.get(BNames.databaseVersion, defaultValue: 1);
/// launch migrations based on version
if (savedVersion < version) {
if (savedVersion < 2) {
await migrateFrom1To2();
}
final String? string = await secureStorage.read(key: encKey);
return base64Url.decode(string!);
} on PlatformException catch (e) {
print('HiveConfig: Error while getting encryption key: $e');
/// add new migrations here, like:
/// if (version < 3) {...}, etc.
}
/// update saved version after successfull migrations
await localSettingsBox.put(BNames.databaseVersion, version);
} catch (error, stackTrace) {
log(
'error running db migrations',
error: error,
stackTrace: stackTrace,
);
rethrow;
}
}
/// introduce and populate resourcesBox
static Future<void> migrateFrom1To2() async {
final Box resourcesBox = Hive.box(BNames.resourcesBox);
if (resourcesBox.isEmpty) {
final Box serverInstallationBox = Hive.box(BNames.serverInstallationBox);
final ServerHostingDetails? serverDetails =
serverInstallationBox.get(BNames.serverDetails);
// move server provider config
final ServerProviderType? serverProvider =
serverInstallationBox.get(BNames.serverProvider) ??
serverDetails?.provider;
final String? serverProviderKey =
serverInstallationBox.get(BNames.hetznerKey);
if (serverProviderKey != null && serverProvider.isSpecified) {
final ServerProviderCredential serverProviderCredential =
ServerProviderCredential(
tokenId: null,
token: serverProviderKey,
provider: serverProvider!,
associatedServerIds: [if (serverDetails != null) serverDetails.id],
);
await resourcesBox.put(
BNames.serverProviderTokens,
[serverProviderCredential],
);
}
final String? serverLocation =
serverInstallationBox.get(BNames.serverLocation);
final String? serverType =
serverInstallationBox.get(BNames.serverTypeIdentifier);
final ServerDomain? serverDomain =
serverInstallationBox.get(BNames.serverDomain);
if (serverDetails != null && serverDomain != null) {
await resourcesBox.put(
BNames.servers,
[
Server(
domain: serverDomain,
hostingDetails: serverDetails.copyWith(
serverLocation: serverLocation,
serverType: serverType,
),
),
],
);
}
// move dns config
final String? dnsProviderKey =
serverInstallationBox.get(BNames.cloudFlareKey);
final DnsProviderType? dnsProvider =
serverInstallationBox.get(BNames.dnsProvider) ??
serverDomain?.provider;
if (dnsProviderKey != null && dnsProvider.isSpecified) {
final DnsProviderCredential dnsProviderCredential =
DnsProviderCredential(
tokenId: null,
token: dnsProviderKey,
provider: dnsProvider!,
associatedDomainNames: [
if (serverDomain != null) serverDomain.domainName,
],
);
await resourcesBox
.put(BNames.dnsProviderTokens, [dnsProviderCredential]);
}
// move backblaze (backups) config
final BackupsCredential? backblazeCredential =
serverInstallationBox.get(BNames.backblazeCredential);
final BackblazeBucket? backblazeBucket =
serverInstallationBox.get(BNames.backblazeBucket);
if (backblazeCredential != null) {
await resourcesBox
.put(BNames.backupsProviderTokens, [backblazeCredential]);
}
if (backblazeBucket != null) {
await resourcesBox.put(BNames.backblazeBucket, backblazeBucket);
}
}
log('successfully migrated db from 1 to 2 version');
}
}
/// Mappings for the different boxes and their keys
@ -77,6 +229,9 @@ class BNames {
/// App settings box. Contains app settings like [darkThemeModeOn], [shouldShowOnboarding]
static String appSettingsBox = 'appSettings';
/// An integer with last saved version of the database
static String databaseVersion = 'databaseVersion';
/// A boolean field of [appSettingsBox] box.
static String darkThemeModeOn = 'isDarkModeOn';
@ -89,9 +244,6 @@ class BNames {
/// A string field
static String appLocale = 'appLocale';
/// Encryption key to decrypt [serverInstallationBox] and [usersBox] box.
static String serverInstallationEncryptionKey = 'key';
/// Server installation box. Contains server details and provider tokens.
static String serverInstallationBox = 'appConfig';
@ -122,7 +274,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.
@ -149,9 +301,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

@ -5,6 +5,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/console_log.dart';
void _addConsoleLog(final ConsoleLog message) =>
@ -120,7 +121,7 @@ abstract class GraphQLApiMap {
String get _token {
String token = '';
final serverDetails = getIt<ApiConfigModel>().serverDetails;
final serverDetails = getIt<ResourcesModel>().serverDetails;
if (serverDetails != null) {
token = serverDetails.apiToken;
}

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

@ -1,19 +1,25 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/foundation.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'package:selfprivacy/config/config.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/models/console_log.dart';
import 'package:selfprivacy/utils/app_logger.dart';
abstract class RestApiMap {
static final log = const AppLogger(name: 'rest_api_map').log;
Future<Dio> getClient({final BaseOptions? customOptions}) async {
final Dio dio = Dio(customOptions ?? (await options));
if (hasLogger) {
dio.interceptors.add(PrettyDioLogger());
if (hasLogger && config.shouldDebugPrint) {
dio.interceptors.add(
PrettyDioLogger(logPrint: (final object) => debugPrint('$object')),
);
}
dio.interceptors.add(ConsoleInterceptor());
dio.httpClientAdapter = IOHttpClientAdapter(
@ -28,14 +34,13 @@ abstract class RestApiMap {
dio.interceptors.add(
InterceptorsWrapper(
onError: (final DioException e, final ErrorInterceptorHandler handler) {
print(e.requestOptions.path);
print(e.requestOptions.data);
onError: (
final DioException exception,
final ErrorInterceptorHandler handler,
) {
log('got dio exception:', error: exception);
print(e.message);
print(e.response);
return handler.next(e);
return handler.next(exception);
},
),
);
@ -103,7 +108,6 @@ class ConsoleInterceptor extends InterceptorsWrapper {
final ErrorInterceptorHandler handler,
) async {
final Response? response = err.response;
log(err.toString());
addConsoleLog(
ManualConsoleLog.warning(

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,21 +792,18 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final BackupsCredential backblazeCredential,
) async {
await repository.saveIsServerStarted(true);
await repository.saveIsServerResetedFirstTime(true);
await repository.saveIsServerResetedSecondTime(true);
await repository.saveHasFinalChecked(true);
await repository.saveIsServerRebootedFirstTime(true);
await repository.saveIsServerRebootedSecondTime(true);
await repository.saveIsRecoveringServer(false);
final serverType = await ProvidersController.currentServerProvider!
.getServerType(state.serverDetails!.id);
await repository.saveServerType(serverType.data!);
await ProvidersController.currentServerProvider!
.trySetServerLocation(serverType.data!.location.identifier);
final User mainUser = await repository.getMainUser();
await repository.saveRootUser(mainUser);
await repository.saveHasFinalChecked(true);
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(
@ -209,6 +182,9 @@ class ServerInstallationRepository {
if (!domainResult.success || domainResult.data.isEmpty) {
return false;
}
await getIt<ResourcesModel>().removeDnsProviderToken(
getIt<ResourcesModel>().dnsProviderCredentials.first,
);
return domainResult.data.any(
(final serverDomain) => serverDomain.domainName == domain,
@ -216,7 +192,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 +214,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 +245,7 @@ class ServerInstallationRepository {
}
Future<ServerHostingDetails> powerOn() async {
final server = getIt<ApiConfigModel>().serverDetails!;
final server = getIt<ResourcesModel>().serverDetails!;
return startServer(server);
}
@ -436,174 +429,119 @@ 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);
await 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);
await 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);
await 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);
await 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);
await 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.
await getIt<WizardDataModel>().moveServerTypeToServerDetails();
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(),
await getIt<ResourcesModel>().associateServerWithToken(
wizardData.serverDetails!.id,
wizardData.serverProviderKey!,
);
return false;
}
final deletionResult =
await ProvidersController.currentServerProvider!.deleteServer(
serverDomain.domainName,
await getIt<ResourcesModel>().associateDomainWithToken(
wizardData.serverDomain!.domainName,
wizardData.dnsProviderKey!,
);
if (!deletionResult.success) {
getIt<NavigationService>().showSnackBar(
'modals.server_validators_error'.tr(),
await getIt<ResourcesModel>().addBackupsCredential(
wizardData.backupsCredential!,
);
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,
]);
await getIt<ApiConfigModel>().init();
await getIt<WizardDataModel>().clearServerInstallation();
}
}

View file

@ -54,7 +54,7 @@ abstract class ServerInstallationState extends Equatable {
ServerSetupProgress get progress => ServerSetupProgress
.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) {
@ -119,7 +119,7 @@ class TimerState extends ServerInstallationNotFinished {
enum ServerSetupProgress {
nothingYet,
serverProviderFilled,
servertTypeFilled,
serverTypeFilled,
dnsProviderFilled,
backblazeFilled,
domainFilled,
@ -147,6 +147,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;
@ -211,12 +231,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
dnsApiToken: dnsApiToken!,
backblazeCredential: backblazeCredential!,
serverDomain: serverDomain!,
rootUser: rootUser!,
serverDetails: serverDetails!,
isServerStarted: isServerStarted,
isServerResetedFirstTime: isServerResetedFirstTime,
isServerResetedSecondTime: isServerResetedSecondTime,
installationDialoguePopUp: installationDialoguePopUp,
);
}
@ -247,13 +262,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 => [
@ -302,9 +318,9 @@ class ServerInstallationRecovery extends ServerInstallationState {
super.dnsApiToken,
super.backblazeCredential,
super.serverDomain,
super.rootUser,
super.serverDetails,
}) : super(
rootUser: null,
isServerStarted: true,
isServerResetedFirstTime: true,
isServerResetedSecondTime: true,
@ -334,7 +350,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,
@ -346,7 +361,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,
@ -358,11 +372,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,115 +1,18 @@
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 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;
static const localeCodeFallback = 'en';
String? _localeCode;
String get localeCode => _localeCode ?? localeCodeFallback;
Future<void> setLocaleCode(final String value) async => _localeCode = value;
Future<void> resetLocaleCode() async => _localeCode = null;
String? _serverProviderKey;
String? _serverLocation;
String? _dnsProviderKey;
String? _serverType;
ServerProviderType? _serverProvider;
DnsProviderType? _dnsProvider;
ServerHostingDetails? _serverDetails;
BackupsCredential? _backblazeCredential;
ServerDomain? _serverDomain;
BackblazeBucket? _backblazeBucket;
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() {
_serverProviderKey = null;
_dnsProvider = null;
_serverLocation = null;
_dnsProviderKey = null;
_backblazeCredential = null;
_serverDomain = null;
_serverDetails = null;
_backblazeBucket = null;
_serverType = null;
_serverProvider = null;
}
Future<void> init() async {
_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,342 @@
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 {
// Check if this token already exists
if (_dnsProviderTokens
.any((final credential) => credential.token == token.token)) {
throw Exception('Token already exists');
}
_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> moveServerTypeToServerDetails() async {
final details = _serverInstallation?.serverDetails;
if (details != null) {
if (_serverInstallation?.serverTypeIdentifier != null &&
_serverInstallation?.serverLocation != null) {
_serverInstallation = _serverInstallation?.copyWith(
serverDetails: () => details.copyWith(
serverType: _serverInstallation?.serverTypeIdentifier,
serverLocation: _serverInstallation?.serverLocation,
),
);
await _box.put(
BNames.serverInstallationWizardData,
_serverInstallation,
);
}
}
}
Future<void> setServerDetails(final ServerHostingDetails details) async {
final detailsWithServerType = details.copyWith(
serverLocation: _serverInstallation?.serverLocation,
serverType: _serverInstallation?.serverTypeIdentifier,
);
_serverInstallation =
(_serverInstallation ?? ServerInstallationWizardData.empty())
.copyWith(serverDetails: () => detailsWithServerType);
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,
@ -103,3 +118,7 @@ enum ServerProviderType {
unknown => 'Unknown',
};
}
extension ServerProviderTypeIsSpecified on ServerProviderType? {
bool get isSpecified => this != null && this != ServerProviderType.unknown;
}

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

@ -57,3 +57,7 @@ enum DnsProviderType {
unknown => 'Unknown',
};
}
extension DnsProviderTypeIsSpecified on DnsProviderType? {
bool get isSpecified => this != null && this != DnsProviderType.unknown;
}

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

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

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,7 +1,6 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/models/console_log.dart';
import 'package:selfprivacy/ui/pages/more/console/console_log_item_widget.dart';
@ -18,21 +17,15 @@ class ConsolePage extends StatefulWidget {
class _ConsolePageState extends State<ConsolePage> {
ConsoleModel get console => getIt<ConsoleModel>();
/// should freeze logs state to properly read logs
late final Future<void> future;
@override
void initState() {
super.initState();
future = getIt.allReady();
console.addListener(update);
}
@override
void dispose() {
console.removeListener(update);
super.dispose();
}
@ -48,7 +41,10 @@ class _ConsolePageState extends State<ConsolePage> {
}
@override
Widget build(final BuildContext context) => SafeArea(
Widget build(final BuildContext context) {
final List<ConsoleLog> logs = console.logs;
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text('console_page.title'.tr()),
@ -68,46 +64,13 @@ class _ConsolePageState extends State<ConsolePage> {
],
),
body: Scrollbar(
child: FutureBuilder(
future: future,
builder: (
final BuildContext context,
final AsyncSnapshot<void> snapshot,
) {
if (snapshot.hasData) {
final List<ConsoleLog> logs = console.logs;
return logs.isEmpty
child: logs.isEmpty
? const _ConsoleViewEmpty()
: _ConsoleViewLoaded(logs: logs);
: _ConsoleViewLoaded(logs: logs),
),
),
);
}
return const _ConsoleViewLoading();
},
),
),
),
);
}
class _ConsoleViewLoading extends StatelessWidget {
const _ConsoleViewLoading();
@override
Widget build(final BuildContext context) => Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('console_page.waiting'.tr()),
const Gap(16),
const Expanded(
child: Center(
child: CircularProgressIndicator.adaptive(),
),
),
],
);
}
class _ConsoleViewEmpty extends StatelessWidget {

View file

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

28
lib/utils/app_logger.dart Normal file
View file

@ -0,0 +1,28 @@
import 'dart:developer' as developer;
import 'package:selfprivacy/config/config.dart';
class AppLogger {
const AppLogger({required this.name});
final String name;
// TODO: research other possible options, which could support both
// throttling and output formatting
void log(
final String message, {
final Object? error,
final StackTrace? stackTrace,
}) {
if (config.shouldDebugPrint) {
// TODO: could probably add UI logging for console_page
developer.log(
message,
error: error,
stackTrace: stackTrace,
time: DateTime.now(),
name: name,
);
}
}
}

View file

@ -0,0 +1,48 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:selfprivacy/utils/app_logger.dart';
class SecureStorage {
static final log = const AppLogger(name: 'secure_storage').log;
static const FlutterSecureStorage secureStorage = FlutterSecureStorage();
static String keyName = 'key';
static Future<Uint8List?> getKey() async {
try {
final bool hasEncryptionKey =
await secureStorage.containsKey(key: keyName);
if (!hasEncryptionKey) {
return null;
}
final String? string = await secureStorage.read(key: keyName);
log('successfully got encryption key: $string');
return base64Url.decode(string!);
} catch (error, stackTrace) {
log(
'error reading encryption key',
error: error,
stackTrace: stackTrace,
);
rethrow;
}
}
static Future<void> setKey(final List<int> key) async {
try {
final value = base64UrlEncode(key);
await secureStorage.write(key: keyName, value: value);
log('successfully saved encryption key: $value');
} catch (error, stackTrace) {
log(
'error saving new encryption key',
error: error,
stackTrace: stackTrace,
);
rethrow;
}
}
}

View file

@ -9,6 +9,7 @@
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <dynamic_color/dynamic_color_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <local_auth_windows/local_auth_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("DynamicColorPluginCApi"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
LocalAuthPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View file

@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
dynamic_color
flutter_secure_storage_windows
local_auth_windows
url_launcher_windows
)