mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-24 17:56:45 +00:00
chore: Merge digital-ocean into master
Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/140 Reviewed-by: Inex Code <inex.code@selfprivacy.org>
This commit is contained in:
commit
58ce0f0f8b
BIN
assets/images/logos/digital_ocean.png
Normal file
BIN
assets/images/logos/digital_ocean.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -31,7 +31,8 @@
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"apply": "Apply",
|
"apply": "Apply",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
"continue": "Continue"
|
"continue": "Continue",
|
||||||
|
"alert": "Alert"
|
||||||
},
|
},
|
||||||
"more_page": {
|
"more_page": {
|
||||||
"configuration_wizard": "Setup wizard",
|
"configuration_wizard": "Setup wizard",
|
||||||
|
@ -109,6 +110,7 @@
|
||||||
"disk": "Disk local",
|
"disk": "Disk local",
|
||||||
"monthly_cost": "Monthly cost",
|
"monthly_cost": "Monthly cost",
|
||||||
"location": "Location",
|
"location": "Location",
|
||||||
|
"provider": "Provider",
|
||||||
"core_count": {
|
"core_count": {
|
||||||
"one": "{} core",
|
"one": "{} core",
|
||||||
"two": "{} cores",
|
"two": "{} cores",
|
||||||
|
@ -267,9 +269,14 @@
|
||||||
},
|
},
|
||||||
"initializing": {
|
"initializing": {
|
||||||
"connect_to_server": "Connect a server",
|
"connect_to_server": "Connect a server",
|
||||||
|
"select_provider": "Select your provider",
|
||||||
"place_where_data": "A place where your data and SelfPrivacy services will reside:",
|
"place_where_data": "A place where your data and SelfPrivacy services will reside:",
|
||||||
"how": "How to obtain API token",
|
"how": "How to obtain API token",
|
||||||
"hetzner_bad_key_error": "Hetzner API key is invalid",
|
"provider_bad_key_error": "Provider API key is invalid",
|
||||||
|
"choose_location_type": "Choose your server location and type:",
|
||||||
|
"back_to_locations": "Go back to available locations!",
|
||||||
|
"no_locations_found": "No available locations found. Make sure your account is accessible.",
|
||||||
|
"no_server_types_found": "No available server types found. Make sure your account is accessible and try to change your server location.",
|
||||||
"cloudflare_bad_key_error": "Cloudflare API key is invalid",
|
"cloudflare_bad_key_error": "Cloudflare API key is invalid",
|
||||||
"backblaze_bad_key_error": "Backblaze storage information is invalid",
|
"backblaze_bad_key_error": "Backblaze storage information is invalid",
|
||||||
"connect_cloudflare": "Connect CloudFlare",
|
"connect_cloudflare": "Connect CloudFlare",
|
||||||
|
@ -295,6 +302,7 @@
|
||||||
"checks": "Checks have been completed \n{} out of {}"
|
"checks": "Checks have been completed \n{} out of {}"
|
||||||
},
|
},
|
||||||
"recovering": {
|
"recovering": {
|
||||||
|
"generic_error": "Operation failed, please try again.",
|
||||||
"recovery_main_header": "Connect to an existing server",
|
"recovery_main_header": "Connect to an existing server",
|
||||||
"domain_recovery_description": "Enter a server domain you want to get access for:",
|
"domain_recovery_description": "Enter a server domain you want to get access for:",
|
||||||
"domain_recover_placeholder": "Your domain",
|
"domain_recover_placeholder": "Your domain",
|
||||||
|
@ -314,9 +322,9 @@
|
||||||
"fallback_select_provider_console": "Access to the server console of my prodiver.",
|
"fallback_select_provider_console": "Access to the server console of my prodiver.",
|
||||||
"authorization_failed": "Couldn't log in with this key",
|
"authorization_failed": "Couldn't log in with this key",
|
||||||
"fallback_select_provider_console_hint": "For example: Hetzner.",
|
"fallback_select_provider_console_hint": "For example: Hetzner.",
|
||||||
"hetzner_connected": "Connect to Hetzner",
|
"server_provider_connected": "Connect to your Server Provider",
|
||||||
"hetzner_connected_description": "Communication established. Enter Hetzner token with access to {}:",
|
"server_provider_connected_description": "Communication established. Enter you token with access to {}:",
|
||||||
"hetzner_connected_placeholder": "Hetzner token",
|
"server_provider_connected_placeholder": "Server Provider token",
|
||||||
"confirm_server": "Confirm server",
|
"confirm_server": "Confirm server",
|
||||||
"confirm_server_description": "Found your server! Confirm it is the right one:",
|
"confirm_server_description": "Found your server! Confirm it is the right one:",
|
||||||
"confirm_server_accept": "Yes! That's it",
|
"confirm_server_accept": "Yes! That's it",
|
||||||
|
|
|
@ -31,7 +31,8 @@
|
||||||
"remove": "Удалить",
|
"remove": "Удалить",
|
||||||
"apply": "Применить",
|
"apply": "Применить",
|
||||||
"done": "Готово",
|
"done": "Готово",
|
||||||
"continue": "Продолжить"
|
"continue": "Продолжить",
|
||||||
|
"alert": "Уведомление"
|
||||||
},
|
},
|
||||||
"more_page": {
|
"more_page": {
|
||||||
"configuration_wizard": "Мастер настройки",
|
"configuration_wizard": "Мастер настройки",
|
||||||
|
@ -109,6 +110,7 @@
|
||||||
"disk": "Диск",
|
"disk": "Диск",
|
||||||
"monthly_cost": "Ежемесячная стоимость",
|
"monthly_cost": "Ежемесячная стоимость",
|
||||||
"location": "Размещение",
|
"location": "Размещение",
|
||||||
|
"provider": "Провайдер",
|
||||||
"core_count": {
|
"core_count": {
|
||||||
"one": "{} ядро",
|
"one": "{} ядро",
|
||||||
"two": "{} ядра",
|
"two": "{} ядра",
|
||||||
|
@ -269,7 +271,11 @@
|
||||||
"connect_to_server": "Подключите сервер",
|
"connect_to_server": "Подключите сервер",
|
||||||
"place_where_data": "Здесь будут жить ваши данные и SelfPrivacy-сервисы:",
|
"place_where_data": "Здесь будут жить ваши данные и SelfPrivacy-сервисы:",
|
||||||
"how": "Как получить API Token",
|
"how": "Как получить API Token",
|
||||||
"hetzner_bad_key_error": "Hetzner API ключ неверен",
|
"provider_bad_key_error": "API ключ провайдера неверен",
|
||||||
|
"choose_location_type": "Выберите локацию и тип вашего сервера:",
|
||||||
|
"back_to_locations": "Назад к доступным локациям!",
|
||||||
|
"no_locations_found": "Не найдено локаций. Убедитесь, что ваш аккаунт доступен.",
|
||||||
|
"no_server_types_found": "Не удалось получить список серверов. Убедитесь, что ваш аккаунт доступен и попытайтесь сменить локацию сервера.",
|
||||||
"cloudflare_bad_key_error": "Cloudflare API ключ неверен",
|
"cloudflare_bad_key_error": "Cloudflare API ключ неверен",
|
||||||
"backblaze_bad_key_error": "Информация о Backblaze хранилище неверна",
|
"backblaze_bad_key_error": "Информация о Backblaze хранилище неверна",
|
||||||
"connect_cloudflare": "Подключите CloudFlare",
|
"connect_cloudflare": "Подключите CloudFlare",
|
||||||
|
@ -295,6 +301,7 @@
|
||||||
"checks": "Проверок выполнено: \n{} / {}"
|
"checks": "Проверок выполнено: \n{} / {}"
|
||||||
},
|
},
|
||||||
"recovering": {
|
"recovering": {
|
||||||
|
"generic_error": "Ошибка проведения операции, попробуйте ещё раз.",
|
||||||
"recovery_main_header": "Подключиться к существующему серверу",
|
"recovery_main_header": "Подключиться к существующему серверу",
|
||||||
"domain_recovery_description": "Введите домен, по которому вы хотите получить доступ к серверу:",
|
"domain_recovery_description": "Введите домен, по которому вы хотите получить доступ к серверу:",
|
||||||
"domain_recover_placeholder": "Домен",
|
"domain_recover_placeholder": "Домен",
|
||||||
|
|
|
@ -87,9 +87,18 @@ class BNames {
|
||||||
/// A String field of [serverInstallationBox] box.
|
/// A String field of [serverInstallationBox] box.
|
||||||
static String hetznerKey = 'hetznerKey';
|
static String hetznerKey = 'hetznerKey';
|
||||||
|
|
||||||
|
/// A String field of [serverInstallationBox] box.
|
||||||
|
static String serverProvider = 'serverProvider';
|
||||||
|
|
||||||
|
/// A String field of [serverLocation] box.
|
||||||
|
static String serverLocation = 'serverLocation';
|
||||||
|
|
||||||
/// A String field of [serverInstallationBox] box.
|
/// A String field of [serverInstallationBox] box.
|
||||||
static String cloudFlareKey = 'cloudFlareKey';
|
static String cloudFlareKey = 'cloudFlareKey';
|
||||||
|
|
||||||
|
/// A String field of [serverTypeIdentifier] box.
|
||||||
|
static String serverTypeIdentifier = 'serverTypeIdentifier';
|
||||||
|
|
||||||
/// A [User] field of [serverInstallationBox] box.
|
/// A [User] field of [serverInstallationBox] box.
|
||||||
static String rootUser = 'rootUser';
|
static String rootUser = 'rootUser';
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,27 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:graphql_flutter/graphql_flutter.dart';
|
import 'package:graphql_flutter/graphql_flutter.dart';
|
||||||
|
import 'package:http/io_client.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/staging_options.dart';
|
||||||
|
|
||||||
abstract class ApiMap {
|
abstract class ApiMap {
|
||||||
Future<GraphQLClient> getClient() async {
|
Future<GraphQLClient> getClient() async {
|
||||||
|
IOClient? ioClient;
|
||||||
|
if (StagingOptions.stagingAcme) {
|
||||||
|
final HttpClient httpClient = HttpClient();
|
||||||
|
httpClient.badCertificateCallback = (
|
||||||
|
final cert,
|
||||||
|
final host,
|
||||||
|
final port,
|
||||||
|
) =>
|
||||||
|
true;
|
||||||
|
ioClient = IOClient(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
final httpLink = HttpLink(
|
final httpLink = HttpLink(
|
||||||
'https://api.$rootAddress/graphql',
|
'https://api.$rootAddress/graphql',
|
||||||
|
httpClient: ioClient,
|
||||||
);
|
);
|
||||||
|
|
||||||
final String token = _getApiToken();
|
final String token = _getApiToken();
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:graphql/client.dart' as graphql;
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:selfprivacy/utils/scalars.dart';
|
import 'package:selfprivacy/utils/scalars.dart';
|
||||||
import 'schema.graphql.dart';
|
import 'schema.graphql.dart';
|
||||||
import 'services.graphql.dart';
|
import 'server_api.graphql.dart';
|
||||||
part 'disk_volumes.graphql.g.dart';
|
part 'disk_volumes.graphql.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
|
|
@ -173,6 +173,7 @@ input RecoveryKeyLimitsInput {
|
||||||
|
|
||||||
enum ServerProvider {
|
enum ServerProvider {
|
||||||
HETZNER
|
HETZNER
|
||||||
|
DIGITALOCEAN
|
||||||
}
|
}
|
||||||
|
|
||||||
type Service {
|
type Service {
|
||||||
|
|
|
@ -693,6 +693,8 @@ enum Enum$DnsProvider {
|
||||||
enum Enum$ServerProvider {
|
enum Enum$ServerProvider {
|
||||||
@JsonValue('HETZNER')
|
@JsonValue('HETZNER')
|
||||||
HETZNER,
|
HETZNER,
|
||||||
|
@JsonValue('DIGITALOCEAN')
|
||||||
|
DIGITALOCEAN,
|
||||||
$unknown
|
$unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,6 +64,14 @@ mutation RebootSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query SystemServerProvider {
|
||||||
|
system {
|
||||||
|
provider {
|
||||||
|
provider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
query GetApiTokens {
|
query GetApiTokens {
|
||||||
api {
|
api {
|
||||||
devices {
|
devices {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import 'package:graphql/client.dart' as graphql;
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:selfprivacy/utils/scalars.dart';
|
import 'package:selfprivacy/utils/scalars.dart';
|
||||||
import 'schema.graphql.dart';
|
import 'schema.graphql.dart';
|
||||||
import 'services.graphql.dart';
|
|
||||||
part 'server_api.graphql.g.dart';
|
part 'server_api.graphql.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
@ -3178,6 +3177,425 @@ class _CopyWithStubImpl$Mutation$RebootSystem$rebootSystem<TRes>
|
||||||
_res;
|
_res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class Query$SystemServerProvider {
|
||||||
|
Query$SystemServerProvider({required this.system, required this.$__typename});
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory Query$SystemServerProvider.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$Query$SystemServerProviderFromJson(json);
|
||||||
|
|
||||||
|
final Query$SystemServerProvider$system system;
|
||||||
|
|
||||||
|
@JsonKey(name: '__typename')
|
||||||
|
final String $__typename;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => _$Query$SystemServerProviderToJson(this);
|
||||||
|
int get hashCode {
|
||||||
|
final l$system = system;
|
||||||
|
final l$$__typename = $__typename;
|
||||||
|
return Object.hashAll([l$system, l$$__typename]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
if (!(other is Query$SystemServerProvider) ||
|
||||||
|
runtimeType != other.runtimeType) return false;
|
||||||
|
final l$system = system;
|
||||||
|
final lOther$system = other.system;
|
||||||
|
if (l$system != lOther$system) return false;
|
||||||
|
final l$$__typename = $__typename;
|
||||||
|
final lOther$$__typename = other.$__typename;
|
||||||
|
if (l$$__typename != lOther$$__typename) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UtilityExtension$Query$SystemServerProvider
|
||||||
|
on Query$SystemServerProvider {
|
||||||
|
CopyWith$Query$SystemServerProvider<Query$SystemServerProvider>
|
||||||
|
get copyWith => CopyWith$Query$SystemServerProvider(this, (i) => i);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CopyWith$Query$SystemServerProvider<TRes> {
|
||||||
|
factory CopyWith$Query$SystemServerProvider(
|
||||||
|
Query$SystemServerProvider instance,
|
||||||
|
TRes Function(Query$SystemServerProvider) then) =
|
||||||
|
_CopyWithImpl$Query$SystemServerProvider;
|
||||||
|
|
||||||
|
factory CopyWith$Query$SystemServerProvider.stub(TRes res) =
|
||||||
|
_CopyWithStubImpl$Query$SystemServerProvider;
|
||||||
|
|
||||||
|
TRes call({Query$SystemServerProvider$system? system, String? $__typename});
|
||||||
|
CopyWith$Query$SystemServerProvider$system<TRes> get system;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CopyWithImpl$Query$SystemServerProvider<TRes>
|
||||||
|
implements CopyWith$Query$SystemServerProvider<TRes> {
|
||||||
|
_CopyWithImpl$Query$SystemServerProvider(this._instance, this._then);
|
||||||
|
|
||||||
|
final Query$SystemServerProvider _instance;
|
||||||
|
|
||||||
|
final TRes Function(Query$SystemServerProvider) _then;
|
||||||
|
|
||||||
|
static const _undefined = {};
|
||||||
|
|
||||||
|
TRes call({Object? system = _undefined, Object? $__typename = _undefined}) =>
|
||||||
|
_then(Query$SystemServerProvider(
|
||||||
|
system: system == _undefined || system == null
|
||||||
|
? _instance.system
|
||||||
|
: (system as Query$SystemServerProvider$system),
|
||||||
|
$__typename: $__typename == _undefined || $__typename == null
|
||||||
|
? _instance.$__typename
|
||||||
|
: ($__typename as String)));
|
||||||
|
CopyWith$Query$SystemServerProvider$system<TRes> get system {
|
||||||
|
final local$system = _instance.system;
|
||||||
|
return CopyWith$Query$SystemServerProvider$system(
|
||||||
|
local$system, (e) => call(system: e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CopyWithStubImpl$Query$SystemServerProvider<TRes>
|
||||||
|
implements CopyWith$Query$SystemServerProvider<TRes> {
|
||||||
|
_CopyWithStubImpl$Query$SystemServerProvider(this._res);
|
||||||
|
|
||||||
|
TRes _res;
|
||||||
|
|
||||||
|
call({Query$SystemServerProvider$system? system, String? $__typename}) =>
|
||||||
|
_res;
|
||||||
|
CopyWith$Query$SystemServerProvider$system<TRes> get system =>
|
||||||
|
CopyWith$Query$SystemServerProvider$system.stub(_res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentNodeQuerySystemServerProvider = DocumentNode(definitions: [
|
||||||
|
OperationDefinitionNode(
|
||||||
|
type: OperationType.query,
|
||||||
|
name: NameNode(value: 'SystemServerProvider'),
|
||||||
|
variableDefinitions: [],
|
||||||
|
directives: [],
|
||||||
|
selectionSet: SelectionSetNode(selections: [
|
||||||
|
FieldNode(
|
||||||
|
name: NameNode(value: 'system'),
|
||||||
|
alias: null,
|
||||||
|
arguments: [],
|
||||||
|
directives: [],
|
||||||
|
selectionSet: SelectionSetNode(selections: [
|
||||||
|
FieldNode(
|
||||||
|
name: NameNode(value: 'provider'),
|
||||||
|
alias: null,
|
||||||
|
arguments: [],
|
||||||
|
directives: [],
|
||||||
|
selectionSet: SelectionSetNode(selections: [
|
||||||
|
FieldNode(
|
||||||
|
name: NameNode(value: 'provider'),
|
||||||
|
alias: null,
|
||||||
|
arguments: [],
|
||||||
|
directives: [],
|
||||||
|
selectionSet: null),
|
||||||
|
FieldNode(
|
||||||
|
name: NameNode(value: '__typename'),
|
||||||
|
alias: null,
|
||||||
|
arguments: [],
|
||||||
|
directives: [],
|
||||||
|
selectionSet: null)
|
||||||
|
])),
|
||||||
|
FieldNode(
|
||||||
|
name: NameNode(value: '__typename'),
|
||||||
|
alias: null,
|
||||||
|
arguments: [],
|
||||||
|
directives: [],
|
||||||
|
selectionSet: null)
|
||||||
|
])),
|
||||||
|
FieldNode(
|
||||||
|
name: NameNode(value: '__typename'),
|
||||||
|
alias: null,
|
||||||
|
arguments: [],
|
||||||
|
directives: [],
|
||||||
|
selectionSet: null)
|
||||||
|
])),
|
||||||
|
]);
|
||||||
|
Query$SystemServerProvider _parserFn$Query$SystemServerProvider(
|
||||||
|
Map<String, dynamic> data) =>
|
||||||
|
Query$SystemServerProvider.fromJson(data);
|
||||||
|
|
||||||
|
class Options$Query$SystemServerProvider
|
||||||
|
extends graphql.QueryOptions<Query$SystemServerProvider> {
|
||||||
|
Options$Query$SystemServerProvider(
|
||||||
|
{String? operationName,
|
||||||
|
graphql.FetchPolicy? fetchPolicy,
|
||||||
|
graphql.ErrorPolicy? errorPolicy,
|
||||||
|
graphql.CacheRereadPolicy? cacheRereadPolicy,
|
||||||
|
Object? optimisticResult,
|
||||||
|
Duration? pollInterval,
|
||||||
|
graphql.Context? context})
|
||||||
|
: super(
|
||||||
|
operationName: operationName,
|
||||||
|
fetchPolicy: fetchPolicy,
|
||||||
|
errorPolicy: errorPolicy,
|
||||||
|
cacheRereadPolicy: cacheRereadPolicy,
|
||||||
|
optimisticResult: optimisticResult,
|
||||||
|
pollInterval: pollInterval,
|
||||||
|
context: context,
|
||||||
|
document: documentNodeQuerySystemServerProvider,
|
||||||
|
parserFn: _parserFn$Query$SystemServerProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
class WatchOptions$Query$SystemServerProvider
|
||||||
|
extends graphql.WatchQueryOptions<Query$SystemServerProvider> {
|
||||||
|
WatchOptions$Query$SystemServerProvider(
|
||||||
|
{String? operationName,
|
||||||
|
graphql.FetchPolicy? fetchPolicy,
|
||||||
|
graphql.ErrorPolicy? errorPolicy,
|
||||||
|
graphql.CacheRereadPolicy? cacheRereadPolicy,
|
||||||
|
Object? optimisticResult,
|
||||||
|
graphql.Context? context,
|
||||||
|
Duration? pollInterval,
|
||||||
|
bool? eagerlyFetchResults,
|
||||||
|
bool carryForwardDataOnException = true,
|
||||||
|
bool fetchResults = false})
|
||||||
|
: super(
|
||||||
|
operationName: operationName,
|
||||||
|
fetchPolicy: fetchPolicy,
|
||||||
|
errorPolicy: errorPolicy,
|
||||||
|
cacheRereadPolicy: cacheRereadPolicy,
|
||||||
|
optimisticResult: optimisticResult,
|
||||||
|
context: context,
|
||||||
|
document: documentNodeQuerySystemServerProvider,
|
||||||
|
pollInterval: pollInterval,
|
||||||
|
eagerlyFetchResults: eagerlyFetchResults,
|
||||||
|
carryForwardDataOnException: carryForwardDataOnException,
|
||||||
|
fetchResults: fetchResults,
|
||||||
|
parserFn: _parserFn$Query$SystemServerProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
class FetchMoreOptions$Query$SystemServerProvider
|
||||||
|
extends graphql.FetchMoreOptions {
|
||||||
|
FetchMoreOptions$Query$SystemServerProvider(
|
||||||
|
{required graphql.UpdateQuery updateQuery})
|
||||||
|
: super(
|
||||||
|
updateQuery: updateQuery,
|
||||||
|
document: documentNodeQuerySystemServerProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ClientExtension$Query$SystemServerProvider on graphql.GraphQLClient {
|
||||||
|
Future<graphql.QueryResult<Query$SystemServerProvider>>
|
||||||
|
query$SystemServerProvider(
|
||||||
|
[Options$Query$SystemServerProvider? options]) async =>
|
||||||
|
await this.query(options ?? Options$Query$SystemServerProvider());
|
||||||
|
graphql.ObservableQuery<Query$SystemServerProvider>
|
||||||
|
watchQuery$SystemServerProvider(
|
||||||
|
[WatchOptions$Query$SystemServerProvider? options]) =>
|
||||||
|
this.watchQuery(options ?? WatchOptions$Query$SystemServerProvider());
|
||||||
|
void writeQuery$SystemServerProvider(
|
||||||
|
{required Query$SystemServerProvider data, bool broadcast = true}) =>
|
||||||
|
this.writeQuery(
|
||||||
|
graphql.Request(
|
||||||
|
operation: graphql.Operation(
|
||||||
|
document: documentNodeQuerySystemServerProvider)),
|
||||||
|
data: data.toJson(),
|
||||||
|
broadcast: broadcast);
|
||||||
|
Query$SystemServerProvider? readQuery$SystemServerProvider(
|
||||||
|
{bool optimistic = true}) {
|
||||||
|
final result = this.readQuery(
|
||||||
|
graphql.Request(
|
||||||
|
operation: graphql.Operation(
|
||||||
|
document: documentNodeQuerySystemServerProvider)),
|
||||||
|
optimistic: optimistic);
|
||||||
|
return result == null ? null : Query$SystemServerProvider.fromJson(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class Query$SystemServerProvider$system {
|
||||||
|
Query$SystemServerProvider$system(
|
||||||
|
{required this.provider, required this.$__typename});
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory Query$SystemServerProvider$system.fromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$Query$SystemServerProvider$systemFromJson(json);
|
||||||
|
|
||||||
|
final Query$SystemServerProvider$system$provider provider;
|
||||||
|
|
||||||
|
@JsonKey(name: '__typename')
|
||||||
|
final String $__typename;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$Query$SystemServerProvider$systemToJson(this);
|
||||||
|
int get hashCode {
|
||||||
|
final l$provider = provider;
|
||||||
|
final l$$__typename = $__typename;
|
||||||
|
return Object.hashAll([l$provider, l$$__typename]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
if (!(other is Query$SystemServerProvider$system) ||
|
||||||
|
runtimeType != other.runtimeType) return false;
|
||||||
|
final l$provider = provider;
|
||||||
|
final lOther$provider = other.provider;
|
||||||
|
if (l$provider != lOther$provider) return false;
|
||||||
|
final l$$__typename = $__typename;
|
||||||
|
final lOther$$__typename = other.$__typename;
|
||||||
|
if (l$$__typename != lOther$$__typename) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UtilityExtension$Query$SystemServerProvider$system
|
||||||
|
on Query$SystemServerProvider$system {
|
||||||
|
CopyWith$Query$SystemServerProvider$system<Query$SystemServerProvider$system>
|
||||||
|
get copyWith =>
|
||||||
|
CopyWith$Query$SystemServerProvider$system(this, (i) => i);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CopyWith$Query$SystemServerProvider$system<TRes> {
|
||||||
|
factory CopyWith$Query$SystemServerProvider$system(
|
||||||
|
Query$SystemServerProvider$system instance,
|
||||||
|
TRes Function(Query$SystemServerProvider$system) then) =
|
||||||
|
_CopyWithImpl$Query$SystemServerProvider$system;
|
||||||
|
|
||||||
|
factory CopyWith$Query$SystemServerProvider$system.stub(TRes res) =
|
||||||
|
_CopyWithStubImpl$Query$SystemServerProvider$system;
|
||||||
|
|
||||||
|
TRes call(
|
||||||
|
{Query$SystemServerProvider$system$provider? provider,
|
||||||
|
String? $__typename});
|
||||||
|
CopyWith$Query$SystemServerProvider$system$provider<TRes> get provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CopyWithImpl$Query$SystemServerProvider$system<TRes>
|
||||||
|
implements CopyWith$Query$SystemServerProvider$system<TRes> {
|
||||||
|
_CopyWithImpl$Query$SystemServerProvider$system(this._instance, this._then);
|
||||||
|
|
||||||
|
final Query$SystemServerProvider$system _instance;
|
||||||
|
|
||||||
|
final TRes Function(Query$SystemServerProvider$system) _then;
|
||||||
|
|
||||||
|
static const _undefined = {};
|
||||||
|
|
||||||
|
TRes call(
|
||||||
|
{Object? provider = _undefined, Object? $__typename = _undefined}) =>
|
||||||
|
_then(Query$SystemServerProvider$system(
|
||||||
|
provider: provider == _undefined || provider == null
|
||||||
|
? _instance.provider
|
||||||
|
: (provider as Query$SystemServerProvider$system$provider),
|
||||||
|
$__typename: $__typename == _undefined || $__typename == null
|
||||||
|
? _instance.$__typename
|
||||||
|
: ($__typename as String)));
|
||||||
|
CopyWith$Query$SystemServerProvider$system$provider<TRes> get provider {
|
||||||
|
final local$provider = _instance.provider;
|
||||||
|
return CopyWith$Query$SystemServerProvider$system$provider(
|
||||||
|
local$provider, (e) => call(provider: e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CopyWithStubImpl$Query$SystemServerProvider$system<TRes>
|
||||||
|
implements CopyWith$Query$SystemServerProvider$system<TRes> {
|
||||||
|
_CopyWithStubImpl$Query$SystemServerProvider$system(this._res);
|
||||||
|
|
||||||
|
TRes _res;
|
||||||
|
|
||||||
|
call(
|
||||||
|
{Query$SystemServerProvider$system$provider? provider,
|
||||||
|
String? $__typename}) =>
|
||||||
|
_res;
|
||||||
|
CopyWith$Query$SystemServerProvider$system$provider<TRes> get provider =>
|
||||||
|
CopyWith$Query$SystemServerProvider$system$provider.stub(_res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
class Query$SystemServerProvider$system$provider {
|
||||||
|
Query$SystemServerProvider$system$provider(
|
||||||
|
{required this.provider, required this.$__typename});
|
||||||
|
|
||||||
|
@override
|
||||||
|
factory Query$SystemServerProvider$system$provider.fromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
_$Query$SystemServerProvider$system$providerFromJson(json);
|
||||||
|
|
||||||
|
@JsonKey(unknownEnumValue: Enum$ServerProvider.$unknown)
|
||||||
|
final Enum$ServerProvider provider;
|
||||||
|
|
||||||
|
@JsonKey(name: '__typename')
|
||||||
|
final String $__typename;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() =>
|
||||||
|
_$Query$SystemServerProvider$system$providerToJson(this);
|
||||||
|
int get hashCode {
|
||||||
|
final l$provider = provider;
|
||||||
|
final l$$__typename = $__typename;
|
||||||
|
return Object.hashAll([l$provider, l$$__typename]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
if (!(other is Query$SystemServerProvider$system$provider) ||
|
||||||
|
runtimeType != other.runtimeType) return false;
|
||||||
|
final l$provider = provider;
|
||||||
|
final lOther$provider = other.provider;
|
||||||
|
if (l$provider != lOther$provider) return false;
|
||||||
|
final l$$__typename = $__typename;
|
||||||
|
final lOther$$__typename = other.$__typename;
|
||||||
|
if (l$$__typename != lOther$$__typename) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UtilityExtension$Query$SystemServerProvider$system$provider
|
||||||
|
on Query$SystemServerProvider$system$provider {
|
||||||
|
CopyWith$Query$SystemServerProvider$system$provider<
|
||||||
|
Query$SystemServerProvider$system$provider>
|
||||||
|
get copyWith =>
|
||||||
|
CopyWith$Query$SystemServerProvider$system$provider(this, (i) => i);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CopyWith$Query$SystemServerProvider$system$provider<TRes> {
|
||||||
|
factory CopyWith$Query$SystemServerProvider$system$provider(
|
||||||
|
Query$SystemServerProvider$system$provider instance,
|
||||||
|
TRes Function(Query$SystemServerProvider$system$provider) then) =
|
||||||
|
_CopyWithImpl$Query$SystemServerProvider$system$provider;
|
||||||
|
|
||||||
|
factory CopyWith$Query$SystemServerProvider$system$provider.stub(TRes res) =
|
||||||
|
_CopyWithStubImpl$Query$SystemServerProvider$system$provider;
|
||||||
|
|
||||||
|
TRes call({Enum$ServerProvider? provider, String? $__typename});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CopyWithImpl$Query$SystemServerProvider$system$provider<TRes>
|
||||||
|
implements CopyWith$Query$SystemServerProvider$system$provider<TRes> {
|
||||||
|
_CopyWithImpl$Query$SystemServerProvider$system$provider(
|
||||||
|
this._instance, this._then);
|
||||||
|
|
||||||
|
final Query$SystemServerProvider$system$provider _instance;
|
||||||
|
|
||||||
|
final TRes Function(Query$SystemServerProvider$system$provider) _then;
|
||||||
|
|
||||||
|
static const _undefined = {};
|
||||||
|
|
||||||
|
TRes call(
|
||||||
|
{Object? provider = _undefined, Object? $__typename = _undefined}) =>
|
||||||
|
_then(Query$SystemServerProvider$system$provider(
|
||||||
|
provider: provider == _undefined || provider == null
|
||||||
|
? _instance.provider
|
||||||
|
: (provider as Enum$ServerProvider),
|
||||||
|
$__typename: $__typename == _undefined || $__typename == null
|
||||||
|
? _instance.$__typename
|
||||||
|
: ($__typename as String)));
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CopyWithStubImpl$Query$SystemServerProvider$system$provider<TRes>
|
||||||
|
implements CopyWith$Query$SystemServerProvider$system$provider<TRes> {
|
||||||
|
_CopyWithStubImpl$Query$SystemServerProvider$system$provider(this._res);
|
||||||
|
|
||||||
|
TRes _res;
|
||||||
|
|
||||||
|
call({Enum$ServerProvider? provider, String? $__typename}) => _res;
|
||||||
|
}
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
class Query$GetApiTokens {
|
class Query$GetApiTokens {
|
||||||
Query$GetApiTokens({required this.api, required this.$__typename});
|
Query$GetApiTokens({required this.api, required this.$__typename});
|
||||||
|
|
|
@ -330,6 +330,58 @@ Map<String, dynamic> _$Mutation$RebootSystem$rebootSystemToJson(
|
||||||
'__typename': instance.$__typename,
|
'__typename': instance.$__typename,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Query$SystemServerProvider _$Query$SystemServerProviderFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
Query$SystemServerProvider(
|
||||||
|
system: Query$SystemServerProvider$system.fromJson(
|
||||||
|
json['system'] as Map<String, dynamic>),
|
||||||
|
$__typename: json['__typename'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$Query$SystemServerProviderToJson(
|
||||||
|
Query$SystemServerProvider instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'system': instance.system.toJson(),
|
||||||
|
'__typename': instance.$__typename,
|
||||||
|
};
|
||||||
|
|
||||||
|
Query$SystemServerProvider$system _$Query$SystemServerProvider$systemFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
Query$SystemServerProvider$system(
|
||||||
|
provider: Query$SystemServerProvider$system$provider.fromJson(
|
||||||
|
json['provider'] as Map<String, dynamic>),
|
||||||
|
$__typename: json['__typename'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$Query$SystemServerProvider$systemToJson(
|
||||||
|
Query$SystemServerProvider$system instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'provider': instance.provider.toJson(),
|
||||||
|
'__typename': instance.$__typename,
|
||||||
|
};
|
||||||
|
|
||||||
|
Query$SystemServerProvider$system$provider
|
||||||
|
_$Query$SystemServerProvider$system$providerFromJson(
|
||||||
|
Map<String, dynamic> json) =>
|
||||||
|
Query$SystemServerProvider$system$provider(
|
||||||
|
provider: $enumDecode(_$Enum$ServerProviderEnumMap, json['provider'],
|
||||||
|
unknownValue: Enum$ServerProvider.$unknown),
|
||||||
|
$__typename: json['__typename'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$Query$SystemServerProvider$system$providerToJson(
|
||||||
|
Query$SystemServerProvider$system$provider instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'provider': _$Enum$ServerProviderEnumMap[instance.provider]!,
|
||||||
|
'__typename': instance.$__typename,
|
||||||
|
};
|
||||||
|
|
||||||
|
const _$Enum$ServerProviderEnumMap = {
|
||||||
|
Enum$ServerProvider.HETZNER: 'HETZNER',
|
||||||
|
Enum$ServerProvider.DIGITALOCEAN: 'DIGITALOCEAN',
|
||||||
|
Enum$ServerProvider.$unknown: r'$unknown',
|
||||||
|
};
|
||||||
|
|
||||||
Query$GetApiTokens _$Query$GetApiTokensFromJson(Map<String, dynamic> json) =>
|
Query$GetApiTokens _$Query$GetApiTokensFromJson(Map<String, dynamic> json) =>
|
||||||
Query$GetApiTokens(
|
Query$GetApiTokens(
|
||||||
api: Query$GetApiTokens$api.fromJson(json['api'] as Map<String, dynamic>),
|
api: Query$GetApiTokens$api.fromJson(json['api'] as Map<String, dynamic>),
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:gql/ast.dart';
|
||||||
import 'package:graphql/client.dart' as graphql;
|
import 'package:graphql/client.dart' as graphql;
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'schema.graphql.dart';
|
import 'schema.graphql.dart';
|
||||||
import 'services.graphql.dart';
|
import 'server_api.graphql.dart';
|
||||||
part 'server_settings.graphql.g.dart';
|
part 'server_settings.graphql.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:graphql/client.dart' as graphql;
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:selfprivacy/utils/scalars.dart';
|
import 'package:selfprivacy/utils/scalars.dart';
|
||||||
import 'schema.graphql.dart';
|
import 'schema.graphql.dart';
|
||||||
|
import 'server_api.graphql.dart';
|
||||||
part 'services.graphql.g.dart';
|
part 'services.graphql.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:gql/ast.dart';
|
||||||
import 'package:graphql/client.dart' as graphql;
|
import 'package:graphql/client.dart' as graphql;
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'schema.graphql.dart';
|
import 'schema.graphql.dart';
|
||||||
import 'services.graphql.dart';
|
import 'server_api.graphql.dart';
|
||||||
part 'users.graphql.g.dart';
|
part 'users.graphql.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(explicitToJson: true)
|
@JsonSerializable(explicitToJson: true)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
part of 'server.dart';
|
part of 'server_api.dart';
|
||||||
|
|
||||||
mixin JobsApi on ApiMap {
|
mixin JobsApi on ApiMap {
|
||||||
Future<List<ServerJob>> getServerJobs() async {
|
Future<List<ServerJob>> getServerJobs() async {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
part of 'server.dart';
|
part of 'server_api.dart';
|
||||||
|
|
||||||
mixin ServerActionsApi on ApiMap {
|
mixin ServerActionsApi on ApiMap {
|
||||||
Future<bool> _commonBoolRequest(final Function graphQLMethod) async {
|
Future<bool> _commonBoolRequest(final Function graphQLMethod) async {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/services.graphql.
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/users.graphql.dart';
|
||||||
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/backup.dart';
|
import 'package:selfprivacy/logic/models/json/backup.dart';
|
||||||
|
@ -88,6 +89,25 @@ class ServerApi extends ApiMap
|
||||||
return apiVersion;
|
return apiVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<ServerProvider> getServerProviderType() async {
|
||||||
|
QueryResult<Query$SystemServerProvider> response;
|
||||||
|
ServerProvider providerType = ServerProvider.unknown;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final GraphQLClient client = await getClient();
|
||||||
|
response = await client.query$SystemServerProvider();
|
||||||
|
if (response.hasException) {
|
||||||
|
print(response.exception.toString());
|
||||||
|
}
|
||||||
|
providerType = ServerProvider.fromGraphQL(
|
||||||
|
response.parsedData!.system.provider.provider,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
return providerType;
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> isUsingBinds() async {
|
Future<bool> isUsingBinds() async {
|
||||||
QueryResult response;
|
QueryResult response;
|
||||||
bool usesBinds = false;
|
bool usesBinds = false;
|
|
@ -1,4 +1,4 @@
|
||||||
part of 'server.dart';
|
part of 'server_api.dart';
|
||||||
|
|
||||||
mixin ServicesApi on ApiMap {
|
mixin ServicesApi on ApiMap {
|
||||||
Future<List<Service>> getAllServices() async {
|
Future<List<Service>> getAllServices() async {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
part of 'server.dart';
|
part of 'server_api.dart';
|
||||||
|
|
||||||
mixin UsersApi on ApiMap {
|
mixin UsersApi on ApiMap {
|
||||||
Future<List<User>> getAllUsers() async {
|
Future<List<User>> getAllUsers() async {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
part of 'server.dart';
|
part of 'server_api.dart';
|
||||||
|
|
||||||
mixin VolumeApi on ApiMap {
|
mixin VolumeApi on ApiMap {
|
||||||
Future<List<ServerDiskVolume>> getServerDiskVolumes() async {
|
Future<List<ServerDiskVolume>> getServerDiskVolumes() async {
|
||||||
|
|
44
lib/logic/api_maps/rest_maps/api_controller.dart
Normal file
44
lib/logic/api_maps/rest_maps/api_controller.dart
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
||||||
|
|
||||||
|
class ApiController {
|
||||||
|
static VolumeProviderApiFactory? get currentVolumeProviderApiFactory =>
|
||||||
|
_volumeProviderApiFactory;
|
||||||
|
static DnsProviderApiFactory? get currentDnsProviderApiFactory =>
|
||||||
|
_dnsProviderApiFactory;
|
||||||
|
static ServerProviderApiFactory? get currentServerProviderApiFactory =>
|
||||||
|
_serverProviderApiFactory;
|
||||||
|
|
||||||
|
static void initVolumeProviderApiFactory(
|
||||||
|
final ServerProviderApiFactorySettings settings,
|
||||||
|
) {
|
||||||
|
_volumeProviderApiFactory =
|
||||||
|
VolumeApiFactoryCreator.createVolumeProviderApiFactory(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initDnsProviderApiFactory(
|
||||||
|
final DnsProviderApiFactorySettings settings,
|
||||||
|
) {
|
||||||
|
_dnsProviderApiFactory =
|
||||||
|
ApiFactoryCreator.createDnsProviderApiFactory(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initServerProviderApiFactory(
|
||||||
|
final ServerProviderApiFactorySettings settings,
|
||||||
|
) {
|
||||||
|
_serverProviderApiFactory =
|
||||||
|
ApiFactoryCreator.createServerProviderApiFactory(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearProviderApiFactories() {
|
||||||
|
_volumeProviderApiFactory = null;
|
||||||
|
_dnsProviderApiFactory = null;
|
||||||
|
_serverProviderApiFactory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VolumeProviderApiFactory? _volumeProviderApiFactory;
|
||||||
|
static DnsProviderApiFactory? _dnsProviderApiFactory;
|
||||||
|
static ServerProviderApiFactory? _serverProviderApiFactory;
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare_factory.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean_factory.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner_factory.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
@ -12,20 +14,22 @@ class UnknownApiProviderException implements Exception {
|
||||||
|
|
||||||
class ApiFactoryCreator {
|
class ApiFactoryCreator {
|
||||||
static ServerProviderApiFactory createServerProviderApiFactory(
|
static ServerProviderApiFactory createServerProviderApiFactory(
|
||||||
final ServerProvider provider,
|
final ServerProviderApiFactorySettings settings,
|
||||||
) {
|
) {
|
||||||
switch (provider) {
|
switch (settings.provider) {
|
||||||
case ServerProvider.hetzner:
|
case ServerProvider.hetzner:
|
||||||
return HetznerApiFactory();
|
return HetznerApiFactory(region: settings.location);
|
||||||
|
case ServerProvider.digitalOcean:
|
||||||
|
return DigitalOceanApiFactory(region: settings.location);
|
||||||
case ServerProvider.unknown:
|
case ServerProvider.unknown:
|
||||||
throw UnknownApiProviderException('Unknown server provider');
|
throw UnknownApiProviderException('Unknown server provider');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static DnsProviderApiFactory createDnsProviderApiFactory(
|
static DnsProviderApiFactory createDnsProviderApiFactory(
|
||||||
final DnsProvider provider,
|
final DnsProviderApiFactorySettings settings,
|
||||||
) {
|
) {
|
||||||
switch (provider) {
|
switch (settings.provider) {
|
||||||
case DnsProvider.cloudflare:
|
case DnsProvider.cloudflare:
|
||||||
return CloudflareApiFactory();
|
return CloudflareApiFactory();
|
||||||
case DnsProvider.unknown:
|
case DnsProvider.unknown:
|
||||||
|
@ -36,11 +40,13 @@ class ApiFactoryCreator {
|
||||||
|
|
||||||
class VolumeApiFactoryCreator {
|
class VolumeApiFactoryCreator {
|
||||||
static VolumeProviderApiFactory createVolumeProviderApiFactory(
|
static VolumeProviderApiFactory createVolumeProviderApiFactory(
|
||||||
final ServerProvider provider,
|
final ServerProviderApiFactorySettings settings,
|
||||||
) {
|
) {
|
||||||
switch (provider) {
|
switch (settings.provider) {
|
||||||
case ServerProvider.hetzner:
|
case ServerProvider.hetzner:
|
||||||
return HetznerApiFactory();
|
return HetznerApiFactory();
|
||||||
|
case ServerProvider.digitalOcean:
|
||||||
|
return DigitalOceanApiFactory();
|
||||||
case ServerProvider.unknown:
|
case ServerProvider.unknown:
|
||||||
throw UnknownApiProviderException('Unknown volume provider');
|
throw UnknownApiProviderException('Unknown volume provider');
|
||||||
}
|
}
|
||||||
|
|
20
lib/logic/api_maps/rest_maps/api_factory_settings.dart
Normal file
20
lib/logic/api_maps/rest_maps/api_factory_settings.dart
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
|
|
||||||
|
class ServerProviderApiFactorySettings {
|
||||||
|
ServerProviderApiFactorySettings({
|
||||||
|
required this.provider,
|
||||||
|
this.location,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ServerProvider provider;
|
||||||
|
final String? location;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DnsProviderApiFactorySettings {
|
||||||
|
DnsProviderApiFactorySettings({
|
||||||
|
required this.provider,
|
||||||
|
});
|
||||||
|
|
||||||
|
final DnsProvider provider;
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/models/message.dart';
|
import 'package:selfprivacy/logic/models/message.dart';
|
||||||
|
|
||||||
abstract class ApiMap {
|
abstract class ApiMap {
|
||||||
Future<Dio> getClient() async {
|
Future<Dio> getClient({final BaseOptions? customOptions}) async {
|
||||||
final Dio dio = Dio(await options);
|
final Dio dio = Dio(customOptions ?? (await options));
|
||||||
if (hasLogger) {
|
if (hasLogger) {
|
||||||
dio.interceptors.add(PrettyDioLogger());
|
dio.interceptors.add(PrettyDioLogger());
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,8 @@ abstract class ApiMap {
|
||||||
|
|
||||||
FutureOr<BaseOptions> get options;
|
FutureOr<BaseOptions> get options;
|
||||||
|
|
||||||
abstract final String rootAddress;
|
String get rootAddress;
|
||||||
|
|
||||||
abstract final bool hasLogger;
|
abstract final bool hasLogger;
|
||||||
abstract final bool isWithToken;
|
abstract final bool isWithToken;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/cloudflare/cloudflare.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_api_settings.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
||||||
|
|
||||||
class CloudflareApiFactory extends DnsProviderApiFactory {
|
class CloudflareApiFactory extends DnsProviderApiFactory {
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
|
||||||
|
|
||||||
|
class DnsProviderApiSettings extends ProviderApiSettings {
|
||||||
|
const DnsProviderApiSettings({
|
||||||
|
super.hasLogger = false,
|
||||||
|
super.isWithToken = true,
|
||||||
|
this.customToken,
|
||||||
|
});
|
||||||
|
final String? customToken;
|
||||||
|
}
|
|
@ -1,17 +1,8 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_api_settings.dart';
|
||||||
|
|
||||||
class DnsProviderApiSettings extends ProviderApiSettings {
|
|
||||||
const DnsProviderApiSettings({
|
|
||||||
super.hasLogger = false,
|
|
||||||
super.isWithToken = true,
|
|
||||||
this.customToken,
|
|
||||||
});
|
|
||||||
final String? customToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DnsProviderApiFactory {
|
abstract class DnsProviderApiFactory {
|
||||||
DnsProviderApi getDnsProvider({
|
DnsProviderApi getDnsProvider({
|
||||||
final DnsProviderApiSettings settings = const DnsProviderApiSettings(),
|
final DnsProviderApiSettings settings,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
class ProviderApiSettings {
|
class ProviderApiSettings {
|
||||||
const ProviderApiSettings({this.hasLogger = false, this.isWithToken = true});
|
const ProviderApiSettings({
|
||||||
|
this.hasLogger = false,
|
||||||
|
this.isWithToken = true,
|
||||||
|
});
|
||||||
final bool hasLogger;
|
final bool hasLogger;
|
||||||
final bool isWithToken;
|
final bool isWithToken;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,780 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/staging_options.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||||
|
import 'package:selfprivacy/utils/extensions/string_extensions.dart';
|
||||||
|
import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
|
class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
|
DigitalOceanApi({
|
||||||
|
required this.region,
|
||||||
|
this.hasLogger = false,
|
||||||
|
this.isWithToken = true,
|
||||||
|
});
|
||||||
|
@override
|
||||||
|
bool hasLogger;
|
||||||
|
@override
|
||||||
|
bool isWithToken;
|
||||||
|
|
||||||
|
final String? region;
|
||||||
|
|
||||||
|
@override
|
||||||
|
BaseOptions get options {
|
||||||
|
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
|
||||||
|
if (isWithToken) {
|
||||||
|
final String? token = getIt<ApiConfigModel>().serverProviderKey;
|
||||||
|
assert(token != null);
|
||||||
|
options.headers = {'Authorization': 'Bearer $token'};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validateStatus != null) {
|
||||||
|
options.validateStatus = validateStatus!;
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get rootAddress => 'https://api.digitalocean.com/v2';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get infectProviderName => 'digitalocean';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get displayProviderName => 'Digital Ocean';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isApiTokenValid(final String token) async {
|
||||||
|
bool isValid = false;
|
||||||
|
Response? response;
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
response = await client.get(
|
||||||
|
'/account',
|
||||||
|
options: Options(
|
||||||
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
isValid = false;
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
|
isValid = true;
|
||||||
|
} else if (response.statusCode == HttpStatus.unauthorized) {
|
||||||
|
isValid = false;
|
||||||
|
} else {
|
||||||
|
throw Exception('code: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hardcoded on their documentation and there is no pricing API at all
|
||||||
|
/// Probably we should scrap the doc page manually
|
||||||
|
@override
|
||||||
|
Future<Price?> getPricePerGb() async => Price(
|
||||||
|
value: 0.10,
|
||||||
|
currency: 'USD',
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ServerVolume?> createVolume() async {
|
||||||
|
ServerVolume? volume;
|
||||||
|
|
||||||
|
final Response createVolumeResponse;
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final List<ServerVolume> volumes = await getVolumes();
|
||||||
|
await Future.delayed(const Duration(seconds: 6));
|
||||||
|
|
||||||
|
createVolumeResponse = await client.post(
|
||||||
|
'/volumes',
|
||||||
|
data: {
|
||||||
|
'size_gigabytes': 10,
|
||||||
|
'name': 'volume${StringGenerators.storageName()}',
|
||||||
|
'labels': {'labelkey': 'value'},
|
||||||
|
'region': region,
|
||||||
|
'filesystem_type': 'ext4',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final volumeId = createVolumeResponse.data['volume']['id'];
|
||||||
|
final volumeSize = createVolumeResponse.data['volume']['size_gigabytes'];
|
||||||
|
final volumeName = createVolumeResponse.data['volume']['name'];
|
||||||
|
volume = ServerVolume(
|
||||||
|
id: volumes.length,
|
||||||
|
name: volumeName,
|
||||||
|
sizeByte: volumeSize,
|
||||||
|
serverId: null,
|
||||||
|
linuxDevice: '/dev/disk/by-id/scsi-0DO_Volume_$volumeName',
|
||||||
|
uuid: volumeId,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerVolume>> getVolumes({final String? status}) async {
|
||||||
|
final List<ServerVolume> volumes = [];
|
||||||
|
|
||||||
|
final Response getVolumesResponse;
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
getVolumesResponse = await client.get(
|
||||||
|
'/volumes',
|
||||||
|
queryParameters: {
|
||||||
|
'status': status,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final List<dynamic> rawVolumes = getVolumesResponse.data['volumes'];
|
||||||
|
int id = 0;
|
||||||
|
for (final rawVolume in rawVolumes) {
|
||||||
|
final volumeId = rawVolume['id'];
|
||||||
|
final int volumeSize = rawVolume['size_gigabytes'] * 1024 * 1024 * 1024;
|
||||||
|
final volumeDropletIds = rawVolume['droplet_ids'];
|
||||||
|
final String volumeName = rawVolume['name'];
|
||||||
|
final volume = ServerVolume(
|
||||||
|
id: id++,
|
||||||
|
name: volumeName,
|
||||||
|
sizeByte: volumeSize,
|
||||||
|
serverId: volumeDropletIds.isNotEmpty ? volumeDropletIds[0] : null,
|
||||||
|
linuxDevice: 'scsi-0DO_Volume_$volumeName',
|
||||||
|
uuid: volumeId,
|
||||||
|
);
|
||||||
|
volumes.add(volume);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return volumes;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ServerVolume?> getVolume(final String volumeUuid) async {
|
||||||
|
ServerVolume? requestedVolume;
|
||||||
|
|
||||||
|
final List<ServerVolume> volumes = await getVolumes();
|
||||||
|
|
||||||
|
for (final volume in volumes) {
|
||||||
|
if (volume.uuid == volumeUuid) {
|
||||||
|
requestedVolume = volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestedVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteVolume(final ServerVolume volume) async {
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
await client.delete('/volumes/${volume.uuid}');
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> attachVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final int serverId,
|
||||||
|
) async {
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
final Response attachVolumeResponse;
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
attachVolumeResponse = await client.post(
|
||||||
|
'/volumes/actions',
|
||||||
|
data: {
|
||||||
|
'type': 'attach',
|
||||||
|
'volume_name': volume.name,
|
||||||
|
'region': region,
|
||||||
|
'droplet_id': serverId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
success =
|
||||||
|
attachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> detachVolume(final ServerVolume volume) async {
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
final Response detachVolumeResponse;
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
detachVolumeResponse = await client.post(
|
||||||
|
'/volumes/actions',
|
||||||
|
data: {
|
||||||
|
'type': 'detach',
|
||||||
|
'volume_name': volume.name,
|
||||||
|
'droplet_id': volume.serverId,
|
||||||
|
'region': region,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
success =
|
||||||
|
detachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> resizeVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final DiskSize size,
|
||||||
|
) async {
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
final Response resizeVolumeResponse;
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
resizeVolumeResponse = await client.post(
|
||||||
|
'/volumes/actions',
|
||||||
|
data: {
|
||||||
|
'type': 'resize',
|
||||||
|
'volume_name': volume.name,
|
||||||
|
'size_gigabytes': size.gibibyte,
|
||||||
|
'region': region,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
success =
|
||||||
|
resizeVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getHostnameFromDomain(final String domain) {
|
||||||
|
// Replace all non-alphanumeric characters with an underscore
|
||||||
|
String hostname =
|
||||||
|
domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
||||||
|
if (hostname.endsWith('-')) {
|
||||||
|
hostname = hostname.substring(0, hostname.length - 1);
|
||||||
|
}
|
||||||
|
if (hostname.startsWith('-')) {
|
||||||
|
hostname = hostname.substring(1);
|
||||||
|
}
|
||||||
|
if (hostname.isEmpty) {
|
||||||
|
hostname = 'selfprivacy-server';
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ServerHostingDetails?> createServer({
|
||||||
|
required final String dnsApiToken,
|
||||||
|
required final User rootUser,
|
||||||
|
required final String domainName,
|
||||||
|
required final String serverType,
|
||||||
|
}) async {
|
||||||
|
ServerHostingDetails? serverDetails;
|
||||||
|
|
||||||
|
final String dbPassword = StringGenerators.dbPassword();
|
||||||
|
final String apiToken = StringGenerators.apiToken();
|
||||||
|
|
||||||
|
final String base64Password =
|
||||||
|
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
|
||||||
|
|
||||||
|
final String formattedHostname = getHostnameFromDomain(domainName);
|
||||||
|
const String infectBranch = 'providers/digital-ocean';
|
||||||
|
final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false';
|
||||||
|
|
||||||
|
final String userdataString =
|
||||||
|
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$formattedHostname bash 2>&1 | tee /tmp/infect.log";
|
||||||
|
print(userdataString);
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Map<String, Object> data = {
|
||||||
|
'name': formattedHostname,
|
||||||
|
'size': serverType,
|
||||||
|
'image': 'ubuntu-20-04-x64',
|
||||||
|
'user_data': userdataString,
|
||||||
|
'region': region!,
|
||||||
|
};
|
||||||
|
print('Decoded data: $data');
|
||||||
|
|
||||||
|
final Response serverCreateResponse = await client.post(
|
||||||
|
'/droplets',
|
||||||
|
data: data,
|
||||||
|
);
|
||||||
|
|
||||||
|
final int serverId = serverCreateResponse.data['droplet']['id'];
|
||||||
|
final ServerVolume? newVolume = await createVolume();
|
||||||
|
final bool attachedVolume = await attachVolume(newVolume!, serverId);
|
||||||
|
|
||||||
|
String? ipv4;
|
||||||
|
int attempts = 0;
|
||||||
|
while (attempts < 5 && ipv4 == null) {
|
||||||
|
await Future.delayed(const Duration(seconds: 20));
|
||||||
|
final List<ServerBasicInfo> servers = await getServers();
|
||||||
|
for (final server in servers) {
|
||||||
|
if (server.name == formattedHostname && server.ip != '0.0.0.0') {
|
||||||
|
ipv4 = server.ip;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++attempts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attachedVolume && ipv4 != null) {
|
||||||
|
serverDetails = ServerHostingDetails(
|
||||||
|
id: serverId,
|
||||||
|
ip4: ipv4,
|
||||||
|
createTime: DateTime.now(),
|
||||||
|
volume: newVolume,
|
||||||
|
apiToken: apiToken,
|
||||||
|
provider: ServerProvider.digitalOcean,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteServer({
|
||||||
|
required final String domainName,
|
||||||
|
}) async {
|
||||||
|
final Dio client = await getClient();
|
||||||
|
|
||||||
|
final ServerBasicInfo serverToRemove = (await getServers()).firstWhere(
|
||||||
|
(final el) => el.name == domainName,
|
||||||
|
);
|
||||||
|
final ServerVolume volumeToRemove = (await getVolumes()).firstWhere(
|
||||||
|
(final el) => el.serverId == serverToRemove.id,
|
||||||
|
);
|
||||||
|
final List<Future> laterFutures = <Future>[];
|
||||||
|
|
||||||
|
await detachVolume(volumeToRemove);
|
||||||
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
|
||||||
|
try {
|
||||||
|
laterFutures.add(deleteVolume(volumeToRemove));
|
||||||
|
laterFutures.add(client.delete('/droplets/$serverToRemove.id'));
|
||||||
|
await Future.wait(laterFutures);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ServerHostingDetails> restart() async {
|
||||||
|
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
await client.post(
|
||||||
|
'/droplets/${server.id}/actions',
|
||||||
|
data: {
|
||||||
|
'type': 'reboot',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.copyWith(startTime: DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ServerHostingDetails> powerOn() async {
|
||||||
|
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
await client.post(
|
||||||
|
'/droplets/${server.id}/actions',
|
||||||
|
data: {
|
||||||
|
'type': 'power_on',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return server.copyWith(startTime: DateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Digital Ocean returns a map of lists of /proc/stat values,
|
||||||
|
/// so here we are trying to implement average CPU
|
||||||
|
/// load calculation for each point in time on a given interval.
|
||||||
|
///
|
||||||
|
/// For each point of time:
|
||||||
|
///
|
||||||
|
/// `Average Load = 100 * (1 - (Idle Load / Total Load))`
|
||||||
|
///
|
||||||
|
/// For more info please proceed to read:
|
||||||
|
/// https://rosettacode.org/wiki/Linux_CPU_utilization
|
||||||
|
List<TimeSeriesData> calculateCpuLoadMetrics(final List rawProcStatMetrics) {
|
||||||
|
final List<TimeSeriesData> cpuLoads = [];
|
||||||
|
|
||||||
|
final int pointsInTime = (rawProcStatMetrics[0]['values'] as List).length;
|
||||||
|
for (int i = 0; i < pointsInTime; ++i) {
|
||||||
|
double currentMetricLoad = 0.0;
|
||||||
|
double? currentMetricIdle;
|
||||||
|
for (final rawProcStat in rawProcStatMetrics) {
|
||||||
|
final String rawProcValue = rawProcStat['values'][i][1];
|
||||||
|
// Converting MBit into bit
|
||||||
|
final double procValue = double.parse(rawProcValue) * 1000000;
|
||||||
|
currentMetricLoad += procValue;
|
||||||
|
if (currentMetricIdle == null &&
|
||||||
|
rawProcStat['metric']['mode'] == 'idle') {
|
||||||
|
currentMetricIdle = procValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentMetricIdle ??= 0.0;
|
||||||
|
currentMetricLoad = 100.0 * (1 - (currentMetricIdle / currentMetricLoad));
|
||||||
|
cpuLoads.add(
|
||||||
|
TimeSeriesData(
|
||||||
|
rawProcStatMetrics[0]['values'][i][0],
|
||||||
|
currentMetricLoad,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpuLoads;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ServerMetrics?> getMetrics(
|
||||||
|
final int serverId,
|
||||||
|
final DateTime start,
|
||||||
|
final DateTime end,
|
||||||
|
) async {
|
||||||
|
ServerMetrics? metrics;
|
||||||
|
|
||||||
|
const int step = 15;
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
Response response = await client.get(
|
||||||
|
'/monitoring/metrics/droplet/bandwidth',
|
||||||
|
queryParameters: {
|
||||||
|
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
|
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
|
'host_id': '$serverId',
|
||||||
|
'interface': 'public',
|
||||||
|
'direction': 'inbound',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final List inbound = response.data['data']['result'][0]['values'];
|
||||||
|
|
||||||
|
response = await client.get(
|
||||||
|
'/monitoring/metrics/droplet/bandwidth',
|
||||||
|
queryParameters: {
|
||||||
|
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
|
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
|
'host_id': '$serverId',
|
||||||
|
'interface': 'public',
|
||||||
|
'direction': 'outbound',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final List outbound = response.data['data']['result'][0]['values'];
|
||||||
|
|
||||||
|
response = await client.get(
|
||||||
|
'/monitoring/metrics/droplet/cpu',
|
||||||
|
queryParameters: {
|
||||||
|
'start': '${(start.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
|
'end': '${(end.microsecondsSinceEpoch / 1000000).round()}',
|
||||||
|
'host_id': '$serverId',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
metrics = ServerMetrics(
|
||||||
|
bandwidthIn: inbound
|
||||||
|
.map(
|
||||||
|
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
bandwidthOut: outbound
|
||||||
|
.map(
|
||||||
|
(final el) => TimeSeriesData(el[0], double.parse(el[1]) * 100000),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
cpu: calculateCpuLoadMetrics(response.data['data']['result']),
|
||||||
|
start: start,
|
||||||
|
end: end,
|
||||||
|
stepsInSecond: step,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerMetadataEntity>> getMetadata(final int serverId) async {
|
||||||
|
List<ServerMetadataEntity> metadata = [];
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Response response = await client.get('/droplets/$serverId');
|
||||||
|
final droplet = response.data!['droplet'];
|
||||||
|
metadata = [
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.id,
|
||||||
|
name: 'server.server_id'.tr(),
|
||||||
|
value: droplet['id'].toString(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.status,
|
||||||
|
name: 'server.status'.tr(),
|
||||||
|
value: droplet['status'].toString().capitalize(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.cpu,
|
||||||
|
name: 'server.cpu'.tr(),
|
||||||
|
value: 'server.core_count'.plural(droplet['vcpus']),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.ram,
|
||||||
|
name: 'server.ram'.tr(),
|
||||||
|
value: "${droplet['memory'].toString()} MB",
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.cost,
|
||||||
|
name: 'server.monthly_cost'.tr(),
|
||||||
|
value: droplet['size']['price_monthly'].toString(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.location,
|
||||||
|
name: 'server.location'.tr(),
|
||||||
|
value:
|
||||||
|
'${droplet['region']['name']} ${getEmojiFlag(droplet['region']['slug'].toString()) ?? ''}',
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.other,
|
||||||
|
name: 'server.provider'.tr(),
|
||||||
|
value: displayProviderName,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerBasicInfo>> getServers() async {
|
||||||
|
List<ServerBasicInfo> servers = [];
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Response response = await client.get('/droplets');
|
||||||
|
servers = response.data!['droplets'].map<ServerBasicInfo>(
|
||||||
|
(final server) {
|
||||||
|
String ipv4 = '0.0.0.0';
|
||||||
|
if (server['networks']['v4'].isNotEmpty) {
|
||||||
|
for (final v4 in server['networks']['v4']) {
|
||||||
|
if (v4['type'].toString() == 'public') {
|
||||||
|
ipv4 = v4['ip_address'].toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerBasicInfo(
|
||||||
|
id: server['id'],
|
||||||
|
reverseDns: server['name'],
|
||||||
|
created: DateTime.now(),
|
||||||
|
ip: ipv4,
|
||||||
|
name: server['name'],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
print(servers);
|
||||||
|
return servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? getEmojiFlag(final String query) {
|
||||||
|
String? emoji;
|
||||||
|
|
||||||
|
switch (query.toLowerCase().substring(0, 3)) {
|
||||||
|
case 'fra':
|
||||||
|
emoji = '🇩🇪';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ams':
|
||||||
|
emoji = '🇳🇱';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sgp':
|
||||||
|
emoji = '🇸🇬';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'lon':
|
||||||
|
emoji = '🇬🇧';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tor':
|
||||||
|
emoji = '🇨🇦';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'blr':
|
||||||
|
emoji = '🇮🇳';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'nyc':
|
||||||
|
case 'sfo':
|
||||||
|
emoji = '🇺🇸';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerProviderLocation>> getAvailableLocations() async {
|
||||||
|
List<ServerProviderLocation> locations = [];
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Response response = await client.get(
|
||||||
|
'/regions',
|
||||||
|
);
|
||||||
|
|
||||||
|
locations = response.data!['regions']
|
||||||
|
.map<ServerProviderLocation>(
|
||||||
|
(final location) => ServerProviderLocation(
|
||||||
|
title: location['slug'],
|
||||||
|
description: location['name'],
|
||||||
|
flag: getEmojiFlag(location['slug']),
|
||||||
|
identifier: location['slug'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerType>> getServerTypesByLocation({
|
||||||
|
required final ServerProviderLocation location,
|
||||||
|
}) async {
|
||||||
|
final List<ServerType> types = [];
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Response response = await client.get(
|
||||||
|
'/sizes',
|
||||||
|
);
|
||||||
|
final rawSizes = response.data!['sizes'];
|
||||||
|
for (final rawSize in rawSizes) {
|
||||||
|
for (final rawRegion in rawSize['regions']) {
|
||||||
|
if (rawRegion.toString() == location.identifier) {
|
||||||
|
types.add(
|
||||||
|
ServerType(
|
||||||
|
title: rawSize['description'],
|
||||||
|
identifier: rawSize['slug'],
|
||||||
|
ram: rawSize['memory'].toDouble(),
|
||||||
|
cores: rawSize['vcpus'],
|
||||||
|
disk: DiskSize(byte: rawSize['disk'] * 1024 * 1024 * 1024),
|
||||||
|
price: Price(
|
||||||
|
value: rawSize['price_monthly'],
|
||||||
|
currency: 'USD',
|
||||||
|
),
|
||||||
|
location: location,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> createReverseDns({
|
||||||
|
required final ServerHostingDetails serverDetails,
|
||||||
|
required final ServerDomain domain,
|
||||||
|
}) async {
|
||||||
|
/// TODO remove from provider interface
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ProviderApiTokenValidation getApiTokenValidation() =>
|
||||||
|
ProviderApiTokenValidation(
|
||||||
|
regexp: RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'),
|
||||||
|
length: 71,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/digital_ocean/digital_ocean.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_api_settings.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
||||||
|
|
||||||
|
class DigitalOceanApiFactory extends ServerProviderApiFactory
|
||||||
|
with VolumeProviderApiFactory {
|
||||||
|
DigitalOceanApiFactory({this.region});
|
||||||
|
|
||||||
|
final String? region;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ServerProviderApi getServerProvider({
|
||||||
|
final ServerProviderApiSettings settings =
|
||||||
|
const ServerProviderApiSettings(),
|
||||||
|
}) =>
|
||||||
|
DigitalOceanApi(
|
||||||
|
region: settings.region ?? region,
|
||||||
|
hasLogger: settings.hasLogger,
|
||||||
|
isWithToken: settings.isWithToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
VolumeProviderApi getVolumeProvider({
|
||||||
|
final ServerProviderApiSettings settings =
|
||||||
|
const ServerProviderApiSettings(),
|
||||||
|
}) =>
|
||||||
|
DigitalOceanApi(
|
||||||
|
region: settings.region ?? region,
|
||||||
|
hasLogger: settings.hasLogger,
|
||||||
|
isWithToken: settings.isWithToken,
|
||||||
|
);
|
||||||
|
}
|
|
@ -2,28 +2,43 @@ import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/staging_options.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||||
|
import 'package:selfprivacy/utils/extensions/string_extensions.dart';
|
||||||
import 'package:selfprivacy/utils/password_generator.dart';
|
import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
HetznerApi({this.hasLogger = false, this.isWithToken = true});
|
HetznerApi({
|
||||||
|
this.region,
|
||||||
|
this.hasLogger = false,
|
||||||
|
this.isWithToken = true,
|
||||||
|
});
|
||||||
@override
|
@override
|
||||||
bool hasLogger;
|
bool hasLogger;
|
||||||
@override
|
@override
|
||||||
bool isWithToken;
|
bool isWithToken;
|
||||||
|
|
||||||
|
final String? region;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BaseOptions get options {
|
BaseOptions get options {
|
||||||
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
|
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
|
||||||
if (isWithToken) {
|
if (isWithToken) {
|
||||||
final String? token = getIt<ApiConfigModel>().hetznerKey;
|
final String? token = getIt<ApiConfigModel>().serverProviderKey;
|
||||||
assert(token != null);
|
assert(token != null);
|
||||||
options.headers = {'Authorization': 'Bearer $token'};
|
options.headers = {'Authorization': 'Bearer $token'};
|
||||||
}
|
}
|
||||||
|
@ -36,7 +51,13 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String rootAddress = 'https://api.hetzner.cloud/v1';
|
String get rootAddress => 'https://api.hetzner.cloud/v1';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get infectProviderName => 'hetzner';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get displayProviderName => 'Hetzner';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> isApiTokenValid(final String token) async {
|
Future<bool> isApiTokenValid(final String token) async {
|
||||||
|
@ -71,19 +92,22 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RegExp getApiTokenValidation() =>
|
ProviderApiTokenValidation getApiTokenValidation() =>
|
||||||
RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
|
ProviderApiTokenValidation(
|
||||||
|
regexp: RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'),
|
||||||
|
length: 64,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<double?> getPricePerGb() async {
|
Future<Price?> getPricePerGb() async {
|
||||||
double? price;
|
double? price;
|
||||||
|
|
||||||
final Response dbGetResponse;
|
final Response pricingResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbGetResponse = await client.get('/pricing');
|
pricingResponse = await client.get('/pricing');
|
||||||
|
|
||||||
final volume = dbGetResponse.data['pricing']['volume'];
|
final volume = pricingResponse.data['pricing']['volume'];
|
||||||
final volumePrice = volume['price_per_gb_month']['gross'];
|
final volumePrice = volume['price_per_gb_month']['gross'];
|
||||||
price = double.parse(volumePrice);
|
price = double.parse(volumePrice);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -92,38 +116,43 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return price;
|
return price == null
|
||||||
|
? null
|
||||||
|
: Price(
|
||||||
|
value: price,
|
||||||
|
currency: 'EUR',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ServerVolume?> createVolume() async {
|
Future<ServerVolume?> createVolume() async {
|
||||||
ServerVolume? volume;
|
ServerVolume? volume;
|
||||||
|
|
||||||
final Response dbCreateResponse;
|
final Response createVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbCreateResponse = await client.post(
|
createVolumeResponse = await client.post(
|
||||||
'/volumes',
|
'/volumes',
|
||||||
data: {
|
data: {
|
||||||
'size': 10,
|
'size': 10,
|
||||||
'name': StringGenerators.dbStorageName(),
|
'name': StringGenerators.storageName(),
|
||||||
'labels': {'labelkey': 'value'},
|
'labels': {'labelkey': 'value'},
|
||||||
'location': 'fsn1',
|
'location': region,
|
||||||
'automount': false,
|
'automount': false,
|
||||||
'format': 'ext4'
|
'format': 'ext4'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final dbId = dbCreateResponse.data['volume']['id'];
|
final volumeId = createVolumeResponse.data['volume']['id'];
|
||||||
final dbSize = dbCreateResponse.data['volume']['size'];
|
final volumeSize = createVolumeResponse.data['volume']['size'];
|
||||||
final dbServer = dbCreateResponse.data['volume']['server'];
|
final volumeServer = createVolumeResponse.data['volume']['server'];
|
||||||
final dbName = dbCreateResponse.data['volume']['name'];
|
final volumeName = createVolumeResponse.data['volume']['name'];
|
||||||
final dbDevice = dbCreateResponse.data['volume']['linux_device'];
|
final volumeDevice = createVolumeResponse.data['volume']['linux_device'];
|
||||||
volume = ServerVolume(
|
volume = ServerVolume(
|
||||||
id: dbId,
|
id: volumeId,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: dbServer,
|
serverId: volumeServer,
|
||||||
linuxDevice: dbDevice,
|
linuxDevice: volumeDevice,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -138,28 +167,28 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
Future<List<ServerVolume>> getVolumes({final String? status}) async {
|
Future<List<ServerVolume>> getVolumes({final String? status}) async {
|
||||||
final List<ServerVolume> volumes = [];
|
final List<ServerVolume> volumes = [];
|
||||||
|
|
||||||
final Response dbGetResponse;
|
final Response getVolumesResonse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbGetResponse = await client.get(
|
getVolumesResonse = await client.get(
|
||||||
'/volumes',
|
'/volumes',
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'status': status,
|
'status': status,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final List<dynamic> rawVolumes = dbGetResponse.data['volumes'];
|
final List<dynamic> rawVolumes = getVolumesResonse.data['volumes'];
|
||||||
for (final rawVolume in rawVolumes) {
|
for (final rawVolume in rawVolumes) {
|
||||||
final int dbId = rawVolume['id'];
|
final int volumeId = rawVolume['id'];
|
||||||
final int dbSize = rawVolume['size'] * 1024 * 1024 * 1024;
|
final int volumeSize = rawVolume['size'] * 1024 * 1024 * 1024;
|
||||||
final dbServer = rawVolume['server'];
|
final volumeServer = rawVolume['server'];
|
||||||
final String dbName = rawVolume['name'];
|
final String volumeName = rawVolume['name'];
|
||||||
final dbDevice = rawVolume['linux_device'];
|
final volumeDevice = rawVolume['linux_device'];
|
||||||
final volume = ServerVolume(
|
final volume = ServerVolume(
|
||||||
id: dbId,
|
id: volumeId,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: dbServer,
|
serverId: volumeServer,
|
||||||
linuxDevice: dbDevice,
|
linuxDevice: volumeDevice,
|
||||||
);
|
);
|
||||||
volumes.add(volume);
|
volumes.add(volume);
|
||||||
}
|
}
|
||||||
|
@ -172,25 +201,26 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return volumes;
|
return volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Future<ServerVolume?> getVolume(
|
||||||
Future<ServerVolume?> getVolume(final int id) async {
|
final String volumeId,
|
||||||
|
) async {
|
||||||
ServerVolume? volume;
|
ServerVolume? volume;
|
||||||
|
|
||||||
final Response dbGetResponse;
|
final Response getVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbGetResponse = await client.get('/volumes/$id');
|
getVolumeResponse = await client.get('/volumes/$volumeId');
|
||||||
final int dbId = dbGetResponse.data['volume']['id'];
|
final int responseVolumeId = getVolumeResponse.data['volume']['id'];
|
||||||
final int dbSize = dbGetResponse.data['volume']['size'];
|
final int volumeSize = getVolumeResponse.data['volume']['size'];
|
||||||
final int dbServer = dbGetResponse.data['volume']['server'];
|
final int volumeServer = getVolumeResponse.data['volume']['server'];
|
||||||
final String dbName = dbGetResponse.data['volume']['name'];
|
final String volumeName = getVolumeResponse.data['volume']['name'];
|
||||||
final dbDevice = dbGetResponse.data['volume']['linux_device'];
|
final volumeDevice = getVolumeResponse.data['volume']['linux_device'];
|
||||||
volume = ServerVolume(
|
volume = ServerVolume(
|
||||||
id: dbId,
|
id: responseVolumeId,
|
||||||
name: dbName,
|
name: volumeName,
|
||||||
sizeByte: dbSize,
|
sizeByte: volumeSize,
|
||||||
serverId: dbServer,
|
serverId: volumeServer,
|
||||||
linuxDevice: dbDevice,
|
linuxDevice: volumeDevice,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -202,10 +232,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> deleteVolume(final int id) async {
|
Future<void> deleteVolume(final ServerVolume volume) async {
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
await client.delete('/volumes/$id');
|
await client.delete('/volumes/${volume.id}');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -214,20 +244,24 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> attachVolume(final int volumeId, final int serverId) async {
|
Future<bool> attachVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final int serverId,
|
||||||
|
) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response attachVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
attachVolumeResponse = await client.post(
|
||||||
'/volumes/$volumeId/actions/attach',
|
'/volumes/${volume.id}/actions/attach',
|
||||||
data: {
|
data: {
|
||||||
'automount': true,
|
'automount': true,
|
||||||
'server': serverId,
|
'server': serverId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
attachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -238,14 +272,17 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> detachVolume(final int volumeId) async {
|
Future<bool> detachVolume(final ServerVolume volume) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response detachVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post('/volumes/$volumeId/actions/detach');
|
detachVolumeResponse = await client.post(
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
'/volumes/${volume.id}/actions/detach',
|
||||||
|
);
|
||||||
|
success =
|
||||||
|
detachVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -256,19 +293,23 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> resizeVolume(final int volumeId, final int sizeGb) async {
|
Future<bool> resizeVolume(
|
||||||
|
final ServerVolume volume,
|
||||||
|
final DiskSize size,
|
||||||
|
) async {
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
final Response dbPostResponse;
|
final Response resizeVolumeResponse;
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
dbPostResponse = await client.post(
|
resizeVolumeResponse = await client.post(
|
||||||
'/volumes/$volumeId/actions/resize',
|
'/volumes/${volume.id}/actions/resize',
|
||||||
data: {
|
data: {
|
||||||
'size': sizeGb,
|
'size': size.gibibyte,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
success = dbPostResponse.data['action']['status'].toString() != 'error';
|
success =
|
||||||
|
resizeVolumeResponse.data['action']['status'].toString() != 'error';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -283,6 +324,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
required final String dnsApiToken,
|
required final String dnsApiToken,
|
||||||
required final User rootUser,
|
required final User rootUser,
|
||||||
required final String domainName,
|
required final String domainName,
|
||||||
|
required final String serverType,
|
||||||
}) async {
|
}) async {
|
||||||
ServerHostingDetails? details;
|
ServerHostingDetails? details;
|
||||||
|
|
||||||
|
@ -295,7 +337,8 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
dnsApiToken: dnsApiToken,
|
dnsApiToken: dnsApiToken,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
dataBase: newVolume,
|
volume: newVolume,
|
||||||
|
serverType: serverType,
|
||||||
);
|
);
|
||||||
|
|
||||||
return details;
|
return details;
|
||||||
|
@ -305,48 +348,43 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
required final String dnsApiToken,
|
required final String dnsApiToken,
|
||||||
required final User rootUser,
|
required final User rootUser,
|
||||||
required final String domainName,
|
required final String domainName,
|
||||||
required final ServerVolume dataBase,
|
required final ServerVolume volume,
|
||||||
|
required final String serverType,
|
||||||
}) async {
|
}) async {
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
|
||||||
final String dbPassword = StringGenerators.dbPassword();
|
final String dbPassword = StringGenerators.dbPassword();
|
||||||
final int dbId = dataBase.id;
|
final int volumeId = volume.id;
|
||||||
|
|
||||||
final String apiToken = StringGenerators.apiToken();
|
final String apiToken = StringGenerators.apiToken();
|
||||||
|
|
||||||
final String hostname = getHostnameFromDomain(domainName);
|
final String hostname = getHostnameFromDomain(domainName);
|
||||||
|
const String infectBranch = 'providers/hetzner';
|
||||||
|
final String stagingAcme = StagingOptions.stagingAcme ? 'true' : 'false';
|
||||||
final String base64Password =
|
final String base64Password =
|
||||||
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
|
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
|
||||||
|
|
||||||
print('hostname: $hostname');
|
|
||||||
|
|
||||||
/// add ssh key when you need it: e.g. "ssh_keys":["kherel"]
|
|
||||||
/// check the branch name, it could be "development" or "master".
|
|
||||||
///
|
|
||||||
final String userdataString =
|
final String userdataString =
|
||||||
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
|
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/$infectBranch/nixos-infect | STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
|
||||||
print(userdataString);
|
|
||||||
|
|
||||||
final Map<String, Object> data = {
|
|
||||||
'name': hostname,
|
|
||||||
'server_type': 'cx11',
|
|
||||||
'start_after_create': false,
|
|
||||||
'image': 'ubuntu-20.04',
|
|
||||||
'volumes': [dbId],
|
|
||||||
'networks': [],
|
|
||||||
'user_data': userdataString,
|
|
||||||
'labels': {},
|
|
||||||
'automount': true,
|
|
||||||
'location': 'fsn1'
|
|
||||||
};
|
|
||||||
print('Decoded data: $data');
|
|
||||||
|
|
||||||
ServerHostingDetails? serverDetails;
|
ServerHostingDetails? serverDetails;
|
||||||
DioError? hetznerError;
|
DioError? hetznerError;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
final Map<String, Object> data = {
|
||||||
|
'name': hostname,
|
||||||
|
'server_type': serverType,
|
||||||
|
'start_after_create': false,
|
||||||
|
'image': 'ubuntu-20.04',
|
||||||
|
'volumes': [volumeId],
|
||||||
|
'networks': [],
|
||||||
|
'user_data': userdataString,
|
||||||
|
'labels': {},
|
||||||
|
'automount': true,
|
||||||
|
'location': region!,
|
||||||
|
};
|
||||||
|
print('Decoded data: $data');
|
||||||
|
|
||||||
final Response serverCreateResponse = await client.post(
|
final Response serverCreateResponse = await client.post(
|
||||||
'/servers',
|
'/servers',
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -356,7 +394,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
id: serverCreateResponse.data['server']['id'],
|
id: serverCreateResponse.data['server']['id'],
|
||||||
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
|
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
|
||||||
createTime: DateTime.now(),
|
createTime: DateTime.now(),
|
||||||
volume: dataBase,
|
volume: volume,
|
||||||
apiToken: apiToken,
|
apiToken: apiToken,
|
||||||
provider: ServerProvider.hetzner,
|
provider: ServerProvider.hetzner,
|
||||||
);
|
);
|
||||||
|
@ -372,7 +410,7 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
await deleteVolume(dbId);
|
await deleteVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hetznerError != null) {
|
if (hetznerError != null) {
|
||||||
|
@ -459,14 +497,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return server.copyWith(startTime: DateTime.now());
|
return server.copyWith(startTime: DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getMetrics(
|
Future<Map<String, dynamic>> requestRawMetrics(
|
||||||
|
final int serverId,
|
||||||
final DateTime start,
|
final DateTime start,
|
||||||
final DateTime end,
|
final DateTime end,
|
||||||
final String type,
|
final String type,
|
||||||
) async {
|
) async {
|
||||||
final ServerHostingDetails? hetznerServer =
|
|
||||||
getIt<ApiConfigModel>().serverDetails;
|
|
||||||
|
|
||||||
Map<String, dynamic> metrics = {};
|
Map<String, dynamic> metrics = {};
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
|
@ -476,10 +512,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
'type': type
|
'type': type
|
||||||
};
|
};
|
||||||
final Response res = await client.get(
|
final Response res = await client.get(
|
||||||
'/servers/${hetznerServer!.id}/metrics',
|
'/servers/$serverId/metrics',
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
);
|
);
|
||||||
metrics = res.data;
|
metrics = res.data['metrics'];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -489,14 +525,115 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return metrics;
|
return metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerInfo> getInfo() async {
|
List<TimeSeriesData> serializeTimeSeries(
|
||||||
final ServerHostingDetails? hetznerServer =
|
final Map<String, dynamic> json,
|
||||||
getIt<ApiConfigModel>().serverDetails;
|
final String type,
|
||||||
final Dio client = await getClient();
|
) {
|
||||||
final Response response = await client.get('/servers/${hetznerServer!.id}');
|
final List list = json['time_series'][type]['values'];
|
||||||
close(client);
|
return list
|
||||||
|
.map((final el) => TimeSeriesData(el[0], double.parse(el[1])))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
return HetznerServerInfo.fromJson(response.data!['server']);
|
@override
|
||||||
|
Future<ServerMetrics?> getMetrics(
|
||||||
|
final int serverId,
|
||||||
|
final DateTime start,
|
||||||
|
final DateTime end,
|
||||||
|
) async {
|
||||||
|
ServerMetrics? metrics;
|
||||||
|
|
||||||
|
final Map<String, dynamic> rawCpuMetrics = await requestRawMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
'cpu',
|
||||||
|
);
|
||||||
|
final Map<String, dynamic> rawNetworkMetrics = await requestRawMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
'network',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rawNetworkMetrics.isEmpty || rawCpuMetrics.isEmpty) {
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics = ServerMetrics(
|
||||||
|
cpu: serializeTimeSeries(
|
||||||
|
rawCpuMetrics,
|
||||||
|
'cpu',
|
||||||
|
),
|
||||||
|
bandwidthIn: serializeTimeSeries(
|
||||||
|
rawNetworkMetrics,
|
||||||
|
'network.0.bandwidth.in',
|
||||||
|
),
|
||||||
|
bandwidthOut: serializeTimeSeries(
|
||||||
|
rawNetworkMetrics,
|
||||||
|
'network.0.bandwidth.out',
|
||||||
|
),
|
||||||
|
end: end,
|
||||||
|
start: start,
|
||||||
|
stepsInSecond: rawCpuMetrics['step'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerMetadataEntity>> getMetadata(final int serverId) async {
|
||||||
|
List<ServerMetadataEntity> metadata = [];
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Response response = await client.get('/servers/$serverId');
|
||||||
|
final hetznerInfo = HetznerServerInfo.fromJson(response.data!['server']);
|
||||||
|
metadata = [
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.id,
|
||||||
|
name: 'server.server_id'.tr(),
|
||||||
|
value: hetznerInfo.id.toString(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.status,
|
||||||
|
name: 'server.status'.tr(),
|
||||||
|
value: hetznerInfo.status.toString().split('.')[1].capitalize(),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.cpu,
|
||||||
|
name: 'server.cpu'.tr(),
|
||||||
|
value: 'server.core_count'.plural(hetznerInfo.serverType.cores),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.ram,
|
||||||
|
name: 'server.ram'.tr(),
|
||||||
|
value: '${hetznerInfo.serverType.memory.toString()} GB',
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.cost,
|
||||||
|
name: 'server.monthly_cost'.tr(),
|
||||||
|
value: hetznerInfo.serverType.prices[1].monthly.toStringAsFixed(2),
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.location,
|
||||||
|
name: 'server.location'.tr(),
|
||||||
|
value:
|
||||||
|
'${hetznerInfo.location.city}, ${hetznerInfo.location.country}',
|
||||||
|
),
|
||||||
|
ServerMetadataEntity(
|
||||||
|
type: MetadataType.other,
|
||||||
|
name: 'server.provider'.tr(),
|
||||||
|
value: displayProviderName,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -521,7 +658,6 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
ip: server.publicNet.ipv4.ip,
|
ip: server.publicNet.ipv4.ip,
|
||||||
reverseDns: server.publicNet.ipv4.reverseDns,
|
reverseDns: server.publicNet.ipv4.reverseDns,
|
||||||
created: server.created,
|
created: server.created,
|
||||||
volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -535,6 +671,96 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return servers;
|
return servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? getEmojiFlag(final String query) {
|
||||||
|
String? emoji;
|
||||||
|
|
||||||
|
switch (query.toLowerCase()) {
|
||||||
|
case 'de':
|
||||||
|
emoji = '🇩🇪';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'fi':
|
||||||
|
emoji = '🇫🇮';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'us':
|
||||||
|
emoji = '🇺🇸';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return emoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerProviderLocation>> getAvailableLocations() async {
|
||||||
|
List<ServerProviderLocation> locations = [];
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Response response = await client.get(
|
||||||
|
'/locations',
|
||||||
|
);
|
||||||
|
|
||||||
|
locations = response.data!['locations']
|
||||||
|
.map<ServerProviderLocation>(
|
||||||
|
(final location) => ServerProviderLocation(
|
||||||
|
title: location['city'],
|
||||||
|
description: location['description'],
|
||||||
|
flag: getEmojiFlag(location['country']),
|
||||||
|
identifier: location['name'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<ServerType>> getServerTypesByLocation({
|
||||||
|
required final ServerProviderLocation location,
|
||||||
|
}) async {
|
||||||
|
final List<ServerType> types = [];
|
||||||
|
|
||||||
|
final Dio client = await getClient();
|
||||||
|
try {
|
||||||
|
final Response response = await client.get(
|
||||||
|
'/server_types',
|
||||||
|
);
|
||||||
|
final rawTypes = response.data!['server_types'];
|
||||||
|
for (final rawType in rawTypes) {
|
||||||
|
for (final rawPrice in rawType['prices']) {
|
||||||
|
if (rawPrice['location'].toString() == location.identifier) {
|
||||||
|
types.add(
|
||||||
|
ServerType(
|
||||||
|
title: rawType['description'],
|
||||||
|
identifier: rawType['name'],
|
||||||
|
ram: rawType['memory'],
|
||||||
|
cores: rawType['cores'],
|
||||||
|
disk: DiskSize(byte: rawType['disk'] * 1024 * 1024 * 1024),
|
||||||
|
price: Price(
|
||||||
|
value: double.parse(rawPrice['price_monthly']['gross']),
|
||||||
|
currency: 'EUR',
|
||||||
|
),
|
||||||
|
location: location,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
} finally {
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> createReverseDns({
|
Future<void> createReverseDns({
|
||||||
required final ServerHostingDetails serverDetails,
|
required final ServerHostingDetails serverDetails,
|
||||||
|
|
|
@ -1,25 +1,33 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
|
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_api_settings.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
||||||
|
|
||||||
class HetznerApiFactory extends ServerProviderApiFactory
|
class HetznerApiFactory extends ServerProviderApiFactory
|
||||||
with VolumeProviderApiFactory {
|
with VolumeProviderApiFactory {
|
||||||
|
HetznerApiFactory({this.region});
|
||||||
|
|
||||||
|
final String? region;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ServerProviderApi getServerProvider({
|
ServerProviderApi getServerProvider({
|
||||||
final ProviderApiSettings settings = const ProviderApiSettings(),
|
final ServerProviderApiSettings settings =
|
||||||
|
const ServerProviderApiSettings(),
|
||||||
}) =>
|
}) =>
|
||||||
HetznerApi(
|
HetznerApi(
|
||||||
|
region: settings.region ?? region,
|
||||||
hasLogger: settings.hasLogger,
|
hasLogger: settings.hasLogger,
|
||||||
isWithToken: settings.isWithToken,
|
isWithToken: settings.isWithToken,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
VolumeProviderApi getVolumeProvider({
|
VolumeProviderApi getVolumeProvider({
|
||||||
final ProviderApiSettings settings = const ProviderApiSettings(),
|
final ServerProviderApiSettings settings =
|
||||||
|
const ServerProviderApiSettings(),
|
||||||
}) =>
|
}) =>
|
||||||
HetznerApi(
|
HetznerApi(
|
||||||
|
region: settings.region ?? region,
|
||||||
hasLogger: settings.hasLogger,
|
hasLogger: settings.hasLogger,
|
||||||
isWithToken: settings.isWithToken,
|
isWithToken: settings.isWithToken,
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,10 +2,27 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.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_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||||
|
|
||||||
|
class ProviderApiTokenValidation {
|
||||||
|
ProviderApiTokenValidation({
|
||||||
|
required this.length,
|
||||||
|
required this.regexp,
|
||||||
|
});
|
||||||
|
final int length;
|
||||||
|
final RegExp regexp;
|
||||||
|
}
|
||||||
|
|
||||||
abstract class ServerProviderApi extends ApiMap {
|
abstract class ServerProviderApi extends ApiMap {
|
||||||
Future<List<ServerBasicInfo>> getServers();
|
Future<List<ServerBasicInfo>> getServers();
|
||||||
|
Future<List<ServerProviderLocation>> getAvailableLocations();
|
||||||
|
Future<List<ServerType>> getServerTypesByLocation({
|
||||||
|
required final ServerProviderLocation location,
|
||||||
|
});
|
||||||
|
|
||||||
Future<ServerHostingDetails> restart();
|
Future<ServerHostingDetails> restart();
|
||||||
Future<ServerHostingDetails> powerOn();
|
Future<ServerHostingDetails> powerOn();
|
||||||
|
@ -15,6 +32,7 @@ abstract class ServerProviderApi extends ApiMap {
|
||||||
required final String dnsApiToken,
|
required final String dnsApiToken,
|
||||||
required final User rootUser,
|
required final User rootUser,
|
||||||
required final String domainName,
|
required final String domainName,
|
||||||
|
required final String serverType,
|
||||||
});
|
});
|
||||||
Future<void> createReverseDns({
|
Future<void> createReverseDns({
|
||||||
required final ServerHostingDetails serverDetails,
|
required final ServerHostingDetails serverDetails,
|
||||||
|
@ -22,5 +40,19 @@ abstract class ServerProviderApi extends ApiMap {
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<bool> isApiTokenValid(final String token);
|
Future<bool> isApiTokenValid(final String token);
|
||||||
RegExp getApiTokenValidation();
|
ProviderApiTokenValidation getApiTokenValidation();
|
||||||
|
Future<List<ServerMetadataEntity>> getMetadata(final int serverId);
|
||||||
|
Future<ServerMetrics?> getMetrics(
|
||||||
|
final int serverId,
|
||||||
|
final DateTime start,
|
||||||
|
final DateTime end,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Provider name key which lets infect understand what kind of installation
|
||||||
|
/// it requires, for example 'digitaloceal' for Digital Ocean
|
||||||
|
String get infectProviderName;
|
||||||
|
|
||||||
|
/// Actual provider name to render on information page for user,
|
||||||
|
/// for example 'Digital Ocean' for Digital Ocean
|
||||||
|
String get displayProviderName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
|
||||||
|
|
||||||
|
class ServerProviderApiSettings extends ProviderApiSettings {
|
||||||
|
const ServerProviderApiSettings({
|
||||||
|
this.region,
|
||||||
|
super.hasLogger = false,
|
||||||
|
super.isWithToken = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? region;
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
|
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_api_settings.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
|
||||||
|
|
||||||
abstract class ServerProviderApiFactory {
|
abstract class ServerProviderApiFactory {
|
||||||
ServerProviderApi getServerProvider({
|
ServerProviderApi getServerProvider({
|
||||||
final ProviderApiSettings settings = const ProviderApiSettings(),
|
final ServerProviderApiSettings settings,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin VolumeProviderApiFactory {
|
mixin VolumeProviderApiFactory {
|
||||||
VolumeProviderApi getVolumeProvider({
|
VolumeProviderApi getVolumeProvider({
|
||||||
final ProviderApiSettings settings = const ProviderApiSettings(),
|
final ServerProviderApiSettings settings,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
|
|
||||||
mixin VolumeProviderApi on ApiMap {
|
mixin VolumeProviderApi on ApiMap {
|
||||||
Future<ServerVolume?> createVolume();
|
Future<ServerVolume?> createVolume();
|
||||||
Future<List<ServerVolume>> getVolumes({final String? status});
|
Future<List<ServerVolume>> getVolumes({final String? status});
|
||||||
Future<ServerVolume?> getVolume(final int id);
|
Future<bool> attachVolume(final ServerVolume volume, final int serverId);
|
||||||
Future<bool> attachVolume(final int volumeId, final int serverId);
|
Future<bool> detachVolume(final ServerVolume volume);
|
||||||
Future<bool> detachVolume(final int volumeId);
|
Future<bool> resizeVolume(final ServerVolume volume, final DiskSize size);
|
||||||
Future<bool> resizeVolume(final int volumeId, final int sizeGb);
|
Future<void> deleteVolume(final ServerVolume volume);
|
||||||
Future<void> deleteVolume(final int id);
|
Future<Price?> getPricePerGb();
|
||||||
Future<double?> getPricePerGb();
|
|
||||||
}
|
}
|
||||||
|
|
8
lib/logic/api_maps/staging_options.dart
Normal file
8
lib/logic/api_maps/staging_options.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/// Controls staging environment for network, is used during manual
|
||||||
|
/// integration testing and such
|
||||||
|
class StagingOptions {
|
||||||
|
/// Whether we request for staging temprorary certificates.
|
||||||
|
/// Hardcode to 'true' in the middle of testing to not
|
||||||
|
/// get your domain banned by constant certificate renewal
|
||||||
|
static bool get stagingAcme => false;
|
||||||
|
}
|
|
@ -5,17 +5,6 @@ enum LoadingStatus {
|
||||||
error,
|
error,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum InitializingSteps {
|
|
||||||
setHetznerKey,
|
|
||||||
setCloudFlareKey,
|
|
||||||
setDomainName,
|
|
||||||
setRootUser,
|
|
||||||
createServer,
|
|
||||||
checkCloudFlareDns,
|
|
||||||
startServer,
|
|
||||||
checkSystemDnsAndDkimSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Period {
|
enum Period {
|
||||||
hour,
|
hour,
|
||||||
day,
|
day,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:async';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/backup.dart';
|
import 'package:selfprivacy/logic/models/json/backup.dart';
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/job.dart';
|
import 'package:selfprivacy/logic/models/job.dart';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/utils/network_utils.dart';
|
import 'package:selfprivacy/utils/network_utils.dart';
|
||||||
|
|
||||||
part 'dns_records_state.dart';
|
part 'dns_records_state.dart';
|
||||||
|
@ -19,11 +18,6 @@ class DnsRecordsCubit
|
||||||
const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing),
|
const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing),
|
||||||
);
|
);
|
||||||
|
|
||||||
DnsProviderApiFactory? dnsProviderApiFactory =
|
|
||||||
ApiFactoryCreator.createDnsProviderApiFactory(
|
|
||||||
DnsProvider.cloudflare, // TODO: HARDCODE FOR NOW!!!
|
|
||||||
); // TODO: Remove when provider selection is implemented.
|
|
||||||
|
|
||||||
final ServerApi api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -44,7 +38,8 @@ class DnsRecordsCubit
|
||||||
final String? ipAddress =
|
final String? ipAddress =
|
||||||
serverInstallationCubit.state.serverDetails?.ip4;
|
serverInstallationCubit.state.serverDetails?.ip4;
|
||||||
if (domain != null && ipAddress != null) {
|
if (domain != null && ipAddress != null) {
|
||||||
final List<DnsRecord> records = await dnsProviderApiFactory!
|
final List<DnsRecord> records = await ApiController
|
||||||
|
.currentDnsProviderApiFactory!
|
||||||
.getDnsProvider()
|
.getDnsProvider()
|
||||||
.getDnsRecords(domain: domain);
|
.getDnsRecords(domain: domain);
|
||||||
final String? dkimPublicKey =
|
final String? dkimPublicKey =
|
||||||
|
@ -124,7 +119,7 @@ class DnsRecordsCubit
|
||||||
final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
|
final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
|
||||||
final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4;
|
final String? ipAddress = serverInstallationCubit.state.serverDetails?.ip4;
|
||||||
final DnsProviderApi dnsProviderApi =
|
final DnsProviderApi dnsProviderApi =
|
||||||
dnsProviderApiFactory!.getDnsProvider();
|
ApiController.currentDnsProviderApiFactory!.getDnsProvider();
|
||||||
await dnsProviderApi.removeSimilarRecords(domain: domain!);
|
await dnsProviderApi.removeSimilarRecords(domain: domain!);
|
||||||
await dnsProviderApi.createMultipleDnsRecords(
|
await dnsProviderApi.createMultipleDnsRecords(
|
||||||
domain: domain,
|
domain: domain,
|
||||||
|
|
|
@ -7,15 +7,10 @@ import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
|
|
||||||
class DnsProviderFormCubit extends FormCubit {
|
class DnsProviderFormCubit extends FormCubit {
|
||||||
DnsProviderFormCubit(this.initializingCubit) {
|
DnsProviderFormCubit(this.initializingCubit) {
|
||||||
final RegExp regExp = initializingCubit.getDnsProviderApiTokenValidation();
|
|
||||||
apiKey = FieldCubit(
|
apiKey = FieldCubit(
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
|
||||||
regExp.hasMatch,
|
|
||||||
'validations.invalid_format'.tr(),
|
|
||||||
),
|
|
||||||
LengthStringNotEqualValidation(40)
|
LengthStringNotEqualValidation(40)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
|
|
||||||
|
@ -9,8 +10,7 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
emit(Loading(LoadingTypes.loadingDomain));
|
emit(Loading(LoadingTypes.loadingDomain));
|
||||||
final List<String> list = await serverInstallationCubit
|
final List<String> list = await ApiController.currentDnsProviderApiFactory!
|
||||||
.repository.dnsProviderApiFactory!
|
|
||||||
.getDnsProvider()
|
.getDnsProvider()
|
||||||
.domainList();
|
.domainList();
|
||||||
if (list.isEmpty) {
|
if (list.isEmpty) {
|
||||||
|
@ -31,8 +31,7 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
|
|
||||||
emit(Loading(LoadingTypes.saving));
|
emit(Loading(LoadingTypes.saving));
|
||||||
|
|
||||||
final String? zoneId = await serverInstallationCubit
|
final String? zoneId = await ApiController.currentDnsProviderApiFactory!
|
||||||
.repository.dnsProviderApiFactory!
|
|
||||||
.getDnsProvider()
|
.getDnsProvider()
|
||||||
.getZoneId(domainName);
|
.getZoneId(domainName);
|
||||||
|
|
||||||
|
|
|
@ -3,21 +3,16 @@ import 'dart:async';
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
|
||||||
|
|
||||||
class ProviderFormCubit extends FormCubit {
|
class ProviderFormCubit extends FormCubit {
|
||||||
ProviderFormCubit(this.serverInstallationCubit) {
|
ProviderFormCubit(this.serverInstallationCubit) {
|
||||||
final RegExp regExp =
|
//final int tokenLength =
|
||||||
serverInstallationCubit.getServerProviderApiTokenValidation();
|
// serverInstallationCubit.serverProviderApiTokenValidation().length;
|
||||||
apiKey = FieldCubit(
|
apiKey = FieldCubit(
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
//LengthStringNotEqualValidation(tokenLength),
|
||||||
regExp.hasMatch,
|
|
||||||
'validations.invalid_format'.tr(),
|
|
||||||
),
|
|
||||||
LengthStringNotEqualValidation(64)
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -26,7 +21,7 @@ class ProviderFormCubit extends FormCubit {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<void> onSubmit() async {
|
FutureOr<void> onSubmit() async {
|
||||||
serverInstallationCubit.setHetznerKey(apiKey.state.value);
|
serverInstallationCubit.setServerProviderKey(apiKey.state.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
final ServerInstallationCubit serverInstallationCubit;
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
@ -45,7 +40,7 @@ class ProviderFormCubit extends FormCubit {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isKeyValid) {
|
if (!isKeyValid) {
|
||||||
apiKey.setError('initializing.hetzner_bad_key_error'.tr());
|
apiKey.setError('initializing.provider_bad_key_error'.tr());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
|
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart';
|
|
||||||
|
|
||||||
class MetricsLoadException implements Exception {
|
|
||||||
MetricsLoadException(this.message);
|
|
||||||
final String message;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HetznerMetricsRepository {
|
|
||||||
Future<HetznerMetricsLoaded> getMetrics(final Period period) async {
|
|
||||||
final DateTime end = DateTime.now();
|
|
||||||
DateTime start;
|
|
||||||
|
|
||||||
switch (period) {
|
|
||||||
case Period.hour:
|
|
||||||
start = end.subtract(const Duration(hours: 1));
|
|
||||||
break;
|
|
||||||
case Period.day:
|
|
||||||
start = end.subtract(const Duration(days: 1));
|
|
||||||
break;
|
|
||||||
case Period.month:
|
|
||||||
start = end.subtract(const Duration(days: 15));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
final HetznerApi api = HetznerApi(hasLogger: false);
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> results = await Future.wait([
|
|
||||||
api.getMetrics(start, end, 'cpu'),
|
|
||||||
api.getMetrics(start, end, 'network'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
final cpuMetricsData = results[0]['metrics'];
|
|
||||||
final networkMetricsData = results[1]['metrics'];
|
|
||||||
|
|
||||||
if (cpuMetricsData == null || networkMetricsData == null) {
|
|
||||||
throw MetricsLoadException('Metrics data is null');
|
|
||||||
}
|
|
||||||
|
|
||||||
return HetznerMetricsLoaded(
|
|
||||||
period: period,
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
stepInSeconds: cpuMetricsData['step'],
|
|
||||||
cpu: timeSeriesSerializer(cpuMetricsData, 'cpu'),
|
|
||||||
ppsIn: timeSeriesSerializer(networkMetricsData, 'network.0.pps.in'),
|
|
||||||
ppsOut: timeSeriesSerializer(networkMetricsData, 'network.0.pps.out'),
|
|
||||||
bandwidthIn:
|
|
||||||
timeSeriesSerializer(networkMetricsData, 'network.0.bandwidth.in'),
|
|
||||||
bandwidthOut: timeSeriesSerializer(
|
|
||||||
networkMetricsData,
|
|
||||||
'network.0.bandwidth.out',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TimeSeriesData> timeSeriesSerializer(
|
|
||||||
final Map<String, dynamic> json,
|
|
||||||
final String type,
|
|
||||||
) {
|
|
||||||
final List list = json['time_series'][type]['values'];
|
|
||||||
return list
|
|
||||||
.map((final el) => TimeSeriesData(el[0], double.parse(el[1])))
|
|
||||||
.toList();
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
part of 'hetzner_metrics_cubit.dart';
|
|
||||||
|
|
||||||
abstract class HetznerMetricsState extends Equatable {
|
|
||||||
const HetznerMetricsState();
|
|
||||||
|
|
||||||
abstract final Period period;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HetznerMetricsLoading extends HetznerMetricsState {
|
|
||||||
const HetznerMetricsLoading(this.period);
|
|
||||||
@override
|
|
||||||
final Period period;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [period];
|
|
||||||
}
|
|
||||||
|
|
||||||
class HetznerMetricsLoaded extends HetznerMetricsState {
|
|
||||||
const HetznerMetricsLoaded({
|
|
||||||
required this.period,
|
|
||||||
required this.start,
|
|
||||||
required this.end,
|
|
||||||
required this.stepInSeconds,
|
|
||||||
required this.cpu,
|
|
||||||
required this.ppsIn,
|
|
||||||
required this.ppsOut,
|
|
||||||
required this.bandwidthIn,
|
|
||||||
required this.bandwidthOut,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
final Period period;
|
|
||||||
final DateTime start;
|
|
||||||
final DateTime end;
|
|
||||||
final num stepInSeconds;
|
|
||||||
|
|
||||||
final List<TimeSeriesData> cpu;
|
|
||||||
final List<TimeSeriesData> ppsIn;
|
|
||||||
final List<TimeSeriesData> ppsOut;
|
|
||||||
final List<TimeSeriesData> bandwidthIn;
|
|
||||||
final List<TimeSeriesData> bandwidthOut;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [period, start, end];
|
|
||||||
}
|
|
|
@ -3,16 +3,16 @@ import 'dart:async';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart';
|
import 'package:selfprivacy/logic/cubit/metrics/metrics_repository.dart';
|
||||||
|
|
||||||
part 'hetzner_metrics_state.dart';
|
part 'metrics_state.dart';
|
||||||
|
|
||||||
class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
class MetricsCubit extends Cubit<MetricsState> {
|
||||||
HetznerMetricsCubit() : super(const HetznerMetricsLoading(Period.day));
|
MetricsCubit() : super(const MetricsLoading(Period.day));
|
||||||
|
|
||||||
final HetznerMetricsRepository repository = HetznerMetricsRepository();
|
final MetricsRepository repository = MetricsRepository();
|
||||||
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
||||||
|
|
||||||
void changePeriod(final Period period) async {
|
void changePeriod(final Period period) async {
|
||||||
closeTimer();
|
closeTimer();
|
||||||
emit(HetznerMetricsLoading(period));
|
emit(MetricsLoading(period));
|
||||||
load(period);
|
load(period);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,14 +40,14 @@ class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
||||||
|
|
||||||
void load(final Period period) async {
|
void load(final Period period) async {
|
||||||
try {
|
try {
|
||||||
final HetznerMetricsLoaded newState = await repository.getMetrics(period);
|
final MetricsLoaded newState = await repository.getMetrics(period);
|
||||||
timer = Timer(
|
timer = Timer(
|
||||||
Duration(seconds: newState.stepInSeconds.toInt()),
|
Duration(seconds: newState.metrics.stepsInSecond.toInt()),
|
||||||
() => load(newState.period),
|
() => load(newState.period),
|
||||||
);
|
);
|
||||||
emit(newState);
|
emit(newState);
|
||||||
} on StateError {
|
} on StateError {
|
||||||
print('Tried to emit Hetzner metrics when cubit is closed');
|
print('Tried to emit metrics when cubit is closed');
|
||||||
} on MetricsLoadException {
|
} on MetricsLoadException {
|
||||||
timer = Timer(
|
timer = Timer(
|
||||||
Duration(seconds: state.period.stepPeriodInSeconds),
|
Duration(seconds: state.period.stepPeriodInSeconds),
|
52
lib/logic/cubit/metrics/metrics_repository.dart
Normal file
52
lib/logic/cubit/metrics/metrics_repository.dart
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart';
|
||||||
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
|
|
||||||
|
import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
|
|
||||||
|
class MetricsLoadException implements Exception {
|
||||||
|
MetricsLoadException(this.message);
|
||||||
|
final String message;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetricsRepository {
|
||||||
|
Future<MetricsLoaded> getMetrics(final Period period) async {
|
||||||
|
final providerApiFactory = ApiController.currentServerProviderApiFactory;
|
||||||
|
if (providerApiFactory == null) {
|
||||||
|
throw MetricsLoadException('Server Provider data is null');
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateTime end = DateTime.now();
|
||||||
|
DateTime start;
|
||||||
|
|
||||||
|
switch (period) {
|
||||||
|
case Period.hour:
|
||||||
|
start = end.subtract(const Duration(hours: 1));
|
||||||
|
break;
|
||||||
|
case Period.day:
|
||||||
|
start = end.subtract(const Duration(days: 1));
|
||||||
|
break;
|
||||||
|
case Period.month:
|
||||||
|
start = end.subtract(const Duration(days: 15));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final serverId = getIt<ApiConfigModel>().serverDetails!.id;
|
||||||
|
final ServerMetrics? metrics =
|
||||||
|
await providerApiFactory.getServerProvider().getMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (metrics == null) {
|
||||||
|
throw MetricsLoadException('Metrics data is null');
|
||||||
|
}
|
||||||
|
|
||||||
|
return MetricsLoaded(
|
||||||
|
period: period,
|
||||||
|
metrics: metrics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
31
lib/logic/cubit/metrics/metrics_state.dart
Normal file
31
lib/logic/cubit/metrics/metrics_state.dart
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
part of 'metrics_cubit.dart';
|
||||||
|
|
||||||
|
abstract class MetricsState extends Equatable {
|
||||||
|
const MetricsState();
|
||||||
|
|
||||||
|
abstract final Period period;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetricsLoading extends MetricsState {
|
||||||
|
const MetricsLoading(this.period);
|
||||||
|
@override
|
||||||
|
final Period period;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [period];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetricsLoaded extends MetricsState {
|
||||||
|
const MetricsLoaded({
|
||||||
|
required this.period,
|
||||||
|
required this.metrics,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Period period;
|
||||||
|
|
||||||
|
final ServerMetrics metrics;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [period, metrics];
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_status.dart';
|
import 'package:selfprivacy/logic/models/disk_status.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
|
|
||||||
part 'provider_volume_state.dart';
|
part 'provider_volume_state.dart';
|
||||||
|
|
||||||
|
@ -14,26 +15,19 @@ class ApiProviderVolumeCubit
|
||||||
extends ServerInstallationDependendCubit<ApiProviderVolumeState> {
|
extends ServerInstallationDependendCubit<ApiProviderVolumeState> {
|
||||||
ApiProviderVolumeCubit(final ServerInstallationCubit serverInstallationCubit)
|
ApiProviderVolumeCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(serverInstallationCubit, const ApiProviderVolumeState.initial());
|
: super(serverInstallationCubit, const ApiProviderVolumeState.initial());
|
||||||
|
|
||||||
VolumeProviderApiFactory? providerApi;
|
|
||||||
|
|
||||||
final ServerApi serverApi = ServerApi();
|
final ServerApi serverApi = ServerApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
final serverDetails = getIt<ApiConfigModel>().serverDetails;
|
|
||||||
providerApi = serverDetails == null
|
|
||||||
? null
|
|
||||||
: VolumeApiFactoryCreator.createVolumeProviderApiFactory(
|
|
||||||
getIt<ApiConfigModel>().serverDetails!.provider,
|
|
||||||
);
|
|
||||||
_refetch();
|
_refetch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<double?> getPricePerGb() async =>
|
Future<Price?> getPricePerGb() async =>
|
||||||
providerApi!.getVolumeProvider().getPricePerGb();
|
ApiController.currentVolumeProviderApiFactory!
|
||||||
|
.getVolumeProvider()
|
||||||
|
.getPricePerGb();
|
||||||
|
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
emit(const ApiProviderVolumeState([], LoadingStatus.refreshing, false));
|
emit(const ApiProviderVolumeState([], LoadingStatus.refreshing, false));
|
||||||
|
@ -41,12 +35,14 @@ class ApiProviderVolumeCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refetch() async {
|
Future<void> _refetch() async {
|
||||||
if (providerApi == null) {
|
if (ApiController.currentVolumeProviderApiFactory == null) {
|
||||||
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false));
|
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<ServerVolume> volumes =
|
final List<ServerVolume> volumes = await ApiController
|
||||||
await providerApi!.getVolumeProvider().getVolumes();
|
.currentVolumeProviderApiFactory!
|
||||||
|
.getVolumeProvider()
|
||||||
|
.getVolumes();
|
||||||
|
|
||||||
if (volumes.isEmpty) {
|
if (volumes.isEmpty) {
|
||||||
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false));
|
return emit(const ApiProviderVolumeState([], LoadingStatus.error, false));
|
||||||
|
@ -57,31 +53,33 @@ class ApiProviderVolumeCubit
|
||||||
|
|
||||||
Future<void> attachVolume(final DiskVolume volume) async {
|
Future<void> attachVolume(final DiskVolume volume) async {
|
||||||
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
|
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
|
||||||
await providerApi!
|
await ApiController.currentVolumeProviderApiFactory!
|
||||||
.getVolumeProvider()
|
.getVolumeProvider()
|
||||||
.attachVolume(volume.providerVolume!.id, server.id);
|
.attachVolume(volume.providerVolume!, server.id);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> detachVolume(final DiskVolume volume) async {
|
Future<void> detachVolume(final DiskVolume volume) async {
|
||||||
await providerApi!
|
await ApiController.currentVolumeProviderApiFactory!
|
||||||
.getVolumeProvider()
|
.getVolumeProvider()
|
||||||
.detachVolume(volume.providerVolume!.id);
|
.detachVolume(volume.providerVolume!);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> resizeVolume(
|
Future<bool> resizeVolume(
|
||||||
final DiskVolume volume,
|
final DiskVolume volume,
|
||||||
final int newSizeGb,
|
final DiskSize newSize,
|
||||||
final Function() callback,
|
final Function() callback,
|
||||||
) async {
|
) async {
|
||||||
getIt<NavigationService>().showSnackBar(
|
getIt<NavigationService>().showSnackBar(
|
||||||
'Starting resize',
|
'Starting resize',
|
||||||
);
|
);
|
||||||
emit(state.copyWith(isResizing: true));
|
emit(state.copyWith(isResizing: true));
|
||||||
final bool resized = await providerApi!.getVolumeProvider().resizeVolume(
|
final bool resized = await ApiController.currentVolumeProviderApiFactory!
|
||||||
volume.providerVolume!.id,
|
.getVolumeProvider()
|
||||||
newSizeGb,
|
.resizeVolume(
|
||||||
|
volume.providerVolume!,
|
||||||
|
newSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!resized) {
|
if (!resized) {
|
||||||
|
@ -93,13 +91,13 @@ class ApiProviderVolumeCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
getIt<NavigationService>().showSnackBar(
|
getIt<NavigationService>().showSnackBar(
|
||||||
'Hetzner resized, waiting 10 seconds',
|
'Provider volume resized, waiting 10 seconds',
|
||||||
);
|
);
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
|
||||||
await ServerApi().resizeVolume(volume.name);
|
await ServerApi().resizeVolume(volume.name);
|
||||||
getIt<NavigationService>().showSnackBar(
|
getIt<NavigationService>().showSnackBar(
|
||||||
'Server api resized, waiting 20 seconds',
|
'Server volume resized, waiting 20 seconds',
|
||||||
);
|
);
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 20));
|
await Future.delayed(const Duration(seconds: 20));
|
||||||
|
@ -115,8 +113,10 @@ class ApiProviderVolumeCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createVolume() async {
|
Future<void> createVolume() async {
|
||||||
final ServerVolume? volume =
|
final ServerVolume? volume = await ApiController
|
||||||
await providerApi!.getVolumeProvider().createVolume();
|
.currentVolumeProviderApiFactory!
|
||||||
|
.getVolumeProvider()
|
||||||
|
.createVolume();
|
||||||
|
|
||||||
final diskVolume = DiskVolume(providerVolume: volume);
|
final diskVolume = DiskVolume(providerVolume: volume);
|
||||||
await attachVolume(diskVolume);
|
await attachVolume(diskVolume);
|
||||||
|
@ -128,9 +128,9 @@ class ApiProviderVolumeCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteVolume(final DiskVolume volume) async {
|
Future<void> deleteVolume(final DiskVolume volume) async {
|
||||||
await providerApi!
|
await ApiController.currentVolumeProviderApiFactory!
|
||||||
.getVolumeProvider()
|
.getVolumeProvider()
|
||||||
.deleteVolume(volume.providerVolume!.id);
|
.deleteVolume(volume.providerVolume!);
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
|
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart';
|
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart';
|
||||||
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
||||||
|
|
||||||
part 'server_detailed_info_state.dart';
|
part 'server_detailed_info_state.dart';
|
||||||
|
@ -22,7 +22,7 @@ class ServerDetailsCubit
|
||||||
final ServerDetailsRepositoryDto data = await repository.load();
|
final ServerDetailsRepositoryDto data = await repository.load();
|
||||||
emit(
|
emit(
|
||||||
Loaded(
|
Loaded(
|
||||||
serverInfo: data.hetznerServerInfo,
|
metadata: data.metadata,
|
||||||
autoUpgradeSettings: data.autoUpgradeSettings,
|
autoUpgradeSettings: data.autoUpgradeSettings,
|
||||||
serverTimezone: data.serverTimezone,
|
serverTimezone: data.serverTimezone,
|
||||||
checkTime: DateTime.now(),
|
checkTime: DateTime.now(),
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart';
|
||||||
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
||||||
|
|
||||||
class ServerDetailsRepository {
|
class ServerDetailsRepository {
|
||||||
HetznerApi hetzner = HetznerApi();
|
|
||||||
ServerApi server = ServerApi();
|
ServerApi server = ServerApi();
|
||||||
|
|
||||||
Future<ServerDetailsRepositoryDto> load() async {
|
Future<ServerDetailsRepositoryDto> load() async {
|
||||||
|
final serverProviderApi = ApiController.currentServerProviderApiFactory;
|
||||||
final settings = await server.getSystemSettings();
|
final settings = await server.getSystemSettings();
|
||||||
|
final serverId = getIt<ApiConfigModel>().serverDetails!.id;
|
||||||
|
final metadata =
|
||||||
|
await serverProviderApi!.getServerProvider().getMetadata(serverId);
|
||||||
|
|
||||||
return ServerDetailsRepositoryDto(
|
return ServerDetailsRepositoryDto(
|
||||||
autoUpgradeSettings: settings.autoUpgradeSettings,
|
autoUpgradeSettings: settings.autoUpgradeSettings,
|
||||||
hetznerServerInfo: await hetzner.getInfo(),
|
metadata: metadata,
|
||||||
serverTimezone: TimeZoneSettings.fromString(
|
serverTimezone: TimeZoneSettings.fromString(
|
||||||
settings.timezone,
|
settings.timezone,
|
||||||
),
|
),
|
||||||
|
@ -36,13 +41,11 @@ class ServerDetailsRepository {
|
||||||
|
|
||||||
class ServerDetailsRepositoryDto {
|
class ServerDetailsRepositoryDto {
|
||||||
ServerDetailsRepositoryDto({
|
ServerDetailsRepositoryDto({
|
||||||
required this.hetznerServerInfo,
|
required this.metadata,
|
||||||
required this.serverTimezone,
|
required this.serverTimezone,
|
||||||
required this.autoUpgradeSettings,
|
required this.autoUpgradeSettings,
|
||||||
});
|
});
|
||||||
final HetznerServerInfo hetznerServerInfo;
|
final List<ServerMetadataEntity> metadata;
|
||||||
|
|
||||||
final TimeZoneSettings serverTimezone;
|
final TimeZoneSettings serverTimezone;
|
||||||
|
|
||||||
final AutoUpgradeSettings autoUpgradeSettings;
|
final AutoUpgradeSettings autoUpgradeSettings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,21 +17,19 @@ class Loading extends ServerDetailsState {}
|
||||||
|
|
||||||
class Loaded extends ServerDetailsState {
|
class Loaded extends ServerDetailsState {
|
||||||
const Loaded({
|
const Loaded({
|
||||||
required this.serverInfo,
|
required this.metadata,
|
||||||
required this.serverTimezone,
|
required this.serverTimezone,
|
||||||
required this.autoUpgradeSettings,
|
required this.autoUpgradeSettings,
|
||||||
required this.checkTime,
|
required this.checkTime,
|
||||||
});
|
});
|
||||||
final HetznerServerInfo serverInfo;
|
final List<ServerMetadataEntity> metadata;
|
||||||
|
|
||||||
final TimeZoneSettings serverTimezone;
|
final TimeZoneSettings serverTimezone;
|
||||||
|
|
||||||
final AutoUpgradeSettings autoUpgradeSettings;
|
final AutoUpgradeSettings autoUpgradeSettings;
|
||||||
final DateTime checkTime;
|
final DateTime checkTime;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [
|
List<Object> get props => [
|
||||||
serverInfo,
|
metadata,
|
||||||
serverTimezone,
|
serverTimezone,
|
||||||
autoUpgradeSettings,
|
autoUpgradeSettings,
|
||||||
checkTime,
|
checkTime,
|
||||||
|
|
|
@ -4,8 +4,12 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/provider_api_settings.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_api_settings.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_api_settings.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.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_domain.dart';
|
||||||
|
@ -13,6 +17,8 @@ import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||||
|
|
||||||
export 'package:provider/provider.dart';
|
export 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -51,40 +57,85 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExp getServerProviderApiTokenValidation() =>
|
void setServerProviderType(final ServerProvider providerType) async {
|
||||||
repository.serverProviderApiFactory!
|
await repository.saveServerProviderType(providerType);
|
||||||
|
ApiController.initServerProviderApiFactory(
|
||||||
|
ServerProviderApiFactorySettings(
|
||||||
|
provider: providerType,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProviderApiTokenValidation serverProviderApiTokenValidation() =>
|
||||||
|
ApiController.currentServerProviderApiFactory!
|
||||||
.getServerProvider()
|
.getServerProvider()
|
||||||
.getApiTokenValidation();
|
.getApiTokenValidation();
|
||||||
|
|
||||||
RegExp getDnsProviderApiTokenValidation() => repository.dnsProviderApiFactory!
|
RegExp getDnsProviderApiTokenValidation() =>
|
||||||
|
ApiController.currentDnsProviderApiFactory!
|
||||||
.getDnsProvider()
|
.getDnsProvider()
|
||||||
.getApiTokenValidation();
|
.getApiTokenValidation();
|
||||||
|
|
||||||
Future<bool> isServerProviderApiTokenValid(
|
Future<bool> isServerProviderApiTokenValid(
|
||||||
final String providerToken,
|
final String providerToken,
|
||||||
) async =>
|
) async =>
|
||||||
repository.serverProviderApiFactory!
|
ApiController.currentServerProviderApiFactory!
|
||||||
.getServerProvider(
|
.getServerProvider(
|
||||||
settings: const ProviderApiSettings(isWithToken: false),
|
settings: const ServerProviderApiSettings(
|
||||||
|
isWithToken: false,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.isApiTokenValid(providerToken);
|
.isApiTokenValid(providerToken);
|
||||||
|
|
||||||
Future<bool> isDnsProviderApiTokenValid(
|
Future<bool> isDnsProviderApiTokenValid(
|
||||||
final String providerToken,
|
final String providerToken,
|
||||||
) async =>
|
) async {
|
||||||
repository.dnsProviderApiFactory!
|
if (ApiController.currentDnsProviderApiFactory == null) {
|
||||||
|
// No other DNS provider is supported for now,
|
||||||
|
// so it's safe to hardcode Cloudflare
|
||||||
|
ApiController.initDnsProviderApiFactory(
|
||||||
|
DnsProviderApiFactorySettings(
|
||||||
|
provider: DnsProvider.cloudflare,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiController.currentDnsProviderApiFactory!
|
||||||
.getDnsProvider(
|
.getDnsProvider(
|
||||||
settings: const DnsProviderApiSettings(isWithToken: false),
|
settings: const DnsProviderApiSettings(isWithToken: false),
|
||||||
)
|
)
|
||||||
.isApiTokenValid(providerToken);
|
.isApiTokenValid(providerToken);
|
||||||
|
}
|
||||||
|
|
||||||
void setHetznerKey(final String hetznerKey) async {
|
Future<List<ServerProviderLocation>> fetchAvailableLocations() async {
|
||||||
await repository.saveHetznerKey(hetznerKey);
|
if (ApiController.currentServerProviderApiFactory == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiController.currentServerProviderApiFactory!
|
||||||
|
.getServerProvider()
|
||||||
|
.getAvailableLocations();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<ServerType>> fetchAvailableTypesByLocation(
|
||||||
|
final ServerProviderLocation location,
|
||||||
|
) async {
|
||||||
|
if (ApiController.currentServerProviderApiFactory == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiController.currentServerProviderApiFactory!
|
||||||
|
.getServerProvider()
|
||||||
|
.getServerTypesByLocation(location: location);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setServerProviderKey(final String serverProviderKey) async {
|
||||||
|
await repository.saveServerProviderKey(serverProviderKey);
|
||||||
|
|
||||||
if (state is ServerInstallationRecovery) {
|
if (state is ServerInstallationRecovery) {
|
||||||
emit(
|
emit(
|
||||||
(state as ServerInstallationRecovery).copyWith(
|
(state as ServerInstallationRecovery).copyWith(
|
||||||
providerApiToken: hetznerKey,
|
providerApiToken: serverProviderKey,
|
||||||
currentStep: RecoveryStep.serverSelection,
|
currentStep: RecoveryStep.serverSelection,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -93,7 +144,33 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
(state as ServerInstallationNotFinished).copyWith(
|
(state as ServerInstallationNotFinished).copyWith(
|
||||||
providerApiToken: hetznerKey,
|
providerApiToken: serverProviderKey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setServerType(final ServerType serverType) async {
|
||||||
|
await repository.saveServerType(serverType);
|
||||||
|
|
||||||
|
ApiController.initServerProviderApiFactory(
|
||||||
|
ServerProviderApiFactorySettings(
|
||||||
|
provider: getIt<ApiConfigModel>().serverProvider!,
|
||||||
|
location: serverType.location.identifier,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// All server providers support volumes for now,
|
||||||
|
// so it's safe to initialize.
|
||||||
|
ApiController.initVolumeProviderApiFactory(
|
||||||
|
ServerProviderApiFactorySettings(
|
||||||
|
provider: getIt<ApiConfigModel>().serverProvider!,
|
||||||
|
location: serverType.location.identifier,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(
|
||||||
|
(state as ServerInstallationNotFinished).copyWith(
|
||||||
|
serverTypeIdentificator: serverType.identifier,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -104,6 +181,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await repository.saveCloudFlareKey(cloudFlareKey);
|
await repository.saveCloudFlareKey(cloudFlareKey);
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
(state as ServerInstallationNotFinished)
|
(state as ServerInstallationNotFinished)
|
||||||
.copyWith(cloudFlareKey: cloudFlareKey),
|
.copyWith(cloudFlareKey: cloudFlareKey),
|
||||||
|
@ -248,14 +326,13 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
timer = Timer(pauseDuration, () async {
|
timer = Timer(pauseDuration, () async {
|
||||||
final ServerHostingDetails hetznerServerDetails =
|
final ServerHostingDetails serverDetails = await repository.restart();
|
||||||
await repository.restart();
|
|
||||||
await repository.saveIsServerResetedFirstTime(true);
|
await repository.saveIsServerResetedFirstTime(true);
|
||||||
await repository.saveServerDetails(hetznerServerDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
|
|
||||||
final ServerInstallationNotFinished newState = dataState.copyWith(
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
isServerResetedFirstTime: true,
|
isServerResetedFirstTime: true,
|
||||||
serverDetails: hetznerServerDetails,
|
serverDetails: serverDetails,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -290,14 +367,13 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
timer = Timer(pauseDuration, () async {
|
timer = Timer(pauseDuration, () async {
|
||||||
final ServerHostingDetails hetznerServerDetails =
|
final ServerHostingDetails serverDetails = await repository.restart();
|
||||||
await repository.restart();
|
|
||||||
await repository.saveIsServerResetedSecondTime(true);
|
await repository.saveIsServerResetedSecondTime(true);
|
||||||
await repository.saveServerDetails(hetznerServerDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
|
|
||||||
final ServerInstallationNotFinished newState = dataState.copyWith(
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
isServerResetedSecondTime: true,
|
isServerResetedSecondTime: true,
|
||||||
serverDetails: hetznerServerDetails,
|
serverDetails: serverDetails,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -423,11 +499,21 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
token,
|
token,
|
||||||
dataState.recoveryCapabilities,
|
dataState.recoveryCapabilities,
|
||||||
);
|
);
|
||||||
|
final ServerProvider provider = await ServerApi(
|
||||||
|
customToken: serverDetails.apiToken,
|
||||||
|
isWithToken: true,
|
||||||
|
).getServerProviderType();
|
||||||
|
if (provider == ServerProvider.unknown) {
|
||||||
|
getIt<NavigationService>()
|
||||||
|
.showSnackBar('recovering.generic_error'.tr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
await repository.saveServerDetails(serverDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
|
setServerProviderType(provider);
|
||||||
emit(
|
emit(
|
||||||
dataState.copyWith(
|
dataState.copyWith(
|
||||||
serverDetails: serverDetails,
|
serverDetails: serverDetails,
|
||||||
currentStep: RecoveryStep.hetznerToken,
|
currentStep: RecoveryStep.serverProviderToken,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} on ServerAuthorizationException {
|
} on ServerAuthorizationException {
|
||||||
|
@ -503,8 +589,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ServerBasicInfoWithValidators>>
|
Future<List<ServerBasicInfoWithValidators>> getAvailableServers() async {
|
||||||
getServersOnHetznerAccount() async {
|
|
||||||
final ServerInstallationRecovery dataState =
|
final ServerInstallationRecovery dataState =
|
||||||
state as ServerInstallationRecovery;
|
state as ServerInstallationRecovery;
|
||||||
final List<ServerBasicInfo> servers =
|
final List<ServerBasicInfo> servers =
|
||||||
|
@ -515,7 +600,9 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
serverBasicInfo: server,
|
serverBasicInfo: server,
|
||||||
isIpValid: server.ip == dataState.serverDetails?.ip4,
|
isIpValid: server.ip == dataState.serverDetails?.ip4,
|
||||||
isReverseDnsValid:
|
isReverseDnsValid:
|
||||||
server.reverseDns == dataState.serverDomain?.domainName,
|
server.reverseDns == dataState.serverDomain?.domainName ||
|
||||||
|
server.reverseDns ==
|
||||||
|
dataState.serverDomain?.domainName.split('.')[0],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return validated.toList();
|
return validated.toList();
|
||||||
|
@ -533,7 +620,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
id: server.id,
|
id: server.id,
|
||||||
createTime: server.created,
|
createTime: server.created,
|
||||||
volume: ServerVolume(
|
volume: ServerVolume(
|
||||||
id: server.volumeId,
|
id: 0,
|
||||||
name: 'recovered_volume',
|
name: 'recovered_volume',
|
||||||
sizeByte: 0,
|
sizeByte: 0,
|
||||||
serverId: server.id,
|
serverId: server.id,
|
||||||
|
@ -612,7 +699,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
|
|
||||||
void clearAppConfig() {
|
void clearAppConfig() {
|
||||||
closeTimer();
|
closeTimer();
|
||||||
|
ApiController.clearProviderApiFactories();
|
||||||
repository.clearAppConfig();
|
repository.clearAppConfig();
|
||||||
emit(const ServerInstallationEmpty());
|
emit(const ServerInstallationEmpty());
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,12 @@ import 'package:hive/hive.dart';
|
||||||
import 'package:pub_semver/pub_semver.dart';
|
import 'package:pub_semver/pub_semver.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_controller.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_factory.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/dns_providers/dns_provider_api_settings.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
@ -24,8 +24,8 @@ import 'package:selfprivacy/logic/models/json/device_token.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||||
import 'package:selfprivacy/logic/models/message.dart';
|
import 'package:selfprivacy/logic/models/message.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||||
import 'package:selfprivacy/utils/network_utils.dart';
|
import 'package:selfprivacy/utils/network_utils.dart';
|
||||||
|
|
||||||
class IpNotFoundException implements Exception {
|
class IpNotFoundException implements Exception {
|
||||||
|
@ -41,41 +41,52 @@ class ServerAuthorizationException implements Exception {
|
||||||
class ServerInstallationRepository {
|
class ServerInstallationRepository {
|
||||||
Box box = Hive.box(BNames.serverInstallationBox);
|
Box box = Hive.box(BNames.serverInstallationBox);
|
||||||
Box<User> usersBox = Hive.box(BNames.usersBox);
|
Box<User> usersBox = Hive.box(BNames.usersBox);
|
||||||
ServerProviderApiFactory? serverProviderApiFactory =
|
|
||||||
ApiFactoryCreator.createServerProviderApiFactory(
|
|
||||||
ServerProvider.hetzner, // TODO: HARDCODE FOR NOW!!!
|
|
||||||
); // TODO: Remove when provider selection is implemented.
|
|
||||||
DnsProviderApiFactory? dnsProviderApiFactory =
|
|
||||||
ApiFactoryCreator.createDnsProviderApiFactory(
|
|
||||||
DnsProvider.cloudflare, // TODO: HARDCODE FOR NOW!!!
|
|
||||||
);
|
|
||||||
|
|
||||||
Future<ServerInstallationState> load() async {
|
Future<ServerInstallationState> load() async {
|
||||||
final String? providerApiToken = getIt<ApiConfigModel>().hetznerKey;
|
final String? providerApiToken = getIt<ApiConfigModel>().serverProviderKey;
|
||||||
|
final String? location = getIt<ApiConfigModel>().serverLocation;
|
||||||
final String? cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey;
|
final String? cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey;
|
||||||
|
final String? serverTypeIdentificator = getIt<ApiConfigModel>().serverType;
|
||||||
final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
|
final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
|
||||||
|
final ServerProvider? serverProvider =
|
||||||
|
getIt<ApiConfigModel>().serverProvider;
|
||||||
final BackblazeCredential? backblazeCredential =
|
final BackblazeCredential? backblazeCredential =
|
||||||
getIt<ApiConfigModel>().backblazeCredential;
|
getIt<ApiConfigModel>().backblazeCredential;
|
||||||
final ServerHostingDetails? serverDetails =
|
final ServerHostingDetails? serverDetails =
|
||||||
getIt<ApiConfigModel>().serverDetails;
|
getIt<ApiConfigModel>().serverDetails;
|
||||||
|
|
||||||
if (serverDetails != null &&
|
if (serverProvider != null ||
|
||||||
serverDetails.provider != ServerProvider.unknown) {
|
(serverDetails != null &&
|
||||||
serverProviderApiFactory =
|
serverDetails.provider != ServerProvider.unknown)) {
|
||||||
ApiFactoryCreator.createServerProviderApiFactory(
|
ApiController.initServerProviderApiFactory(
|
||||||
serverDetails.provider,
|
ServerProviderApiFactorySettings(
|
||||||
|
provider: serverProvider ?? serverDetails!.provider,
|
||||||
|
location: location,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// All current providers support volumes
|
||||||
|
// so it's safe to hardcode for now
|
||||||
|
ApiController.initVolumeProviderApiFactory(
|
||||||
|
ServerProviderApiFactorySettings(
|
||||||
|
provider: serverProvider ?? serverDetails!.provider,
|
||||||
|
location: location,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serverDomain != null && serverDomain.provider != DnsProvider.unknown) {
|
if (serverDomain != null && serverDomain.provider != DnsProvider.unknown) {
|
||||||
dnsProviderApiFactory = ApiFactoryCreator.createDnsProviderApiFactory(
|
ApiController.initDnsProviderApiFactory(
|
||||||
serverDomain.provider,
|
DnsProviderApiFactorySettings(
|
||||||
|
provider: serverDomain.provider,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
|
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
|
||||||
return ServerInstallationFinished(
|
return ServerInstallationFinished(
|
||||||
providerApiToken: providerApiToken!,
|
providerApiToken: providerApiToken!,
|
||||||
|
serverTypeIdentificator: serverTypeIdentificator ?? '',
|
||||||
cloudFlareKey: cloudflareToken!,
|
cloudFlareKey: cloudflareToken!,
|
||||||
serverDomain: serverDomain!,
|
serverDomain: serverDomain!,
|
||||||
backblazeCredential: backblazeCredential!,
|
backblazeCredential: backblazeCredential!,
|
||||||
|
@ -126,13 +137,13 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
RecoveryStep _getCurrentRecoveryStep(
|
RecoveryStep _getCurrentRecoveryStep(
|
||||||
final String? hetznerToken,
|
final String? serverProviderToken,
|
||||||
final String? cloudflareToken,
|
final String? cloudflareToken,
|
||||||
final ServerDomain serverDomain,
|
final ServerDomain serverDomain,
|
||||||
final ServerHostingDetails? serverDetails,
|
final ServerHostingDetails? serverDetails,
|
||||||
) {
|
) {
|
||||||
if (serverDetails != null) {
|
if (serverDetails != null) {
|
||||||
if (hetznerToken != null) {
|
if (serverProviderToken != null) {
|
||||||
if (serverDetails.provider != ServerProvider.unknown) {
|
if (serverDetails.provider != ServerProvider.unknown) {
|
||||||
if (serverDomain.provider != DnsProvider.unknown) {
|
if (serverDomain.provider != DnsProvider.unknown) {
|
||||||
return RecoveryStep.backblazeToken;
|
return RecoveryStep.backblazeToken;
|
||||||
|
@ -141,7 +152,7 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
return RecoveryStep.serverSelection;
|
return RecoveryStep.serverSelection;
|
||||||
}
|
}
|
||||||
return RecoveryStep.hetznerToken;
|
return RecoveryStep.serverProviderToken;
|
||||||
}
|
}
|
||||||
return RecoveryStep.selecting;
|
return RecoveryStep.selecting;
|
||||||
}
|
}
|
||||||
|
@ -152,18 +163,20 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> startServer(
|
Future<ServerHostingDetails> startServer(
|
||||||
final ServerHostingDetails hetznerServer,
|
final ServerHostingDetails server,
|
||||||
) async {
|
) async {
|
||||||
ServerHostingDetails serverDetails;
|
ServerHostingDetails serverDetails;
|
||||||
|
|
||||||
final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
|
serverDetails = await ApiController.currentServerProviderApiFactory!
|
||||||
serverDetails = await api.powerOn();
|
.getServerProvider()
|
||||||
|
.powerOn();
|
||||||
|
|
||||||
return serverDetails;
|
return serverDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getDomainId(final String token, final String domain) async {
|
Future<String?> getDomainId(final String token, final String domain) async {
|
||||||
final DnsProviderApi dnsProviderApi = dnsProviderApiFactory!.getDnsProvider(
|
final DnsProviderApi dnsProviderApi =
|
||||||
|
ApiController.currentDnsProviderApiFactory!.getDnsProvider(
|
||||||
settings: DnsProviderApiSettings(
|
settings: DnsProviderApiSettings(
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
customToken: token,
|
customToken: token,
|
||||||
|
@ -232,12 +245,14 @@ class ServerInstallationRepository {
|
||||||
required final Future<void> Function(ServerHostingDetails serverDetails)
|
required final Future<void> Function(ServerHostingDetails serverDetails)
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}) async {
|
}) async {
|
||||||
final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
|
final ServerProviderApi api =
|
||||||
|
ApiController.currentServerProviderApiFactory!.getServerProvider();
|
||||||
try {
|
try {
|
||||||
final ServerHostingDetails? serverDetails = await api.createServer(
|
final ServerHostingDetails? serverDetails = await api.createServer(
|
||||||
dnsApiToken: cloudFlareKey,
|
dnsApiToken: cloudFlareKey,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
|
serverType: getIt<ApiConfigModel>().serverType!,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (serverDetails == null) {
|
if (serverDetails == null) {
|
||||||
|
@ -248,16 +263,11 @@ class ServerInstallationRepository {
|
||||||
onSuccess(serverDetails);
|
onSuccess(serverDetails);
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
if (e.response!.data['error']['code'] == 'uniqueness_error') {
|
if (e.response!.data['error']['code'] == 'uniqueness_error') {
|
||||||
final NavigationService nav = getIt.get<NavigationService>();
|
showPopUpAlert(
|
||||||
nav.showPopUpDialog(
|
alertTitle: 'modals.already_exists'.tr(),
|
||||||
BrandAlert(
|
description: 'modals.destroy_server'.tr(),
|
||||||
title: 'modals.already_exists'.tr(),
|
actionButtonTitle: 'modals.yes'.tr(),
|
||||||
contentText: 'modals.destroy_server'.tr(),
|
actionButtonOnPressed: () async {
|
||||||
actions: [
|
|
||||||
ActionButton(
|
|
||||||
text: 'basis.delete'.tr(),
|
|
||||||
isRed: true,
|
|
||||||
onPressed: () async {
|
|
||||||
await api.deleteServer(
|
await api.deleteServer(
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
);
|
);
|
||||||
|
@ -268,6 +278,7 @@ class ServerInstallationRepository {
|
||||||
dnsApiToken: cloudFlareKey,
|
dnsApiToken: cloudFlareKey,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
|
serverType: getIt<ApiConfigModel>().serverType!,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -280,31 +291,21 @@ class ServerInstallationRepository {
|
||||||
await saveServerDetails(serverDetails);
|
await saveServerDetails(serverDetails);
|
||||||
onSuccess(serverDetails);
|
onSuccess(serverDetails);
|
||||||
},
|
},
|
||||||
),
|
cancelButtonOnPressed: onCancel,
|
||||||
ActionButton(
|
|
||||||
text: 'basis.cancel'.tr(),
|
|
||||||
onPressed: onCancel,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final NavigationService nav = getIt.get<NavigationService>();
|
showPopUpAlert(
|
||||||
nav.showPopUpDialog(
|
alertTitle: 'modals.unexpected_error'.tr(),
|
||||||
BrandAlert(
|
description: 'modals.try_again'.tr(),
|
||||||
title: 'modals.unexpected_error'.tr(),
|
actionButtonTitle: 'modals.yes'.tr(),
|
||||||
contentText: 'modals.try_again'.tr(),
|
actionButtonOnPressed: () async {
|
||||||
actions: [
|
|
||||||
ActionButton(
|
|
||||||
text: 'modals.yes'.tr(),
|
|
||||||
isRed: true,
|
|
||||||
onPressed: () async {
|
|
||||||
ServerHostingDetails? serverDetails;
|
ServerHostingDetails? serverDetails;
|
||||||
try {
|
try {
|
||||||
serverDetails = await api.createServer(
|
serverDetails = await api.createServer(
|
||||||
dnsApiToken: cloudFlareKey,
|
dnsApiToken: cloudFlareKey,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
|
serverType: getIt<ApiConfigModel>().serverType!,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -317,13 +318,7 @@ class ServerInstallationRepository {
|
||||||
await saveServerDetails(serverDetails);
|
await saveServerDetails(serverDetails);
|
||||||
onSuccess(serverDetails);
|
onSuccess(serverDetails);
|
||||||
},
|
},
|
||||||
),
|
cancelButtonOnPressed: onCancel,
|
||||||
ActionButton(
|
|
||||||
text: 'basis.cancel'.tr(),
|
|
||||||
onPressed: onCancel,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,9 +330,9 @@ class ServerInstallationRepository {
|
||||||
required final void Function() onCancel,
|
required final void Function() onCancel,
|
||||||
}) async {
|
}) async {
|
||||||
final DnsProviderApi dnsProviderApi =
|
final DnsProviderApi dnsProviderApi =
|
||||||
dnsProviderApiFactory!.getDnsProvider();
|
ApiController.currentDnsProviderApiFactory!.getDnsProvider();
|
||||||
final ServerProviderApi serverApi =
|
final ServerProviderApi serverApi =
|
||||||
serverProviderApiFactory!.getServerProvider();
|
ApiController.currentServerProviderApiFactory!.getServerProvider();
|
||||||
|
|
||||||
await dnsProviderApi.removeSimilarRecords(
|
await dnsProviderApi.removeSimilarRecords(
|
||||||
ip4: serverDetails.ip4,
|
ip4: serverDetails.ip4,
|
||||||
|
@ -350,31 +345,19 @@ class ServerInstallationRepository {
|
||||||
domain: domain,
|
domain: domain,
|
||||||
);
|
);
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
final NavigationService nav = getIt.get<NavigationService>();
|
showPopUpAlert(
|
||||||
nav.showPopUpDialog(
|
alertTitle: e.response!.data['errors'][0]['code'] == 1038
|
||||||
BrandAlert(
|
|
||||||
title: e.response!.data['errors'][0]['code'] == 1038
|
|
||||||
? 'modals.you_cant_use_this_api'.tr()
|
? 'modals.you_cant_use_this_api'.tr()
|
||||||
: 'domain.error'.tr(),
|
: 'domain.error'.tr(),
|
||||||
contentText: 'modals.delete_server_volume'.tr(),
|
description: 'modals.delete_server_volume'.tr(),
|
||||||
actions: [
|
cancelButtonOnPressed: onCancel,
|
||||||
ActionButton(
|
actionButtonTitle: 'basis.delete'.tr(),
|
||||||
text: 'basis.delete'.tr(),
|
actionButtonOnPressed: () async {
|
||||||
isRed: true,
|
|
||||||
onPressed: () async {
|
|
||||||
await serverApi.deleteServer(
|
await serverApi.deleteServer(
|
||||||
domainName: domain.domainName,
|
domainName: domain.domainName,
|
||||||
);
|
);
|
||||||
|
|
||||||
onCancel();
|
onCancel();
|
||||||
},
|
},
|
||||||
),
|
|
||||||
ActionButton(
|
|
||||||
text: 'basis.cancel'.tr(),
|
|
||||||
onPressed: onCancel,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -389,7 +372,7 @@ class ServerInstallationRepository {
|
||||||
|
|
||||||
Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async {
|
Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async {
|
||||||
final DnsProviderApi dnsProviderApi =
|
final DnsProviderApi dnsProviderApi =
|
||||||
dnsProviderApiFactory!.getDnsProvider();
|
ApiController.currentDnsProviderApiFactory!.getDnsProvider();
|
||||||
final ServerApi api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
|
|
||||||
late DnsRecord record;
|
late DnsRecord record;
|
||||||
|
@ -408,15 +391,15 @@ class ServerInstallationRepository {
|
||||||
return api.isHttpServerWorking();
|
return api.isHttpServerWorking();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> restart() async {
|
Future<ServerHostingDetails> restart() async =>
|
||||||
final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
|
ApiController.currentServerProviderApiFactory!
|
||||||
return api.restart();
|
.getServerProvider()
|
||||||
}
|
.restart();
|
||||||
|
|
||||||
Future<ServerHostingDetails> powerOn() async {
|
Future<ServerHostingDetails> powerOn() async =>
|
||||||
final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
|
ApiController.currentServerProviderApiFactory!
|
||||||
return api.powerOn();
|
.getServerProvider()
|
||||||
}
|
.powerOn();
|
||||||
|
|
||||||
Future<ServerRecoveryCapabilities> getRecoveryCapabilities(
|
Future<ServerRecoveryCapabilities> getRecoveryCapabilities(
|
||||||
final ServerDomain serverDomain,
|
final ServerDomain serverDomain,
|
||||||
|
@ -651,10 +634,10 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ServerBasicInfo>> getServersOnProviderAccount() async {
|
Future<List<ServerBasicInfo>> getServersOnProviderAccount() async =>
|
||||||
final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
|
ApiController.currentServerProviderApiFactory!
|
||||||
return api.getServers();
|
.getServerProvider()
|
||||||
}
|
.getServers();
|
||||||
|
|
||||||
Future<void> saveServerDetails(
|
Future<void> saveServerDetails(
|
||||||
final ServerHostingDetails serverDetails,
|
final ServerHostingDetails serverDetails,
|
||||||
|
@ -667,12 +650,24 @@ class ServerInstallationRepository {
|
||||||
getIt<ApiConfigModel>().init();
|
getIt<ApiConfigModel>().init();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveHetznerKey(final String key) async {
|
Future<void> saveServerProviderType(final ServerProvider type) async {
|
||||||
print('saved');
|
await getIt<ApiConfigModel>().storeServerProviderType(type);
|
||||||
await getIt<ApiConfigModel>().storeHetznerKey(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteHetznerKey() async {
|
Future<void> saveServerProviderKey(final String key) async {
|
||||||
|
await getIt<ApiConfigModel>().storeServerProviderKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveServerType(final ServerType serverType) async {
|
||||||
|
await getIt<ApiConfigModel>().storeServerTypeIdentifier(
|
||||||
|
serverType.identifier,
|
||||||
|
);
|
||||||
|
await getIt<ApiConfigModel>().storeServerLocation(
|
||||||
|
serverType.location.identifier,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteServerProviderKey() async {
|
||||||
await box.delete(BNames.hetznerKey);
|
await box.delete(BNames.hetznerKey);
|
||||||
getIt<ApiConfigModel>().init();
|
getIt<ApiConfigModel>().init();
|
||||||
}
|
}
|
||||||
|
@ -731,11 +726,9 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteServer(final ServerDomain serverDomain) async {
|
Future<void> deleteServer(final ServerDomain serverDomain) async {
|
||||||
final ServerProviderApi api = serverProviderApiFactory!.getServerProvider();
|
await ApiController.currentServerProviderApiFactory!
|
||||||
final DnsProviderApi dnsProviderApi =
|
.getServerProvider()
|
||||||
dnsProviderApiFactory!.getDnsProvider();
|
.deleteServer(
|
||||||
|
|
||||||
await api.deleteServer(
|
|
||||||
domainName: serverDomain.domainName,
|
domainName: serverDomain.domainName,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -746,7 +739,9 @@ class ServerInstallationRepository {
|
||||||
await box.put(BNames.isLoading, false);
|
await box.put(BNames.isLoading, false);
|
||||||
await box.put(BNames.serverDetails, null);
|
await box.put(BNames.serverDetails, null);
|
||||||
|
|
||||||
await dnsProviderApi.removeSimilarRecords(domain: serverDomain);
|
await ApiController.currentDnsProviderApiFactory!
|
||||||
|
.getDnsProvider()
|
||||||
|
.removeSimilarRecords(domain: serverDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteServerRelatedRecords() async {
|
Future<void> deleteServerRelatedRecords() async {
|
||||||
|
|
|
@ -3,6 +3,7 @@ part of '../server_installation/server_installation_cubit.dart';
|
||||||
abstract class ServerInstallationState extends Equatable {
|
abstract class ServerInstallationState extends Equatable {
|
||||||
const ServerInstallationState({
|
const ServerInstallationState({
|
||||||
required this.providerApiToken,
|
required this.providerApiToken,
|
||||||
|
required this.serverTypeIdentificator,
|
||||||
required this.cloudFlareKey,
|
required this.cloudFlareKey,
|
||||||
required this.backblazeCredential,
|
required this.backblazeCredential,
|
||||||
required this.serverDomain,
|
required this.serverDomain,
|
||||||
|
@ -16,6 +17,7 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
providerApiToken,
|
providerApiToken,
|
||||||
|
serverTypeIdentificator,
|
||||||
cloudFlareKey,
|
cloudFlareKey,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
serverDomain,
|
serverDomain,
|
||||||
|
@ -27,6 +29,7 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
|
|
||||||
final String? providerApiToken;
|
final String? providerApiToken;
|
||||||
final String? cloudFlareKey;
|
final String? cloudFlareKey;
|
||||||
|
final String? serverTypeIdentificator;
|
||||||
final BackblazeCredential? backblazeCredential;
|
final BackblazeCredential? backblazeCredential;
|
||||||
final ServerDomain? serverDomain;
|
final ServerDomain? serverDomain;
|
||||||
final User? rootUser;
|
final User? rootUser;
|
||||||
|
@ -35,7 +38,8 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
final bool isServerResetedFirstTime;
|
final bool isServerResetedFirstTime;
|
||||||
final bool isServerResetedSecondTime;
|
final bool isServerResetedSecondTime;
|
||||||
|
|
||||||
bool get isServerProviderFilled => providerApiToken != null;
|
bool get isServerProviderApiKeyFilled => providerApiToken != null;
|
||||||
|
bool get isServerTypeFilled => serverTypeIdentificator != null;
|
||||||
bool get isDnsProviderFilled => cloudFlareKey != null;
|
bool get isDnsProviderFilled => cloudFlareKey != null;
|
||||||
bool get isBackupsProviderFilled => backblazeCredential != null;
|
bool get isBackupsProviderFilled => backblazeCredential != null;
|
||||||
bool get isDomainSelected => serverDomain != null;
|
bool get isDomainSelected => serverDomain != null;
|
||||||
|
@ -58,7 +62,8 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
|
|
||||||
List<bool?> get _fulfilementList {
|
List<bool?> get _fulfilementList {
|
||||||
final List<bool> res = [
|
final List<bool> res = [
|
||||||
isServerProviderFilled,
|
isServerProviderApiKeyFilled,
|
||||||
|
isServerTypeFilled,
|
||||||
isDnsProviderFilled,
|
isDnsProviderFilled,
|
||||||
isBackupsProviderFilled,
|
isBackupsProviderFilled,
|
||||||
isDomainSelected,
|
isDomainSelected,
|
||||||
|
@ -81,6 +86,7 @@ class TimerState extends ServerInstallationNotFinished {
|
||||||
this.duration,
|
this.duration,
|
||||||
}) : super(
|
}) : super(
|
||||||
providerApiToken: dataState.providerApiToken,
|
providerApiToken: dataState.providerApiToken,
|
||||||
|
serverTypeIdentificator: dataState.serverTypeIdentificator,
|
||||||
cloudFlareKey: dataState.cloudFlareKey,
|
cloudFlareKey: dataState.cloudFlareKey,
|
||||||
backblazeCredential: dataState.backblazeCredential,
|
backblazeCredential: dataState.backblazeCredential,
|
||||||
serverDomain: dataState.serverDomain,
|
serverDomain: dataState.serverDomain,
|
||||||
|
@ -106,7 +112,8 @@ class TimerState extends ServerInstallationNotFinished {
|
||||||
|
|
||||||
enum ServerSetupProgress {
|
enum ServerSetupProgress {
|
||||||
nothingYet,
|
nothingYet,
|
||||||
hetznerFilled,
|
serverProviderFilled,
|
||||||
|
servertTypeFilled,
|
||||||
cloudFlareFilled,
|
cloudFlareFilled,
|
||||||
backblazeFilled,
|
backblazeFilled,
|
||||||
domainFilled,
|
domainFilled,
|
||||||
|
@ -125,6 +132,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
required this.dnsMatches,
|
required this.dnsMatches,
|
||||||
super.providerApiToken,
|
super.providerApiToken,
|
||||||
|
super.serverTypeIdentificator,
|
||||||
super.cloudFlareKey,
|
super.cloudFlareKey,
|
||||||
super.backblazeCredential,
|
super.backblazeCredential,
|
||||||
super.serverDomain,
|
super.serverDomain,
|
||||||
|
@ -137,6 +145,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
providerApiToken,
|
providerApiToken,
|
||||||
|
serverTypeIdentificator,
|
||||||
cloudFlareKey,
|
cloudFlareKey,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
serverDomain,
|
serverDomain,
|
||||||
|
@ -150,6 +159,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationNotFinished copyWith({
|
ServerInstallationNotFinished copyWith({
|
||||||
final String? providerApiToken,
|
final String? providerApiToken,
|
||||||
|
final String? serverTypeIdentificator,
|
||||||
final String? cloudFlareKey,
|
final String? cloudFlareKey,
|
||||||
final BackblazeCredential? backblazeCredential,
|
final BackblazeCredential? backblazeCredential,
|
||||||
final ServerDomain? serverDomain,
|
final ServerDomain? serverDomain,
|
||||||
|
@ -163,6 +173,8 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
}) =>
|
}) =>
|
||||||
ServerInstallationNotFinished(
|
ServerInstallationNotFinished(
|
||||||
providerApiToken: providerApiToken ?? this.providerApiToken,
|
providerApiToken: providerApiToken ?? this.providerApiToken,
|
||||||
|
serverTypeIdentificator:
|
||||||
|
serverTypeIdentificator ?? this.serverTypeIdentificator,
|
||||||
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
|
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
|
||||||
backblazeCredential: backblazeCredential ?? this.backblazeCredential,
|
backblazeCredential: backblazeCredential ?? this.backblazeCredential,
|
||||||
serverDomain: serverDomain ?? this.serverDomain,
|
serverDomain: serverDomain ?? this.serverDomain,
|
||||||
|
@ -179,6 +191,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationFinished finish() => ServerInstallationFinished(
|
ServerInstallationFinished finish() => ServerInstallationFinished(
|
||||||
providerApiToken: providerApiToken!,
|
providerApiToken: providerApiToken!,
|
||||||
|
serverTypeIdentificator: serverTypeIdentificator ?? '',
|
||||||
cloudFlareKey: cloudFlareKey!,
|
cloudFlareKey: cloudFlareKey!,
|
||||||
backblazeCredential: backblazeCredential!,
|
backblazeCredential: backblazeCredential!,
|
||||||
serverDomain: serverDomain!,
|
serverDomain: serverDomain!,
|
||||||
|
@ -194,6 +207,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished {
|
||||||
const ServerInstallationEmpty()
|
const ServerInstallationEmpty()
|
||||||
: super(
|
: super(
|
||||||
providerApiToken: null,
|
providerApiToken: null,
|
||||||
|
serverTypeIdentificator: null,
|
||||||
cloudFlareKey: null,
|
cloudFlareKey: null,
|
||||||
backblazeCredential: null,
|
backblazeCredential: null,
|
||||||
serverDomain: null,
|
serverDomain: null,
|
||||||
|
@ -210,6 +224,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished {
|
||||||
class ServerInstallationFinished extends ServerInstallationState {
|
class ServerInstallationFinished extends ServerInstallationState {
|
||||||
const ServerInstallationFinished({
|
const ServerInstallationFinished({
|
||||||
required String super.providerApiToken,
|
required String super.providerApiToken,
|
||||||
|
required String super.serverTypeIdentificator,
|
||||||
required String super.cloudFlareKey,
|
required String super.cloudFlareKey,
|
||||||
required BackblazeCredential super.backblazeCredential,
|
required BackblazeCredential super.backblazeCredential,
|
||||||
required ServerDomain super.serverDomain,
|
required ServerDomain super.serverDomain,
|
||||||
|
@ -223,6 +238,7 @@ class ServerInstallationFinished extends ServerInstallationState {
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
providerApiToken,
|
providerApiToken,
|
||||||
|
serverTypeIdentificator,
|
||||||
cloudFlareKey,
|
cloudFlareKey,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
serverDomain,
|
serverDomain,
|
||||||
|
@ -238,7 +254,7 @@ enum RecoveryStep {
|
||||||
recoveryKey,
|
recoveryKey,
|
||||||
newDeviceKey,
|
newDeviceKey,
|
||||||
oldToken,
|
oldToken,
|
||||||
hetznerToken,
|
serverProviderToken,
|
||||||
serverSelection,
|
serverSelection,
|
||||||
cloudflareToken,
|
cloudflareToken,
|
||||||
backblazeToken,
|
backblazeToken,
|
||||||
|
@ -261,6 +277,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
required this.currentStep,
|
required this.currentStep,
|
||||||
required this.recoveryCapabilities,
|
required this.recoveryCapabilities,
|
||||||
super.providerApiToken,
|
super.providerApiToken,
|
||||||
|
super.serverTypeIdentificator,
|
||||||
super.cloudFlareKey,
|
super.cloudFlareKey,
|
||||||
super.backblazeCredential,
|
super.backblazeCredential,
|
||||||
super.serverDomain,
|
super.serverDomain,
|
||||||
|
@ -277,6 +294,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
providerApiToken,
|
providerApiToken,
|
||||||
|
serverTypeIdentificator,
|
||||||
cloudFlareKey,
|
cloudFlareKey,
|
||||||
backblazeCredential,
|
backblazeCredential,
|
||||||
serverDomain,
|
serverDomain,
|
||||||
|
@ -289,6 +307,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationRecovery copyWith({
|
ServerInstallationRecovery copyWith({
|
||||||
final String? providerApiToken,
|
final String? providerApiToken,
|
||||||
|
final String? serverTypeIdentificator,
|
||||||
final String? cloudFlareKey,
|
final String? cloudFlareKey,
|
||||||
final BackblazeCredential? backblazeCredential,
|
final BackblazeCredential? backblazeCredential,
|
||||||
final ServerDomain? serverDomain,
|
final ServerDomain? serverDomain,
|
||||||
|
@ -299,6 +318,8 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
}) =>
|
}) =>
|
||||||
ServerInstallationRecovery(
|
ServerInstallationRecovery(
|
||||||
providerApiToken: providerApiToken ?? this.providerApiToken,
|
providerApiToken: providerApiToken ?? this.providerApiToken,
|
||||||
|
serverTypeIdentificator:
|
||||||
|
serverTypeIdentificator ?? this.serverTypeIdentificator,
|
||||||
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
|
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
|
||||||
backblazeCredential: backblazeCredential ?? this.backblazeCredential,
|
backblazeCredential: backblazeCredential ?? this.backblazeCredential,
|
||||||
serverDomain: serverDomain ?? this.serverDomain,
|
serverDomain: serverDomain ?? this.serverDomain,
|
||||||
|
@ -310,6 +331,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
|
|
||||||
ServerInstallationFinished finish() => ServerInstallationFinished(
|
ServerInstallationFinished finish() => ServerInstallationFinished(
|
||||||
providerApiToken: providerApiToken!,
|
providerApiToken: providerApiToken!,
|
||||||
|
serverTypeIdentificator: serverTypeIdentificator ?? '',
|
||||||
cloudFlareKey: cloudFlareKey!,
|
cloudFlareKey: cloudFlareKey!,
|
||||||
backblazeCredential: backblazeCredential!,
|
backblazeCredential: backblazeCredential!,
|
||||||
serverDomain: serverDomain!,
|
serverDomain: serverDomain!,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/service.dart';
|
import 'package:selfprivacy/logic/models/service.dart';
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
|
||||||
|
|
|
@ -9,22 +9,33 @@ class ApiConfigModel {
|
||||||
final Box _box = Hive.box(BNames.serverInstallationBox);
|
final Box _box = Hive.box(BNames.serverInstallationBox);
|
||||||
|
|
||||||
ServerHostingDetails? get serverDetails => _serverDetails;
|
ServerHostingDetails? get serverDetails => _serverDetails;
|
||||||
String? get hetznerKey => _hetznerKey;
|
String? get serverProviderKey => _serverProviderKey;
|
||||||
|
String? get serverLocation => _serverLocation;
|
||||||
|
String? get serverType => _serverType;
|
||||||
String? get cloudFlareKey => _cloudFlareKey;
|
String? get cloudFlareKey => _cloudFlareKey;
|
||||||
|
ServerProvider? get serverProvider => _serverProvider;
|
||||||
BackblazeCredential? get backblazeCredential => _backblazeCredential;
|
BackblazeCredential? get backblazeCredential => _backblazeCredential;
|
||||||
ServerDomain? get serverDomain => _serverDomain;
|
ServerDomain? get serverDomain => _serverDomain;
|
||||||
BackblazeBucket? get backblazeBucket => _backblazeBucket;
|
BackblazeBucket? get backblazeBucket => _backblazeBucket;
|
||||||
|
|
||||||
String? _hetznerKey;
|
String? _serverProviderKey;
|
||||||
|
String? _serverLocation;
|
||||||
String? _cloudFlareKey;
|
String? _cloudFlareKey;
|
||||||
|
String? _serverType;
|
||||||
|
ServerProvider? _serverProvider;
|
||||||
ServerHostingDetails? _serverDetails;
|
ServerHostingDetails? _serverDetails;
|
||||||
BackblazeCredential? _backblazeCredential;
|
BackblazeCredential? _backblazeCredential;
|
||||||
ServerDomain? _serverDomain;
|
ServerDomain? _serverDomain;
|
||||||
BackblazeBucket? _backblazeBucket;
|
BackblazeBucket? _backblazeBucket;
|
||||||
|
|
||||||
Future<void> storeHetznerKey(final String value) async {
|
Future<void> storeServerProviderType(final ServerProvider value) async {
|
||||||
|
await _box.put(BNames.serverProvider, value);
|
||||||
|
_serverProvider = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeServerProviderKey(final String value) async {
|
||||||
await _box.put(BNames.hetznerKey, value);
|
await _box.put(BNames.hetznerKey, value);
|
||||||
_hetznerKey = value;
|
_serverProviderKey = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeCloudFlareKey(final String value) async {
|
Future<void> storeCloudFlareKey(final String value) async {
|
||||||
|
@ -32,6 +43,16 @@ class ApiConfigModel {
|
||||||
_cloudFlareKey = value;
|
_cloudFlareKey = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> storeServerTypeIdentifier(final String typeIdentifier) async {
|
||||||
|
await _box.put(BNames.serverTypeIdentifier, typeIdentifier);
|
||||||
|
_serverType = typeIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeServerLocation(final String serverLocation) async {
|
||||||
|
await _box.put(BNames.serverLocation, serverLocation);
|
||||||
|
_serverLocation = serverLocation;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> storeBackblazeCredential(final BackblazeCredential value) async {
|
Future<void> storeBackblazeCredential(final BackblazeCredential value) async {
|
||||||
await _box.put(BNames.backblazeCredential, value);
|
await _box.put(BNames.backblazeCredential, value);
|
||||||
_backblazeCredential = value;
|
_backblazeCredential = value;
|
||||||
|
@ -53,20 +74,26 @@ class ApiConfigModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
_hetznerKey = null;
|
_serverProviderKey = null;
|
||||||
|
_serverLocation = null;
|
||||||
_cloudFlareKey = null;
|
_cloudFlareKey = null;
|
||||||
_backblazeCredential = null;
|
_backblazeCredential = null;
|
||||||
_serverDomain = null;
|
_serverDomain = null;
|
||||||
_serverDetails = null;
|
_serverDetails = null;
|
||||||
_backblazeBucket = null;
|
_backblazeBucket = null;
|
||||||
|
_serverType = null;
|
||||||
|
_serverProvider = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
_hetznerKey = _box.get(BNames.hetznerKey);
|
_serverProviderKey = _box.get(BNames.hetznerKey);
|
||||||
|
_serverLocation = _box.get(BNames.serverLocation);
|
||||||
_cloudFlareKey = _box.get(BNames.cloudFlareKey);
|
_cloudFlareKey = _box.get(BNames.cloudFlareKey);
|
||||||
_backblazeCredential = _box.get(BNames.backblazeCredential);
|
_backblazeCredential = _box.get(BNames.backblazeCredential);
|
||||||
_serverDomain = _box.get(BNames.serverDomain);
|
_serverDomain = _box.get(BNames.serverDomain);
|
||||||
_serverDetails = _box.get(BNames.serverDetails);
|
_serverDetails = _box.get(BNames.serverDetails);
|
||||||
_backblazeBucket = _box.get(BNames.backblazeBucket);
|
_backblazeBucket = _box.get(BNames.backblazeBucket);
|
||||||
|
_serverType = _box.get(BNames.serverTypeIdentifier);
|
||||||
|
_serverProvider = _box.get(BNames.serverProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
class TimeSeriesData {
|
|
||||||
TimeSeriesData(
|
|
||||||
this.secondsSinceEpoch,
|
|
||||||
this.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
final int secondsSinceEpoch;
|
|
||||||
DateTime get time =>
|
|
||||||
DateTime.fromMillisecondsSinceEpoch(secondsSinceEpoch * 1000);
|
|
||||||
final double value;
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/schema/schema.graphql.dart';
|
||||||
|
|
||||||
part 'server_details.g.dart';
|
part 'server_details.g.dart';
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ class ServerVolume {
|
||||||
required this.sizeByte,
|
required this.sizeByte,
|
||||||
required this.serverId,
|
required this.serverId,
|
||||||
required this.linuxDevice,
|
required this.linuxDevice,
|
||||||
|
this.uuid,
|
||||||
});
|
});
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
|
@ -70,6 +72,8 @@ class ServerVolume {
|
||||||
int? serverId;
|
int? serverId;
|
||||||
@HiveField(5, defaultValue: null)
|
@HiveField(5, defaultValue: null)
|
||||||
String? linuxDevice;
|
String? linuxDevice;
|
||||||
|
@HiveField(6, defaultValue: null)
|
||||||
|
String? uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiveType(typeId: 101)
|
@HiveType(typeId: 101)
|
||||||
|
@ -78,4 +82,17 @@ enum ServerProvider {
|
||||||
unknown,
|
unknown,
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
hetzner,
|
hetzner,
|
||||||
|
@HiveField(2)
|
||||||
|
digitalOcean;
|
||||||
|
|
||||||
|
factory ServerProvider.fromGraphQL(final Enum$ServerProvider provider) {
|
||||||
|
switch (provider) {
|
||||||
|
case Enum$ServerProvider.HETZNER:
|
||||||
|
return hetzner;
|
||||||
|
case Enum$ServerProvider.DIGITALOCEAN:
|
||||||
|
return digitalOcean;
|
||||||
|
default:
|
||||||
|
return unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,13 +76,14 @@ class ServerVolumeAdapter extends TypeAdapter<ServerVolume> {
|
||||||
sizeByte: fields[3] == null ? 10737418240 : fields[3] as int,
|
sizeByte: fields[3] == null ? 10737418240 : fields[3] as int,
|
||||||
serverId: fields[4] as int?,
|
serverId: fields[4] as int?,
|
||||||
linuxDevice: fields[5] as String?,
|
linuxDevice: fields[5] as String?,
|
||||||
|
uuid: fields[6] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, ServerVolume obj) {
|
void write(BinaryWriter writer, ServerVolume obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(5)
|
..writeByte(6)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
..write(obj.id)
|
..write(obj.id)
|
||||||
..writeByte(2)
|
..writeByte(2)
|
||||||
|
@ -92,7 +93,9 @@ class ServerVolumeAdapter extends TypeAdapter<ServerVolume> {
|
||||||
..writeByte(4)
|
..writeByte(4)
|
||||||
..write(obj.serverId)
|
..write(obj.serverId)
|
||||||
..writeByte(5)
|
..writeByte(5)
|
||||||
..write(obj.linuxDevice);
|
..write(obj.linuxDevice)
|
||||||
|
..writeByte(6)
|
||||||
|
..write(obj.uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -117,6 +120,8 @@ class ServerProviderAdapter extends TypeAdapter<ServerProvider> {
|
||||||
return ServerProvider.unknown;
|
return ServerProvider.unknown;
|
||||||
case 1:
|
case 1:
|
||||||
return ServerProvider.hetzner;
|
return ServerProvider.hetzner;
|
||||||
|
case 2:
|
||||||
|
return ServerProvider.digitalOcean;
|
||||||
default:
|
default:
|
||||||
return ServerProvider.unknown;
|
return ServerProvider.unknown;
|
||||||
}
|
}
|
||||||
|
@ -131,6 +136,9 @@ class ServerProviderAdapter extends TypeAdapter<ServerProvider> {
|
||||||
case ServerProvider.hetzner:
|
case ServerProvider.hetzner:
|
||||||
writer.writeByte(1);
|
writer.writeByte(1);
|
||||||
break;
|
break;
|
||||||
|
case ServerProvider.digitalOcean:
|
||||||
|
writer.writeByte(2);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
30
lib/logic/models/metrics.dart
Normal file
30
lib/logic/models/metrics.dart
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
class TimeSeriesData {
|
||||||
|
TimeSeriesData(
|
||||||
|
this.secondsSinceEpoch,
|
||||||
|
this.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
final int secondsSinceEpoch;
|
||||||
|
DateTime get time =>
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(secondsSinceEpoch * 1000);
|
||||||
|
final double value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerMetrics {
|
||||||
|
ServerMetrics({
|
||||||
|
required this.stepsInSecond,
|
||||||
|
required this.cpu,
|
||||||
|
required this.bandwidthIn,
|
||||||
|
required this.bandwidthOut,
|
||||||
|
required this.start,
|
||||||
|
required this.end,
|
||||||
|
});
|
||||||
|
|
||||||
|
final num stepsInSecond;
|
||||||
|
final List<TimeSeriesData> cpu;
|
||||||
|
final List<TimeSeriesData> bandwidthIn;
|
||||||
|
final List<TimeSeriesData> bandwidthOut;
|
||||||
|
|
||||||
|
final DateTime start;
|
||||||
|
final DateTime end;
|
||||||
|
}
|
9
lib/logic/models/price.dart
Normal file
9
lib/logic/models/price.dart
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class Price {
|
||||||
|
Price({
|
||||||
|
required this.value,
|
||||||
|
required this.currency,
|
||||||
|
});
|
||||||
|
|
||||||
|
double value;
|
||||||
|
String currency;
|
||||||
|
}
|
|
@ -5,14 +5,12 @@ class ServerBasicInfo {
|
||||||
required this.reverseDns,
|
required this.reverseDns,
|
||||||
required this.ip,
|
required this.ip,
|
||||||
required this.created,
|
required this.created,
|
||||||
required this.volumeId,
|
|
||||||
});
|
});
|
||||||
final int id;
|
final int id;
|
||||||
final String name;
|
final String name;
|
||||||
final String reverseDns;
|
final String reverseDns;
|
||||||
final String ip;
|
final String ip;
|
||||||
final DateTime created;
|
final DateTime created;
|
||||||
final int volumeId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
|
@ -26,7 +24,6 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
reverseDns: serverBasicInfo.reverseDns,
|
reverseDns: serverBasicInfo.reverseDns,
|
||||||
ip: serverBasicInfo.ip,
|
ip: serverBasicInfo.ip,
|
||||||
created: serverBasicInfo.created,
|
created: serverBasicInfo.created,
|
||||||
volumeId: serverBasicInfo.volumeId,
|
|
||||||
isIpValid: isIpValid,
|
isIpValid: isIpValid,
|
||||||
isReverseDnsValid: isReverseDnsValid,
|
isReverseDnsValid: isReverseDnsValid,
|
||||||
);
|
);
|
||||||
|
@ -37,7 +34,6 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
required super.reverseDns,
|
required super.reverseDns,
|
||||||
required super.ip,
|
required super.ip,
|
||||||
required super.created,
|
required super.created,
|
||||||
required super.volumeId,
|
|
||||||
required this.isIpValid,
|
required this.isIpValid,
|
||||||
required this.isReverseDnsValid,
|
required this.isReverseDnsValid,
|
||||||
});
|
});
|
||||||
|
|
29
lib/logic/models/server_metadata.dart
Normal file
29
lib/logic/models/server_metadata.dart
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum MetadataType {
|
||||||
|
id(icon: Icons.numbers_outlined),
|
||||||
|
status(icon: Icons.mode_standby_outlined),
|
||||||
|
cpu(icon: Icons.memory_outlined),
|
||||||
|
ram(icon: Icons.memory_outlined),
|
||||||
|
cost(icon: Icons.payments_outlined),
|
||||||
|
location(icon: Icons.location_on_outlined),
|
||||||
|
|
||||||
|
other(icon: Icons.info_outlined);
|
||||||
|
|
||||||
|
const MetadataType({
|
||||||
|
required this.icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
final IconData icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerMetadataEntity {
|
||||||
|
ServerMetadataEntity({
|
||||||
|
required this.name,
|
||||||
|
required this.value,
|
||||||
|
this.type = MetadataType.other,
|
||||||
|
});
|
||||||
|
final MetadataType type;
|
||||||
|
final String name;
|
||||||
|
final String value;
|
||||||
|
}
|
15
lib/logic/models/server_provider_location.dart
Normal file
15
lib/logic/models/server_provider_location.dart
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
class ServerProviderLocation {
|
||||||
|
ServerProviderLocation({
|
||||||
|
required this.title,
|
||||||
|
required this.identifier,
|
||||||
|
this.description,
|
||||||
|
this.flag,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String identifier;
|
||||||
|
final String? description;
|
||||||
|
|
||||||
|
/// as emoji
|
||||||
|
final String? flag;
|
||||||
|
}
|
22
lib/logic/models/server_type.dart
Normal file
22
lib/logic/models/server_type.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
|
||||||
|
class ServerType {
|
||||||
|
ServerType({
|
||||||
|
required this.title,
|
||||||
|
required this.identifier,
|
||||||
|
required this.ram,
|
||||||
|
required this.cores,
|
||||||
|
required this.disk,
|
||||||
|
required this.price,
|
||||||
|
required this.location,
|
||||||
|
});
|
||||||
|
final String title;
|
||||||
|
final String identifier;
|
||||||
|
final double ram;
|
||||||
|
final DiskSize disk;
|
||||||
|
final int cores;
|
||||||
|
final Price price;
|
||||||
|
final ServerProviderLocation location;
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
|
import 'package:selfprivacy/theming/factory/app_theme_factory.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/initializing.dart';
|
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
|
||||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||||
import 'package:wakelock/wakelock.dart';
|
import 'package:wakelock/wakelock.dart';
|
||||||
|
|
|
@ -2,18 +2,16 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:selfprivacy/config/brand_theme.dart';
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_jobs/server_jobs_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
import 'package:selfprivacy/logic/models/json/server_job.dart';
|
||||||
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
|
||||||
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
|
||||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
|
import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
|
import 'package:selfprivacy/ui/components/brand_loader/brand_loader.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart';
|
import 'package:selfprivacy/ui/components/jobs_content/server_job_card.dart';
|
||||||
|
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||||
|
|
||||||
class JobsContent extends StatelessWidget {
|
class JobsContent extends StatelessWidget {
|
||||||
const JobsContent({super.key});
|
const JobsContent({super.key});
|
||||||
|
@ -47,26 +45,16 @@ class JobsContent extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
BrandButton.text(
|
BrandButton.text(
|
||||||
onPressed: () {
|
|
||||||
final NavigationService nav = getIt<NavigationService>();
|
|
||||||
nav.showPopUpDialog(
|
|
||||||
BrandAlert(
|
|
||||||
title: 'jobs.reboot_server'.tr(),
|
title: 'jobs.reboot_server'.tr(),
|
||||||
contentText: 'modals.are_you_sure'.tr(),
|
onPressed: () {
|
||||||
actions: [
|
showPopUpAlert(
|
||||||
ActionButton(
|
alertTitle: 'jobs.reboot_server'.tr(),
|
||||||
text: 'basis.cancel'.tr(),
|
description: 'modals.are_you_sure'.tr(),
|
||||||
),
|
actionButtonTitle: 'modals.reboot'.tr(),
|
||||||
ActionButton(
|
actionButtonOnPressed: () =>
|
||||||
onPressed: () =>
|
|
||||||
{context.read<JobsCubit>().rebootServer()},
|
{context.read<JobsCubit>().rebootServer()},
|
||||||
text: 'modals.reboot'.tr(),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
title: 'jobs.reboot_server'.tr(),
|
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/config/text_themes.dart';
|
import 'package:selfprivacy/config/text_themes.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/initializing.dart';
|
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||||
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
||||||
|
|
||||||
Future<T?> showBrandBottomSheet<T>({
|
Future<T?> showBrandBottomSheet<T>({
|
||||||
required final BuildContext context,
|
required final BuildContext context,
|
||||||
|
@ -12,3 +16,30 @@ Future<T?> showBrandBottomSheet<T>({
|
||||||
shadow: const BoxShadow(color: Colors.transparent),
|
shadow: const BoxShadow(color: Colors.transparent),
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void showPopUpAlert({
|
||||||
|
required final String description,
|
||||||
|
required final String actionButtonTitle,
|
||||||
|
required final void Function() actionButtonOnPressed,
|
||||||
|
final void Function()? cancelButtonOnPressed,
|
||||||
|
final String? alertTitle,
|
||||||
|
final String? cancelButtonTitle,
|
||||||
|
}) {
|
||||||
|
getIt.get<NavigationService>().showPopUpDialog(
|
||||||
|
BrandAlert(
|
||||||
|
title: alertTitle ?? 'basis.alert'.tr(),
|
||||||
|
contentText: description,
|
||||||
|
actions: [
|
||||||
|
ActionButton(
|
||||||
|
text: actionButtonTitle,
|
||||||
|
isRed: true,
|
||||||
|
onPressed: actionButtonOnPressed,
|
||||||
|
),
|
||||||
|
ActionButton(
|
||||||
|
text: cancelButtonTitle ?? 'basis.cancel'.tr(),
|
||||||
|
onPressed: cancelButtonOnPressed,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/backups/backups_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/backup.dart';
|
import 'package:selfprivacy/logic/models/json/backup.dart';
|
||||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
|
||||||
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
|
||||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart';
|
import 'package:selfprivacy/ui/components/brand_cards/outlined_card.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
import 'package:selfprivacy/ui/helpers/modals.dart';
|
||||||
|
|
||||||
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
|
@ -157,28 +155,17 @@ class _BackupDetailsState extends State<BackupDetails>
|
||||||
onTap: preventActions
|
onTap: preventActions
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
final NavigationService nav =
|
showPopUpAlert(
|
||||||
getIt<NavigationService>();
|
alertTitle: 'backup.restoring'.tr(),
|
||||||
nav.showPopUpDialog(
|
description: 'backup.restore_alert'.tr(
|
||||||
BrandAlert(
|
|
||||||
title: 'backup.restoring'.tr(),
|
|
||||||
contentText: 'backup.restore_alert'.tr(
|
|
||||||
args: [backup.time.toString()],
|
args: [backup.time.toString()],
|
||||||
),
|
),
|
||||||
actions: [
|
actionButtonTitle: 'modals.yes'.tr(),
|
||||||
ActionButton(
|
actionButtonOnPressed: () => {
|
||||||
text: 'basis.cancel'.tr(),
|
|
||||||
),
|
|
||||||
ActionButton(
|
|
||||||
onPressed: () => {
|
|
||||||
context
|
context
|
||||||
.read<BackupsCubit>()
|
.read<BackupsCubit>()
|
||||||
.restoreBackup(backup.id)
|
.restoreBackup(backup.id)
|
||||||
},
|
},
|
||||||
text: 'modals.yes'.tr(),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_theme.dart';
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
|
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server_api.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
import 'package:selfprivacy/ui/components/brand_header/brand_header.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
import 'package:package_info/package_info.dart';
|
import 'package:package_info/package_info.dart';
|
||||||
|
|
|
@ -11,7 +11,7 @@ import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
import 'package:selfprivacy/ui/pages/devices/devices.dart';
|
import 'package:selfprivacy/ui/pages/devices/devices.dart';
|
||||||
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart';
|
import 'package:selfprivacy/ui/pages/recovery_key/recovery_key.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart';
|
import 'package:selfprivacy/ui/pages/server_storage/binds_migration/services_migration.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/initializing.dart';
|
import 'package:selfprivacy/ui/pages/setup/initializing/initializing.dart';
|
||||||
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart';
|
||||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||||
import 'package:selfprivacy/ui/pages/users/users.dart';
|
import 'package:selfprivacy/ui/pages/users/users.dart';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
String bottomTitle(
|
String bottomTitle(
|
||||||
|
@ -11,7 +11,7 @@ String bottomTitle(
|
||||||
final day = DateFormat('MMMd');
|
final day = DateFormat('MMMd');
|
||||||
String res;
|
String res;
|
||||||
|
|
||||||
if (value <= 0) {
|
if (value <= 0 || value >= data.length) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@ part of '../server_details_screen.dart';
|
||||||
class _Chart extends StatelessWidget {
|
class _Chart extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final HetznerMetricsCubit cubit = context.watch<HetznerMetricsCubit>();
|
final MetricsCubit cubit = context.watch<MetricsCubit>();
|
||||||
final Period period = cubit.state.period;
|
final Period period = cubit.state.period;
|
||||||
final HetznerMetricsState state = cubit.state;
|
final MetricsState state = cubit.state;
|
||||||
List<Widget> charts;
|
List<Widget> charts;
|
||||||
if (state is HetznerMetricsLoaded || state is HetznerMetricsLoading) {
|
if (state is MetricsLoaded || state is MetricsLoading) {
|
||||||
charts = [
|
charts = [
|
||||||
FilledCard(
|
FilledCard(
|
||||||
clipped: false,
|
clipped: false,
|
||||||
|
@ -26,10 +26,10 @@ class _Chart extends StatelessWidget {
|
||||||
Stack(
|
Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (state is HetznerMetricsLoaded) getCpuChart(state),
|
if (state is MetricsLoaded) getCpuChart(state),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
opacity: state is HetznerMetricsLoading ? 1 : 0,
|
opacity: state is MetricsLoading ? 1 : 0,
|
||||||
child: const _GraphLoadingCardContent(),
|
child: const _GraphLoadingCardContent(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -72,10 +72,10 @@ class _Chart extends StatelessWidget {
|
||||||
Stack(
|
Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (state is HetznerMetricsLoaded) getBandwidthChart(state),
|
if (state is MetricsLoaded) getBandwidthChart(state),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
opacity: state is HetznerMetricsLoading ? 1 : 0,
|
opacity: state is MetricsLoading ? 1 : 0,
|
||||||
child: const _GraphLoadingCardContent(),
|
child: const _GraphLoadingCardContent(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -122,29 +122,29 @@ class _Chart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getCpuChart(final HetznerMetricsLoaded state) {
|
Widget getCpuChart(final MetricsLoaded state) {
|
||||||
final data = state.cpu;
|
final data = state.metrics.cpu;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: CpuChart(
|
child: CpuChart(
|
||||||
data: data,
|
data: data,
|
||||||
period: state.period,
|
period: state.period,
|
||||||
start: state.start,
|
start: state.metrics.start,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getBandwidthChart(final HetznerMetricsLoaded state) {
|
Widget getBandwidthChart(final MetricsLoaded state) {
|
||||||
final ppsIn = state.bandwidthIn;
|
final ppsIn = state.metrics.bandwidthIn;
|
||||||
final ppsOut = state.bandwidthOut;
|
final ppsOut = state.metrics.bandwidthOut;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: NetworkChart(
|
child: NetworkChart(
|
||||||
listData: [ppsIn, ppsOut],
|
listData: [ppsIn, ppsOut],
|
||||||
period: state.period,
|
period: state.period,
|
||||||
start: state.start,
|
start: state.metrics.start,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class CpuChart extends StatelessWidget {
|
||||||
],
|
],
|
||||||
minY: 0,
|
minY: 0,
|
||||||
maxY: 100,
|
maxY: 100,
|
||||||
minX: data.length - 200,
|
minX: 0,
|
||||||
titlesData: FlTitlesData(
|
titlesData: FlTitlesData(
|
||||||
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||||
bottomTitles: AxisTitles(
|
bottomTitles: AxisTitles(
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||||
|
|
||||||
class NetworkChart extends StatelessWidget {
|
class NetworkChart extends StatelessWidget {
|
||||||
|
@ -116,7 +116,7 @@ class NetworkChart extends StatelessWidget {
|
||||||
...listData[1].map((final e) => e.value)
|
...listData[1].map((final e) => e.value)
|
||||||
].reduce(max) *
|
].reduce(max) *
|
||||||
1.2,
|
1.2,
|
||||||
minX: listData[0].length - 200,
|
minX: 0,
|
||||||
titlesData: FlTitlesData(
|
titlesData: FlTitlesData(
|
||||||
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
|
||||||
bottomTitles: AxisTitles(
|
bottomTitles: AxisTitles(
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
||||||
|
@ -21,7 +21,6 @@ import 'package:selfprivacy/ui/pages/server_details/charts/cpu_chart.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_details/charts/network_charts.dart';
|
import 'package:selfprivacy/ui/pages/server_details/charts/network_charts.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart';
|
import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart';
|
||||||
import 'package:selfprivacy/utils/extensions/duration.dart';
|
import 'package:selfprivacy/utils/extensions/duration.dart';
|
||||||
import 'package:selfprivacy/utils/extensions/string_extensions.dart';
|
|
||||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
import 'package:timezone/timezone.dart';
|
import 'package:timezone/timezone.dart';
|
||||||
|
@ -92,7 +91,7 @@ class _ServerDetailsScreenState extends State<ServerDetailsScreen>
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final context) => HetznerMetricsCubit()..restart(),
|
create: (final context) => MetricsCubit()..restart(),
|
||||||
child: _Chart(),
|
child: _Chart(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
|
@ -10,7 +10,6 @@ class _TextDetails extends StatelessWidget {
|
||||||
} else if (details is ServerDetailsNotReady) {
|
} else if (details is ServerDetailsNotReady) {
|
||||||
return _TempMessage(message: 'basis.no_data'.tr());
|
return _TempMessage(message: 'basis.no_data'.tr());
|
||||||
} else if (details is Loaded) {
|
} else if (details is Loaded) {
|
||||||
final data = details.serverInfo;
|
|
||||||
return FilledCard(
|
return FilledCard(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -24,37 +23,15 @@ class _TextDetails extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTileOnSurfaceVariant(
|
...details.metadata
|
||||||
leadingIcon: Icons.numbers_outlined,
|
.map(
|
||||||
title: data.id.toString(),
|
(final metadata) => ListTileOnSurfaceVariant(
|
||||||
subtitle: 'server.server_id'.tr(),
|
leadingIcon: metadata.type.icon,
|
||||||
),
|
title: metadata.name,
|
||||||
ListTileOnSurfaceVariant(
|
subtitle: metadata.value,
|
||||||
leadingIcon: Icons.mode_standby_outlined,
|
|
||||||
title: data.status.toString().split('.')[1].capitalize(),
|
|
||||||
subtitle: 'server.status'.tr(),
|
|
||||||
),
|
|
||||||
ListTileOnSurfaceVariant(
|
|
||||||
leadingIcon: Icons.memory_outlined,
|
|
||||||
title: 'server.core_count'.plural(data.serverType.cores),
|
|
||||||
subtitle: 'server.cpu'.tr(),
|
|
||||||
),
|
|
||||||
ListTileOnSurfaceVariant(
|
|
||||||
leadingIcon: Icons.memory_outlined,
|
|
||||||
title: '${data.serverType.memory.toString()} GB',
|
|
||||||
subtitle: 'server.ram'.tr(),
|
|
||||||
),
|
|
||||||
ListTileOnSurfaceVariant(
|
|
||||||
leadingIcon: Icons.euro_outlined,
|
|
||||||
title: data.serverType.prices[1].monthly.toStringAsFixed(2),
|
|
||||||
subtitle: 'server.monthly_cost'.tr(),
|
|
||||||
),
|
|
||||||
// Server location
|
|
||||||
ListTileOnSurfaceVariant(
|
|
||||||
leadingIcon: Icons.location_on_outlined,
|
|
||||||
title: '${data.location.city}, ${data.location.country}',
|
|
||||||
subtitle: 'server.location'.tr(),
|
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_depe
|
||||||
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/provider_volumes/provider_volume_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_button/filled_button.dart';
|
import 'package:selfprivacy/ui/components/brand_button/filled_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_status.dart';
|
import 'package:selfprivacy/logic/models/disk_status.dart';
|
||||||
|
@ -74,7 +75,7 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_euroPerGb = snapshot.data as double;
|
_euroPerGb = (snapshot.data as Price).value;
|
||||||
_sizeController.text = _currentSliderGbValue.truncate().toString();
|
_sizeController.text = _currentSliderGbValue.truncate().toString();
|
||||||
_priceController.text =
|
_priceController.text =
|
||||||
(_euroPerGb * double.parse(_sizeController.text))
|
(_euroPerGb * double.parse(_sizeController.text))
|
||||||
|
@ -152,7 +153,7 @@ class _ExtendingVolumePageState extends State<ExtendingVolumePage> {
|
||||||
: () {
|
: () {
|
||||||
context.read<ApiProviderVolumeCubit>().resizeVolume(
|
context.read<ApiProviderVolumeCubit>().resizeVolume(
|
||||||
widget.diskVolumeToResize,
|
widget.diskVolumeToResize,
|
||||||
_currentSliderGbValue.round(),
|
DiskSize.fromGibibyte(_currentSliderGbValue),
|
||||||
context.read<ApiServerVolumeCubit>().reload,
|
context.read<ApiServerVolumeCubit>().reload,
|
||||||
);
|
);
|
||||||
Navigator.of(context).pushAndRemoveUntil(
|
Navigator.of(context).pushAndRemoveUntil(
|
||||||
|
|
|
@ -2,12 +2,12 @@ import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_theme.dart';
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/dns_provider_form_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/domain_setup_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart';
|
|
||||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/root_user_form_cubit.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||||
|
@ -17,6 +17,8 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart';
|
import 'package:selfprivacy/ui/components/brand_timer/brand_timer.dart';
|
||||||
import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart';
|
import 'package:selfprivacy/ui/components/progress_bar/progress_bar.dart';
|
||||||
import 'package:selfprivacy/ui/pages/root_route.dart';
|
import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/setup/initializing/server_provider_picker.dart';
|
||||||
|
import 'package:selfprivacy/ui/pages/setup/initializing/server_type_picker.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
|
|
||||||
|
@ -33,7 +35,8 @@ class InitializingPage extends StatelessWidget {
|
||||||
Widget? actualInitializingPage;
|
Widget? actualInitializingPage;
|
||||||
if (cubit.state is! ServerInstallationFinished) {
|
if (cubit.state is! ServerInstallationFinished) {
|
||||||
actualInitializingPage = [
|
actualInitializingPage = [
|
||||||
() => _stepHetzner(cubit),
|
() => _stepServerProviderToken(cubit),
|
||||||
|
() => _stepServerType(cubit),
|
||||||
() => _stepCloudflare(cubit),
|
() => _stepCloudflare(cubit),
|
||||||
() => _stepBackblaze(cubit),
|
() => _stepBackblaze(cubit),
|
||||||
() => _stepDomain(cubit),
|
() => _stepDomain(cubit),
|
||||||
|
@ -67,7 +70,8 @@ class InitializingPage extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: ProgressBar(
|
: ProgressBar(
|
||||||
steps: const [
|
steps: const [
|
||||||
'Hetzner',
|
'Hosting',
|
||||||
|
'Server Type',
|
||||||
'CloudFlare',
|
'CloudFlare',
|
||||||
'Backblaze',
|
'Backblaze',
|
||||||
'Domain',
|
'Domain',
|
||||||
|
@ -78,6 +82,11 @@ class InitializingPage extends StatelessWidget {
|
||||||
activeIndex: cubit.state.porgressBar,
|
activeIndex: cubit.state.porgressBar,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (cubit.state.porgressBar ==
|
||||||
|
ServerSetupProgress.serverProviderFilled.index)
|
||||||
|
BrandText.h2(
|
||||||
|
'initializing.choose_location_type'.tr(),
|
||||||
|
),
|
||||||
_addCard(
|
_addCard(
|
||||||
AnimatedSwitcher(
|
AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
|
@ -136,55 +145,34 @@ class InitializingPage extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _stepHetzner(final ServerInstallationCubit serverInstallationCubit) =>
|
Widget _stepServerProviderToken(
|
||||||
|
final ServerInstallationCubit serverInstallationCubit,
|
||||||
|
) =>
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final context) => ProviderFormCubit(
|
create: (final context) => ProviderFormCubit(serverInstallationCubit),
|
||||||
serverInstallationCubit,
|
|
||||||
),
|
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (final context) {
|
builder: (final context) {
|
||||||
final formCubitState = context.watch<ProviderFormCubit>().state;
|
final providerCubit = context.watch<ProviderFormCubit>();
|
||||||
return Column(
|
return ServerProviderPicker(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
formCubit: providerCubit,
|
||||||
children: [
|
serverInstallationCubit: serverInstallationCubit,
|
||||||
Image.asset(
|
|
||||||
'assets/images/logos/hetzner.png',
|
|
||||||
width: 150,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
BrandText.h2('initializing.connect_to_server'.tr()),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
BrandText.body2('initializing.place_where_data'.tr()),
|
|
||||||
const Spacer(),
|
|
||||||
CubitFormTextField(
|
|
||||||
formFieldCubit: context.read<ProviderFormCubit>().apiKey,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
scrollPadding: const EdgeInsets.only(bottom: 70),
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'Hetzner API Token',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
BrandButton.rised(
|
|
||||||
onPressed: formCubitState.isSubmitting
|
|
||||||
? null
|
|
||||||
: () => context.read<ProviderFormCubit>().trySubmit(),
|
|
||||||
text: 'basis.connect'.tr(),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
BrandButton.text(
|
|
||||||
onPressed: () => _showModal(
|
|
||||||
context,
|
|
||||||
const _HowTo(fileName: 'how_hetzner'),
|
|
||||||
),
|
|
||||||
title: 'initializing.how'.tr(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Widget _stepServerType(
|
||||||
|
final ServerInstallationCubit serverInstallationCubit,
|
||||||
|
) =>
|
||||||
|
BlocProvider(
|
||||||
|
create: (final context) => ProviderFormCubit(serverInstallationCubit),
|
||||||
|
child: Builder(
|
||||||
|
builder: (final context) => ServerTypePicker(
|
||||||
|
serverInstallationCubit: serverInstallationCubit,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
void _showModal(final BuildContext context, final Widget widget) {
|
void _showModal(final BuildContext context, final Widget widget) {
|
||||||
showModalBottomSheet<void>(
|
showModalBottomSheet<void>(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -198,10 +186,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final context) => DnsProviderFormCubit(initializingCubit),
|
create: (final context) => DnsProviderFormCubit(initializingCubit),
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (final context) {
|
builder: (final context) => Column(
|
||||||
final formCubitState = context.watch<DnsProviderFormCubit>().state;
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Image.asset(
|
Image.asset(
|
||||||
|
@ -223,9 +208,8 @@ class InitializingPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
BrandButton.rised(
|
BrandButton.rised(
|
||||||
onPressed: formCubitState.isSubmitting
|
onPressed: () =>
|
||||||
? null
|
context.read<DnsProviderFormCubit>().trySubmit(),
|
||||||
: () => context.read<DnsProviderFormCubit>().trySubmit(),
|
|
||||||
text: 'basis.connect'.tr(),
|
text: 'basis.connect'.tr(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
@ -239,8 +223,7 @@ class InitializingPage extends StatelessWidget {
|
||||||
title: 'initializing.how'.tr(),
|
title: 'initializing.how'.tr(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
204
lib/ui/pages/setup/initializing/server_provider_picker.dart
Normal file
204
lib/ui/pages/setup/initializing/server_provider_picker.dart
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/provider_form_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_button/filled_button.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||||
|
|
||||||
|
class ServerProviderPicker extends StatefulWidget {
|
||||||
|
const ServerProviderPicker({
|
||||||
|
required this.formCubit,
|
||||||
|
required this.serverInstallationCubit,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ProviderFormCubit formCubit;
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ServerProviderPicker> createState() => _ServerProviderPickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ServerProviderPickerState extends State<ServerProviderPicker> {
|
||||||
|
ServerProvider selectedProvider = ServerProvider.unknown;
|
||||||
|
|
||||||
|
void setProvider(final ServerProvider provider) {
|
||||||
|
setState(() {
|
||||||
|
selectedProvider = provider;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) {
|
||||||
|
switch (selectedProvider) {
|
||||||
|
case ServerProvider.unknown:
|
||||||
|
return ProviderSelectionPage(
|
||||||
|
serverInstallationCubit: widget.serverInstallationCubit,
|
||||||
|
callback: setProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
case ServerProvider.hetzner:
|
||||||
|
return ProviderInputDataPage(
|
||||||
|
providerCubit: widget.formCubit,
|
||||||
|
providerInfo: ProviderPageInfo(
|
||||||
|
providerType: ServerProvider.hetzner,
|
||||||
|
pathToHow: 'hetzner_how',
|
||||||
|
image: Image.asset(
|
||||||
|
'assets/images/logos/hetzner.png',
|
||||||
|
width: 150,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
case ServerProvider.digitalOcean:
|
||||||
|
return ProviderInputDataPage(
|
||||||
|
providerCubit: widget.formCubit,
|
||||||
|
providerInfo: ProviderPageInfo(
|
||||||
|
providerType: ServerProvider.digitalOcean,
|
||||||
|
pathToHow: 'hetzner_how',
|
||||||
|
image: Image.asset(
|
||||||
|
'assets/images/logos/digital_ocean.png',
|
||||||
|
width: 150,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProviderPageInfo {
|
||||||
|
const ProviderPageInfo({
|
||||||
|
required this.providerType,
|
||||||
|
required this.pathToHow,
|
||||||
|
required this.image,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String pathToHow;
|
||||||
|
final Image image;
|
||||||
|
final ServerProvider providerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProviderInputDataPage extends StatelessWidget {
|
||||||
|
const ProviderInputDataPage({
|
||||||
|
required this.providerInfo,
|
||||||
|
required this.providerCubit,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ProviderPageInfo providerInfo;
|
||||||
|
final ProviderFormCubit providerCubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
providerInfo.image,
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'initializing.connect_to_server'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
CubitFormTextField(
|
||||||
|
formFieldCubit: providerCubit.apiKey,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
scrollPadding: const EdgeInsets.only(bottom: 70),
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'Provider API Token',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
FilledButton(
|
||||||
|
title: 'basis.connect'.tr(),
|
||||||
|
onPressed: () => providerCubit.trySubmit(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
OutlinedButton(
|
||||||
|
child: Text('initializing.how'.tr()),
|
||||||
|
onPressed: () => showModalBottomSheet<void>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
builder: (final BuildContext context) => BrandBottomSheet(
|
||||||
|
isExpended: true,
|
||||||
|
child: Padding(
|
||||||
|
padding: paddingH15V0,
|
||||||
|
child: ListView(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
children: [
|
||||||
|
BrandMarkdown(
|
||||||
|
fileName: providerInfo.pathToHow,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProviderSelectionPage extends StatelessWidget {
|
||||||
|
const ProviderSelectionPage({
|
||||||
|
required this.callback,
|
||||||
|
required this.serverInstallationCubit,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Function callback;
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'initializing.select_provider'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
'initializing.place_where_data'.tr(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 320,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
serverInstallationCubit
|
||||||
|
.setServerProviderType(ServerProvider.hetzner);
|
||||||
|
callback(ServerProvider.hetzner);
|
||||||
|
},
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/logos/hetzner.png',
|
||||||
|
width: 150,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 20,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
serverInstallationCubit
|
||||||
|
.setServerProviderType(ServerProvider.digitalOcean);
|
||||||
|
callback(ServerProvider.digitalOcean);
|
||||||
|
},
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/images/logos/digital_ocean.png',
|
||||||
|
width: 150,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
195
lib/ui/pages/setup/initializing/server_type_picker.dart
Normal file
195
lib/ui/pages/setup/initializing/server_type_picker.dart
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/config/brand_theme.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/server_type.dart';
|
||||||
|
import 'package:selfprivacy/ui/components/brand_button/brand_button.dart';
|
||||||
|
|
||||||
|
class ServerTypePicker extends StatefulWidget {
|
||||||
|
const ServerTypePicker({
|
||||||
|
required this.serverInstallationCubit,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ServerTypePicker> createState() => _ServerTypePickerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ServerTypePickerState extends State<ServerTypePicker> {
|
||||||
|
ServerProviderLocation? serverProviderLocation;
|
||||||
|
ServerType? serverType;
|
||||||
|
|
||||||
|
void setServerProviderLocation(final ServerProviderLocation? location) {
|
||||||
|
setState(() {
|
||||||
|
serverProviderLocation = location;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) {
|
||||||
|
if (serverProviderLocation == null) {
|
||||||
|
return SelectLocationPage(
|
||||||
|
serverInstallationCubit: widget.serverInstallationCubit,
|
||||||
|
callback: setServerProviderLocation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SelectTypePage(
|
||||||
|
location: serverProviderLocation!,
|
||||||
|
serverInstallationCubit: widget.serverInstallationCubit,
|
||||||
|
backToLocationPickingCallback: () {
|
||||||
|
setServerProviderLocation(null);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectLocationPage extends StatelessWidget {
|
||||||
|
const SelectLocationPage({
|
||||||
|
required this.serverInstallationCubit,
|
||||||
|
required this.callback,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Function callback;
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => FutureBuilder(
|
||||||
|
future: serverInstallationCubit.fetchAvailableLocations(),
|
||||||
|
builder: (
|
||||||
|
final BuildContext context,
|
||||||
|
final AsyncSnapshot<Object?> snapshot,
|
||||||
|
) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
if ((snapshot.data as List<ServerProviderLocation>).isEmpty) {
|
||||||
|
return Text('initializing.no_locations_found'.tr());
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
padding: paddingH15V0,
|
||||||
|
children: [
|
||||||
|
...(snapshot.data! as List<ServerProviderLocation>).map(
|
||||||
|
(final location) => InkWell(
|
||||||
|
onTap: () {
|
||||||
|
callback(location);
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (location.flag != null) Text(location.flag!),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(location.title),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
if (location.description != null)
|
||||||
|
Text(location.description!),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectTypePage extends StatelessWidget {
|
||||||
|
const SelectTypePage({
|
||||||
|
required this.backToLocationPickingCallback,
|
||||||
|
required this.location,
|
||||||
|
required this.serverInstallationCubit,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final ServerProviderLocation location;
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
final Function backToLocationPickingCallback;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => FutureBuilder(
|
||||||
|
future: serverInstallationCubit.fetchAvailableTypesByLocation(location),
|
||||||
|
builder: (
|
||||||
|
final BuildContext context,
|
||||||
|
final AsyncSnapshot<Object?> snapshot,
|
||||||
|
) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
if ((snapshot.data as List<ServerType>).isEmpty) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'initializing.no_server_types_found'.tr(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
BrandButton.rised(
|
||||||
|
onPressed: () {
|
||||||
|
backToLocationPickingCallback();
|
||||||
|
},
|
||||||
|
text: 'initializing.back_to_locations'.tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
padding: paddingH15V0,
|
||||||
|
children: [
|
||||||
|
...(snapshot.data! as List<ServerType>).map(
|
||||||
|
(final type) => InkWell(
|
||||||
|
onTap: () {
|
||||||
|
serverInstallationCubit.setServerType(type);
|
||||||
|
},
|
||||||
|
child: Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
type.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'cores: ${type.cores.toString()}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'ram: ${type.ram.toString()}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'disk: ${type.disk.gibibyte.toString()}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'price: ${type.price.value.toString()} ${type.price.currency}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
|
@ -45,9 +45,8 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
children: [
|
children: [
|
||||||
FutureBuilder<List<ServerBasicInfoWithValidators>>(
|
FutureBuilder<List<ServerBasicInfoWithValidators>>(
|
||||||
future: context
|
future:
|
||||||
.read<ServerInstallationCubit>()
|
context.read<ServerInstallationCubit>().getAvailableServers(),
|
||||||
.getServersOnHetznerAccount(),
|
|
||||||
builder: (final context, final snapshot) {
|
builder: (final context, final snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
final servers = snapshot.data;
|
final servers = snapshot.data;
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'package:selfprivacy/ui/pages/setup/recovering/recover_by_new_device_key.
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_backblaze.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_backblaze.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_cloudflare.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_server.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_confirm_server.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_hentzner_connected.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_server_provider_connected.dart';
|
||||||
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_method_select.dart';
|
import 'package:selfprivacy/ui/pages/setup/recovering/recovery_method_select.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
|
|
||||||
|
@ -47,8 +47,8 @@ class RecoveryRouting extends StatelessWidget {
|
||||||
case RecoveryStep.oldToken:
|
case RecoveryStep.oldToken:
|
||||||
currentPage = const RecoverByOldToken();
|
currentPage = const RecoverByOldToken();
|
||||||
break;
|
break;
|
||||||
case RecoveryStep.hetznerToken:
|
case RecoveryStep.serverProviderToken:
|
||||||
currentPage = const RecoveryHetznerConnected();
|
currentPage = const RecoveryServerProviderConnected();
|
||||||
break;
|
break;
|
||||||
case RecoveryStep.serverSelection:
|
case RecoveryStep.serverSelection:
|
||||||
currentPage = const RecoveryConfirmServer();
|
currentPage = const RecoveryConfirmServer();
|
||||||
|
|
|
@ -10,8 +10,8 @@ import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
import 'package:selfprivacy/ui/components/brand_md/brand_md.dart';
|
||||||
|
|
||||||
class RecoveryHetznerConnected extends StatelessWidget {
|
class RecoveryServerProviderConnected extends StatelessWidget {
|
||||||
const RecoveryHetznerConnected({super.key});
|
const RecoveryServerProviderConnected({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
|
@ -26,8 +26,8 @@ class RecoveryHetznerConnected extends StatelessWidget {
|
||||||
context.watch<ProviderFormCubit>().state;
|
context.watch<ProviderFormCubit>().state;
|
||||||
|
|
||||||
return BrandHeroScreen(
|
return BrandHeroScreen(
|
||||||
heroTitle: 'recovering.hetzner_connected'.tr(),
|
heroTitle: 'recovering.server_provider_connected'.tr(),
|
||||||
heroSubtitle: 'recovering.hetzner_connected_description'.tr(
|
heroSubtitle: 'recovering.server_provider_connected_description'.tr(
|
||||||
args: [appConfig.state.serverDomain?.domainName ?? 'your domain'],
|
args: [appConfig.state.serverDomain?.domainName ?? 'your domain'],
|
||||||
),
|
),
|
||||||
hasBackButton: true,
|
hasBackButton: true,
|
||||||
|
@ -40,7 +40,8 @@ class RecoveryHetznerConnected extends StatelessWidget {
|
||||||
formFieldCubit: context.read<ProviderFormCubit>().apiKey,
|
formFieldCubit: context.read<ProviderFormCubit>().apiKey,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
labelText: 'recovering.hetzner_connected_placeholder'.tr(),
|
labelText:
|
||||||
|
'recovering.server_provider_connected_placeholder'.tr(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
|
@ -101,10 +101,10 @@ class StringGenerators {
|
||||||
hasSymbols: true,
|
hasSymbols: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
static StringGeneratorFunction dbStorageName = () => getRandomString(
|
static StringGeneratorFunction storageName = () => getRandomString(
|
||||||
6,
|
6,
|
||||||
hasLowercaseLetters: true,
|
hasLowercaseLetters: true,
|
||||||
hasUppercaseLetters: true,
|
hasUppercaseLetters: false,
|
||||||
hasNumbers: true,
|
hasNumbers: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
32
pubspec.lock
32
pubspec.lock
|
@ -35,7 +35,7 @@ packages:
|
||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.2"
|
version: "2.9.0"
|
||||||
auto_size_text:
|
auto_size_text:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -126,7 +126,7 @@ packages:
|
||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.1"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -147,7 +147,7 @@ packages:
|
||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -350,7 +350,7 @@ packages:
|
||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.1"
|
||||||
ffi:
|
ffi:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -631,12 +631,12 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.3"
|
version: "1.1.3"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http
|
name: http
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.4"
|
version: "0.13.5"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -762,21 +762,21 @@ packages:
|
||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.11"
|
version: "0.12.12"
|
||||||
material_color_utilities:
|
material_color_utilities:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.4"
|
version: "0.1.5"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.7.0"
|
version: "1.8.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -846,7 +846,7 @@ packages:
|
||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
version: "1.8.2"
|
||||||
path_drawing:
|
path_drawing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1159,7 +1159,7 @@ packages:
|
||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.9.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1187,7 +1187,7 @@ packages:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.1"
|
||||||
system_theme:
|
system_theme:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1208,28 +1208,28 @@ packages:
|
||||||
name: term_glyph
|
name: term_glyph
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.1"
|
||||||
test:
|
test:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.21.1"
|
version: "1.21.4"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.9"
|
version: "0.4.12"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.13"
|
version: "0.4.16"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -33,6 +33,7 @@ dependencies:
|
||||||
gtk_theme_fl: ^0.0.1
|
gtk_theme_fl: ^0.0.1
|
||||||
hive: ^2.2.3
|
hive: ^2.2.3
|
||||||
hive_flutter: ^1.1.0
|
hive_flutter: ^1.1.0
|
||||||
|
http: ^0.13.5
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
ionicons: ^0.1.2
|
ionicons: ^0.1.2
|
||||||
json_annotation: ^4.6.0
|
json_annotation: ^4.6.0
|
||||||
|
|
Loading…
Reference in a new issue