Implement Provider Api Abstractions

pull/99/head
NaiJi ✨ 2022-07-12 15:54:16 +03:00
parent f40749ca57
commit 37b7e9f839
49 changed files with 1058 additions and 316 deletions

View File

@ -1,7 +1,18 @@
targets: targets:
$default: $default:
builders: builders:
graphql_codegen:
options:
scalars:
DateTime:
type: DateTime
fromJsonFunctionName: dateTimeFromJson
toJsonFunctionName: dateTimeToJson
import: package:selfprivacy/utils/scalars.dart
clients:
- graphql
- graphql_flutter
json_serializable: json_serializable:
options: options:
create_factory: true create_factory: true
create_to_json: false create_to_json: false

View File

@ -0,0 +1,9 @@
query GetApiTokensQuery {
api {
devices {
creationDate
isCaller
name
}
}
}

View File

@ -0,0 +1,34 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'get_api_tokens.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Query$GetApiTokensQuery _$Query$GetApiTokensQueryFromJson(
Map<String, dynamic> json) =>
Query$GetApiTokensQuery(
api: Query$GetApiTokensQuery$api.fromJson(
json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Query$GetApiTokensQuery$api _$Query$GetApiTokensQuery$apiFromJson(
Map<String, dynamic> json) =>
Query$GetApiTokensQuery$api(
devices: (json['devices'] as List<dynamic>)
.map((e) => Query$GetApiTokensQuery$api$devices.fromJson(
e as Map<String, dynamic>))
.toList(),
$__typename: json['__typename'] as String,
);
Query$GetApiTokensQuery$api$devices
_$Query$GetApiTokensQuery$api$devicesFromJson(Map<String, dynamic> json) =>
Query$GetApiTokensQuery$api$devices(
creationDate: dateTimeFromJson(json['creationDate']),
isCaller: json['isCaller'] as bool,
name: json['name'] as String,
$__typename: json['__typename'] as String,
);

View File

@ -0,0 +1,5 @@
query GetApiVersionQuery {
api {
version
}
}

View File

@ -0,0 +1,22 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'get_api_version.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Query$GetApiVersionQuery _$Query$GetApiVersionQueryFromJson(
Map<String, dynamic> json) =>
Query$GetApiVersionQuery(
api: Query$GetApiVersionQuery$api.fromJson(
json['api'] as Map<String, dynamic>),
$__typename: json['__typename'] as String,
);
Query$GetApiVersionQuery$api _$Query$GetApiVersionQuery$apiFromJson(
Map<String, dynamic> json) =>
Query$GetApiVersionQuery$api(
version: json['version'] as String,
$__typename: json['__typename'] as String,
);

View File

@ -0,0 +1,151 @@
scalar DateTime
type Alert {
severity: Severity!
title: String!
message: String!
timestamp: DateTime
}
type Api {
version: String!
devices: [ApiDevice!]!
recoveryKey: ApiRecoveryKeyStatus!
}
type ApiDevice {
name: String!
creationDate: DateTime!
isCaller: Boolean!
}
type ApiKeyMutationReturn implements MutationReturnInterface {
success: Boolean!
message: String!
code: Int!
key: String
}
type ApiRecoveryKeyStatus {
exists: Boolean!
valid: Boolean!
creationDate: DateTime
expirationDate: DateTime
usesLeft: Int
}
type AutoUpgradeOptions {
enable: Boolean!
allowReboot: Boolean!
}
type DeviceApiTokenMutationReturn implements MutationReturnInterface {
success: Boolean!
message: String!
code: Int!
token: String
}
enum DnsProvider {
CLOUDFLARE
}
type DnsRecord {
recordType: String!
name: String!
content: String!
ttl: Int!
priority: Int
}
type GenericMutationReturn implements MutationReturnInterface {
success: Boolean!
message: String!
code: Int!
}
type Mutation {
getNewRecoveryApiKey(limits: RecoveryKeyLimitsInput!): ApiKeyMutationReturn!
useRecoveryApiKey(input: UseRecoveryKeyInput!): DeviceApiTokenMutationReturn!
refreshDeviceApiToken: DeviceApiTokenMutationReturn!
deleteDeviceApiToken(device: String!): GenericMutationReturn!
getNewDeviceApiKey: ApiKeyMutationReturn!
invalidateNewDeviceApiKey: GenericMutationReturn!
authorizeWithNewDeviceApiKey(input: UseNewDeviceKeyInput!): DeviceApiTokenMutationReturn!
}
interface MutationReturnInterface {
success: Boolean!
message: String!
code: Int!
}
type Query {
system: System!
api: Api!
}
input RecoveryKeyLimitsInput {
expirationDate: DateTime
uses: Int
}
enum ServerProvider {
HETZNER
}
enum Severity {
INFO
WARNING
ERROR
CRITICAL
SUCCESS
}
type SshSettings {
enable: Boolean!
passwordAuthentication: Boolean!
rootSshKeys: [String!]!
}
type System {
status: Alert!
domain: SystemDomainInfo!
settings: SystemSettings!
info: SystemInfo!
provider: SystemProviderInfo!
busy: Boolean!
}
type SystemDomainInfo {
domain: String!
hostname: String!
provider: DnsProvider!
requiredDnsRecords: [DnsRecord!]!
}
type SystemInfo {
systemVersion: String!
pythonVersion: String!
}
type SystemProviderInfo {
provider: ServerProvider!
id: String!
}
type SystemSettings {
autoUpgrade: AutoUpgradeOptions!
ssh: SshSettings!
timezone: String!
}
input UseNewDeviceKeyInput {
key: String!
deviceName: String!
}
input UseRecoveryKeyInput {
key: String!
deviceName: String!
}

View File

@ -0,0 +1,28 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'schema.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Input$RecoveryKeyLimitsInput _$Input$RecoveryKeyLimitsInputFromJson(
Map<String, dynamic> json) =>
Input$RecoveryKeyLimitsInput(
expirationDate: _nullable$dateTimeFromJson(json['expirationDate']),
uses: json['uses'] as int?,
);
Input$UseNewDeviceKeyInput _$Input$UseNewDeviceKeyInputFromJson(
Map<String, dynamic> json) =>
Input$UseNewDeviceKeyInput(
key: json['key'] as String,
deviceName: json['deviceName'] as String,
);
Input$UseRecoveryKeyInput _$Input$UseRecoveryKeyInputFromJson(
Map<String, dynamic> json) =>
Input$UseRecoveryKeyInput(
key: json['key'] as String,
deviceName: json['deviceName'] as String,
);

View File

@ -0,0 +1,39 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_factory.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
class ApiFactoryCreator {
static ProviderApiFactory createProviderApiFactory(
final ServerProvider provider,
) {
switch (provider) {
case ServerProvider.hetzner:
case ServerProvider.unknown: // ?? :)
return HetznerApiFactory();
}
}
// createDnsApiFactory
// createStorageApiFactory
// etc . . .
}
class VolumeApiFactoryCreator {
static VolumeProviderApiFactory createVolumeProviderApiFactory(
final ServerProvider provider,
) {
switch (provider) {
case ServerProvider.hetzner:
case ServerProvider.unknown: // ?? :)
return HetznerApiFactory();
}
}
// createDnsApiFactory
// createStorageApiFactory
// etc . . .
}

View File

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
class BackblazeApiAuth { class BackblazeApiAuth {

View File

@ -2,7 +2,7 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.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';

View File

@ -3,18 +3,15 @@ import 'dart:io';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/providers/volume_provider.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/provider_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/utils/password_generator.dart'; import 'package:selfprivacy/utils/password_generator.dart';
class HetznerApi extends ApiMap { class HetznerApi extends VolumeProviderApi {
HetznerApi({this.hasLogger = false, this.isWithToken = true}); HetznerApi({final super.hasLogger = false, final super.isWithToken = true});
@override
bool hasLogger;
@override
bool isWithToken;
@override @override
BaseOptions get options { BaseOptions get options {
@ -35,27 +32,43 @@ class HetznerApi extends ApiMap {
@override @override
String rootAddress = 'https://api.hetzner.cloud/v1'; String rootAddress = 'https://api.hetzner.cloud/v1';
Future<bool> isValid(final String token) async { @override
validateStatus = (final int? status) => Future<bool> isApiTokenValid(final String token) async {
status == HttpStatus.ok || status == HttpStatus.unauthorized; bool isValid = false;
Response? response;
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get( try {
'/servers', response = await client.get(
options: Options( '/servers',
headers: {'Authorization': 'Bearer $token'}, options: Options(
), headers: {'Authorization': 'Bearer $token'},
); ),
close(client); );
} catch (e) {
if (response.statusCode == HttpStatus.ok) { print(e);
return true; isValid = false;
} else if (response.statusCode == HttpStatus.unauthorized) { } finally {
return false; close(client);
} else {
throw Exception('code: ${response.statusCode}');
} }
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;
} }
@override
RegExp getApiTokenValidation() =>
RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
@override
Future<ServerVolume?> createVolume() async { Future<ServerVolume?> createVolume() async {
ServerVolume? volume; ServerVolume? volume;
@ -92,6 +105,7 @@ class HetznerApi extends ApiMap {
return volume; return volume;
} }
@override
Future<List<ServerVolume>> getVolumes({final String? status}) async { Future<List<ServerVolume>> getVolumes({final String? status}) async {
final List<ServerVolume> volumes = []; final List<ServerVolume> volumes = [];
@ -127,6 +141,7 @@ class HetznerApi extends ApiMap {
return volumes; return volumes;
} }
@override
Future<ServerVolume?> getVolume(final int id) async { Future<ServerVolume?> getVolume(final int id) async {
ServerVolume? volume; ServerVolume? volume;
@ -153,7 +168,8 @@ class HetznerApi extends ApiMap {
return volume; return volume;
} }
void deleteVolume(final int id) async { @override
Future<void> deleteVolume(final int id) async {
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
await client.delete('/volumes/$id'); await client.delete('/volumes/$id');
@ -164,6 +180,7 @@ class HetznerApi extends ApiMap {
} }
} }
@override
Future<bool> attachVolume(final int volumeId, final int serverId) async { Future<bool> attachVolume(final int volumeId, final int serverId) async {
bool success = false; bool success = false;
@ -187,6 +204,7 @@ class HetznerApi extends ApiMap {
return success; return success;
} }
@override
Future<bool> detachVolume(final int volumeId) async { Future<bool> detachVolume(final int volumeId) async {
bool success = false; bool success = false;
@ -204,6 +222,7 @@ class HetznerApi extends ApiMap {
return success; return success;
} }
@override
Future<bool> resizeVolume(final int volumeId, final int sizeGb) async { Future<bool> resizeVolume(final int volumeId, final int sizeGb) async {
bool success = false; bool success = false;
@ -226,8 +245,35 @@ class HetznerApi extends ApiMap {
return success; return success;
} }
@override
Future<ServerHostingDetails?> createServer({ Future<ServerHostingDetails?> createServer({
required final String cloudFlareKey, required final String dnsApiToken,
required final User rootUser,
required final String domainName,
}) async {
ServerHostingDetails? details;
final ServerVolume? newVolume = await createVolume();
if (newVolume == null) {
return details;
}
details = await createServerByVolume(
dnsApiToken: dnsApiToken,
rootUser: rootUser,
domainName: domainName,
dataBase: newVolume,
);
if (details == null) {
deleteVolume(newVolume.id);
}
return details;
}
Future<ServerHostingDetails?> createServerByVolume({
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 dataBase,
@ -250,7 +296,7 @@ class HetznerApi extends ApiMap {
/// check the branch name, it could be "development" or "master". /// 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=$cloudFlareKey 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/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";
print(userdataString); print(userdataString);
final Map<String, Object> data = { final Map<String, Object> data = {
@ -285,6 +331,7 @@ class HetznerApi extends ApiMap {
); );
} on DioError catch (e) { } on DioError catch (e) {
print(e); print(e);
deleteVolume(dataBase.id);
rethrow; rethrow;
} catch (e) { } catch (e) {
print(e); print(e);
@ -312,7 +359,8 @@ class HetznerApi extends ApiMap {
return hostname; return hostname;
} }
Future<void> deleteSelfprivacyServerAndAllVolumes({ @override
Future<void> deleteServer({
required final String domainName, required final String domainName,
}) async { }) async {
final Dio client = await getClient(); final Dio client = await getClient();
@ -339,22 +387,34 @@ class HetznerApi extends ApiMap {
close(client); close(client);
} }
Future<ServerHostingDetails> reset() async { @override
Future<ServerHostingDetails> restart() async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!; final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
final Dio client = await getClient(); final Dio client = await getClient();
await client.post('/servers/${server.id}/actions/reset'); try {
close(client); await client.post('/servers/${server.id}/actions/reset');
} catch (e) {
print(e);
} finally {
close(client);
}
return server.copyWith(startTime: DateTime.now()); return server.copyWith(startTime: DateTime.now());
} }
@override
Future<ServerHostingDetails> powerOn() async { Future<ServerHostingDetails> powerOn() async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!; final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
final Dio client = await getClient(); final Dio client = await getClient();
await client.post('/servers/${server.id}/actions/poweron'); try {
close(client); await client.post('/servers/${server.id}/actions/poweron');
} catch (e) {
print(e);
} finally {
close(client);
}
return server.copyWith(startTime: DateTime.now()); return server.copyWith(startTime: DateTime.now());
} }
@ -381,41 +441,40 @@ class HetznerApi extends ApiMap {
return res.data; return res.data;
} }
Future<HetznerServerInfo> getInfo() async { Future<ProviderServerInfo> getInfo() async {
final ServerHostingDetails? hetznerServer = final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails; getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get('/servers/${hetznerServer!.id}'); final Response response = await client.get('/servers/${hetznerServer!.id}');
close(client); close(client);
return HetznerServerInfo.fromJson(response.data!['server']); return ProviderServerInfo.fromJson(response.data!['server']);
} }
Future<List<HetznerServerInfo>> getServers() async { @override
Future<List<ProviderServerInfo>> getServers() async {
final Dio client = await getClient(); final Dio client = await getClient();
final Response response = await client.get('/servers'); final Response response = await client.get('/servers');
close(client); close(client);
return (response.data!['servers'] as List) return (response.data!['servers'] as List)
// ignore: unnecessary_lambdas // ignore: unnecessary_lambdas
.map((final e) => HetznerServerInfo.fromJson(e)) .map((final e) => ProviderServerInfo.fromJson(e))
.toList(); .toList();
} }
@override
Future<void> createReverseDns({ Future<void> createReverseDns({
required final String ip4, required final ServerHostingDetails serverDetails,
required final String domainName, required final ServerDomain domain,
}) async { }) async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient(); final Dio client = await getClient();
try { try {
await client.post( await client.post(
'/servers/${hetznerServer!.id}/actions/change_dns_ptr', '/servers/${serverDetails.id}/actions/change_dns_ptr',
data: { data: {
'ip': ip4, 'ip': serverDetails.ip4,
'dns_ptr': domainName, 'dns_ptr': domain.domainName,
}, },
); );
} catch (e) { } catch (e) {

View File

@ -0,0 +1,25 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/volume_provider.dart';
class HetznerApiFactory extends ProviderApiFactory
with VolumeProviderApiFactory {
@override
ProviderApi getProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
@override
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
}) =>
HetznerApi(
hasLogger: settings.hasLogger,
isWithToken: settings.isWithToken,
);
}

View File

@ -0,0 +1,32 @@
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_domain.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/json/provider_server_info.dart';
abstract class ProviderApi extends ApiMap {
ProviderApi({this.hasLogger = false, this.isWithToken = true});
@override
bool hasLogger;
@override
bool isWithToken;
Future<List<ProviderServerInfo>> getServers();
Future<ServerHostingDetails> restart();
Future<ServerHostingDetails> powerOn();
Future<void> deleteServer({required final String domainName});
Future<ServerHostingDetails?> createServer({
required final String dnsApiToken,
required final User rootUser,
required final String domainName,
});
Future<void> createReverseDns({
required final ServerHostingDetails serverDetails,
required final ServerDomain domain,
});
Future<bool> isApiTokenValid(final String token);
RegExp getApiTokenValidation();
}

View File

@ -0,0 +1,20 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/volume_provider.dart';
class ProviderApiSettings {
const ProviderApiSettings({this.hasLogger = false, this.isWithToken = true});
final bool hasLogger;
final bool isWithToken;
}
abstract class ProviderApiFactory {
ProviderApi getProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}
mixin VolumeProviderApiFactory {
VolumeProviderApi getVolumeProvider({
final ProviderApiSettings settings = const ProviderApiSettings(),
});
}

View File

@ -0,0 +1,17 @@
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
abstract class VolumeProviderApi extends ProviderApi {
VolumeProviderApi({
final super.hasLogger = false,
final super.isWithToken = true,
});
Future<ServerVolume?> createVolume();
Future<List<ServerVolume>> getVolumes({final String? status});
Future<ServerVolume?> getVolume(final int id);
Future<bool> attachVolume(final int volumeId, final int serverId);
Future<bool> detachVolume(final int volumeId);
Future<bool> resizeVolume(final int volumeId, final int sizeGb);
Future<void> deleteVolume(final int id);
}

View File

@ -15,7 +15,7 @@ import 'package:selfprivacy/logic/models/json/device_token.dart';
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
import 'package:selfprivacy/logic/models/timezone_settings.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
class ApiResponse<D> { class ApiResponse<D> {
ApiResponse({ ApiResponse({

View File

@ -2,8 +2,8 @@ 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/backblaze.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.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';

View File

@ -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/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.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';

View File

@ -3,8 +3,8 @@ import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_depe
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/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
part 'dns_records_state.dart'; part 'dns_records_state.dart';

View File

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/backblaze.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.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:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';

View File

@ -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/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.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'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';

View File

@ -1,5 +1,5 @@
import 'package:cubit_form/cubit_form.dart'; import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.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';

View File

@ -2,13 +2,13 @@ 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/hetzner.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'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
class HetznerFormCubit extends FormCubit { class ProviderFormCubit extends FormCubit {
HetznerFormCubit(this.serverInstallationCubit) { ProviderFormCubit(this.serverInstallationCubit) {
final RegExp regExp = RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); final RegExp regExp =
serverInstallationCubit.getProviderApiTokenValidation();
apiKey = FieldCubit( apiKey = FieldCubit(
initalValue: '', initalValue: '',
validations: [ validations: [
@ -30,16 +30,15 @@ class HetznerFormCubit extends FormCubit {
} }
final ServerInstallationCubit serverInstallationCubit; final ServerInstallationCubit serverInstallationCubit;
late final FieldCubit<String> apiKey; late final FieldCubit<String> apiKey;
@override @override
FutureOr<bool> asyncValidation() async { FutureOr<bool> asyncValidation() async {
late bool isKeyValid; late bool isKeyValid;
final HetznerApi apiClient = HetznerApi(isWithToken: false);
try { try {
isKeyValid = await apiClient.isValid(apiKey.state.value); isKeyValid = await serverInstallationCubit
.isProviderApiTokenValid(apiKey.state.value);
} catch (e) { } catch (e) {
addError(e); addError(e);
isKeyValid = false; isKeyValid = false;

View File

@ -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/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.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';

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner.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/hetzner_metrics.dart';

View File

@ -2,7 +2,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/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.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';

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.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';

View File

@ -3,7 +3,7 @@ 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/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/json/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/json/provider_server_info.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';

View File

@ -1,7 +1,7 @@
import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart'; import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/json/provider_server_info.dart';
import 'package:selfprivacy/logic/models/timezone_settings.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart';
class ServerDetailsRepository { class ServerDetailsRepository {
@ -24,7 +24,7 @@ class ServerDetailsRepositoryDto {
required this.serverTimezone, required this.serverTimezone,
required this.autoUpgradeSettings, required this.autoUpgradeSettings,
}); });
final HetznerServerInfo hetznerServerInfo; final ProviderServerInfo hetznerServerInfo;
final TimeZoneSettings serverTimezone; final TimeZoneSettings serverTimezone;

View File

@ -22,7 +22,7 @@ class Loaded extends ServerDetailsState {
required this.autoUpgradeSettings, required this.autoUpgradeSettings,
required this.checkTime, required this.checkTime,
}); });
final HetznerServerInfo serverInfo; final ProviderServerInfo serverInfo;
final TimeZoneSettings serverTimezone; final TimeZoneSettings serverTimezone;

View File

@ -4,6 +4,7 @@ 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/providers/provider_factory.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';
@ -49,13 +50,37 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
} }
} }
RegExp getProviderApiTokenValidation() {
if (repository.providerApiFactory == null) {
print(
"validateProviderApiToken: Factory for API provider doesn't exist!",
);
return RegExp(r'');
}
return repository.providerApiFactory!.getProvider().getApiTokenValidation();
}
Future<bool> isProviderApiTokenValid(final String providerToken) async {
if (repository.providerApiFactory == null) {
print(
"validateProviderApiToken: Factory for API provider doesn't exist!",
);
return false;
}
return repository.providerApiFactory!
.getProvider(settings: const ProviderApiSettings(isWithToken: false))
.isApiTokenValid(providerToken);
}
void setHetznerKey(final String hetznerKey) async { void setHetznerKey(final String hetznerKey) async {
await repository.saveHetznerKey(hetznerKey); await repository.saveHetznerKey(hetznerKey);
if (state is ServerInstallationRecovery) { if (state is ServerInstallationRecovery) {
emit( emit(
(state as ServerInstallationRecovery).copyWith( (state as ServerInstallationRecovery).copyWith(
hetznerKey: hetznerKey, providerApiToken: hetznerKey,
currentStep: RecoveryStep.serverSelection, currentStep: RecoveryStep.serverSelection,
), ),
); );
@ -63,7 +88,9 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
} }
emit( emit(
(state as ServerInstallationNotFinished).copyWith(hetznerKey: hetznerKey), (state as ServerInstallationNotFinished).copyWith(
providerApiToken: hetznerKey,
),
); );
} }
@ -116,12 +143,16 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
); );
Future<void> onSuccess(final ServerHostingDetails serverDetails) async { Future<void> onSuccess(final ServerHostingDetails serverDetails) async {
await repository.createDnsRecords( final bool dnsRecordsCreated = await repository.createDnsRecords(
serverDetails.ip4, serverDetails.ip4,
state.serverDomain!, state.serverDomain!,
onCancel: onCancel, onCancel: onCancel,
); );
if (dnsRecordsCreated) {
repository.onCreationSuccess(serverDetails, state.serverDomain!);
}
emit( emit(
(state as ServerInstallationNotFinished).copyWith( (state as ServerInstallationNotFinished).copyWith(
isLoading: false, isLoading: false,
@ -164,9 +195,24 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
); );
if (matches.values.every((final bool value) => value)) { if (matches.values.every((final bool value) => value)) {
final ServerHostingDetails server = await repository.startServer( final ServerHostingDetails? server = await repository.startServer(
dataState.serverDetails!, dataState.serverDetails!,
); );
if (server == null) {
final ServerInstallationNotFinished newState = dataState.copyWith(
isLoading: false,
dnsMatches: matches,
);
emit(newState);
runDelayed(
startServerIfDnsIsOkay,
const Duration(seconds: 30),
newState,
);
return;
}
await repository.saveServerDetails(server); await repository.saveServerDetails(server);
await repository.saveIsServerStarted(true); await repository.saveIsServerStarted(true);
@ -464,7 +510,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
final ServerInstallationRecovery dataState = final ServerInstallationRecovery dataState =
state as ServerInstallationRecovery; state as ServerInstallationRecovery;
final List<ServerBasicInfo> servers = final List<ServerBasicInfo> servers =
await repository.getServersOnHetznerAccount(); await repository.getServersOnProviderAccount();
final Iterable<ServerBasicInfoWithValidators> validated = servers.map( final Iterable<ServerBasicInfoWithValidators> validated = servers.map(
(final ServerBasicInfo server) => (final ServerBasicInfo server) =>
ServerBasicInfoWithValidators.fromServerBasicInfo( ServerBasicInfoWithValidators.fromServerBasicInfo(
@ -566,7 +612,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
print('================================'); print('================================');
print('ServerInstallationState changed!'); print('ServerInstallationState changed!');
print('Current type: ${change.nextState.runtimeType}'); print('Current type: ${change.nextState.runtimeType}');
print('Hetzner key: ${change.nextState.hetznerKey}'); print('Hetzner key: ${change.nextState.providerApiToken}');
print('Cloudflare key: ${change.nextState.cloudFlareKey}'); print('Cloudflare key: ${change.nextState.cloudFlareKey}');
print('Domain: ${change.nextState.serverDomain}'); print('Domain: ${change.nextState.serverDomain}');
print('BackblazeCredential: ${change.nextState.backblazeCredential}'); print('BackblazeCredential: ${change.nextState.backblazeCredential}');
@ -599,7 +645,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
await repository.deleteServerRelatedRecords(); await repository.deleteServerRelatedRecords();
emit( emit(
ServerInstallationNotFinished( ServerInstallationNotFinished(
hetznerKey: state.hetznerKey, providerApiToken: state.providerApiToken,
serverDomain: state.serverDomain, serverDomain: state.serverDomain,
cloudFlareKey: state.cloudFlareKey, cloudFlareKey: state.cloudFlareKey,
backblazeCredential: state.backblazeCredential, backblazeCredential: state.backblazeCredential,

View File

@ -9,16 +9,18 @@ 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/cloudflare.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/providers/provider_factory.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.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';
import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/json/device_token.dart'; import 'package:selfprivacy/logic/models/json/device_token.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/json/provider_server_info.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/ui/components/action_button/action_button.dart';
@ -39,9 +41,13 @@ 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);
ProviderApiFactory? providerApiFactory =
ApiFactoryCreator.createProviderApiFactory(
ServerProvider.hetzner, // HARDCODE FOR NOW!!!
); // Remove when provider selection is implemented.
Future<ServerInstallationState> load() async { Future<ServerInstallationState> load() async {
final String? hetznerToken = getIt<ApiConfigModel>().hetznerKey; final String? providerApiToken = getIt<ApiConfigModel>().hetznerKey;
final String? cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey; final String? cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey;
final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain; final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
final BackblazeCredential? backblazeCredential = final BackblazeCredential? backblazeCredential =
@ -49,9 +55,14 @@ class ServerInstallationRepository {
final ServerHostingDetails? serverDetails = final ServerHostingDetails? serverDetails =
getIt<ApiConfigModel>().serverDetails; getIt<ApiConfigModel>().serverDetails;
if (serverDetails != null) {
providerApiFactory =
ApiFactoryCreator.createProviderApiFactory(serverDetails.provider);
}
if (box.get(BNames.hasFinalChecked, defaultValue: false)) { if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
return ServerInstallationFinished( return ServerInstallationFinished(
hetznerKey: hetznerToken!, providerApiToken: providerApiToken!,
cloudFlareKey: cloudflareToken!, cloudFlareKey: cloudflareToken!,
serverDomain: serverDomain!, serverDomain: serverDomain!,
backblazeCredential: backblazeCredential!, backblazeCredential: backblazeCredential!,
@ -68,14 +79,14 @@ class ServerInstallationRepository {
if (box.get(BNames.isRecoveringServer, defaultValue: false) && if (box.get(BNames.isRecoveringServer, defaultValue: false) &&
serverDomain != null) { serverDomain != null) {
return ServerInstallationRecovery( return ServerInstallationRecovery(
hetznerKey: hetznerToken, providerApiToken: providerApiToken,
cloudFlareKey: cloudflareToken, cloudFlareKey: cloudflareToken,
serverDomain: serverDomain, serverDomain: serverDomain,
backblazeCredential: backblazeCredential, backblazeCredential: backblazeCredential,
serverDetails: serverDetails, serverDetails: serverDetails,
rootUser: box.get(BNames.rootUser), rootUser: box.get(BNames.rootUser),
currentStep: _getCurrentRecoveryStep( currentStep: _getCurrentRecoveryStep(
hetznerToken, providerApiToken,
cloudflareToken, cloudflareToken,
serverDomain, serverDomain,
serverDetails, serverDetails,
@ -85,7 +96,7 @@ class ServerInstallationRepository {
} }
return ServerInstallationNotFinished( return ServerInstallationNotFinished(
hetznerKey: hetznerToken, providerApiToken: providerApiToken,
cloudFlareKey: cloudflareToken, cloudFlareKey: cloudflareToken,
serverDomain: serverDomain, serverDomain: serverDomain,
backblazeCredential: backblazeCredential, backblazeCredential: backblazeCredential,
@ -127,11 +138,18 @@ class ServerInstallationRepository {
usersBox.clear(); usersBox.clear();
} }
Future<ServerHostingDetails> startServer( Future<ServerHostingDetails?> startServer(
final ServerHostingDetails hetznerServer, final ServerHostingDetails hetznerServer,
) async { ) async {
final HetznerApi hetznerApi = HetznerApi(); ServerHostingDetails? details;
final ServerHostingDetails serverDetails = await hetznerApi.powerOn();
if (providerApiFactory == null) {
print("startServer: Factory for API provider doesn't exist!");
return details;
}
final ProviderApi api = providerApiFactory!.getProvider();
final ServerHostingDetails serverDetails = await api.powerOn();
return serverDetails; return serverDetails;
} }
@ -208,23 +226,17 @@ class ServerInstallationRepository {
required final Future<void> Function(ServerHostingDetails serverDetails) required final Future<void> Function(ServerHostingDetails serverDetails)
onSuccess, onSuccess,
}) async { }) async {
final HetznerApi hetznerApi = HetznerApi(); if (providerApiFactory == null) {
late ServerVolume dataBase; print("createServer: Factory for API provider doesn't exist!");
return;
}
final ProviderApi api = providerApiFactory!.getProvider();
try { try {
final ServerVolume? createdVolume = await hetznerApi.createVolume(); final ServerHostingDetails? serverDetails = await api.createServer(
if (createdVolume == null) { dnsApiToken: cloudFlareKey,
print('Volume is not created!');
return;
}
dataBase = createdVolume;
final ServerHostingDetails? serverDetails = await hetznerApi.createServer(
cloudFlareKey: cloudFlareKey,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
dataBase: dataBase,
); );
if (serverDetails == null) { if (serverDetails == null) {
@ -245,17 +257,16 @@ class ServerInstallationRepository {
text: 'basis.delete'.tr(), text: 'basis.delete'.tr(),
isRed: true, isRed: true,
onPressed: () async { onPressed: () async {
await hetznerApi.deleteSelfprivacyServerAndAllVolumes( await api.deleteServer(
domainName: domainName, domainName: domainName,
); );
ServerHostingDetails? serverDetails; ServerHostingDetails? serverDetails;
try { try {
serverDetails = await hetznerApi.createServer( serverDetails = await api.createServer(
cloudFlareKey: cloudFlareKey, dnsApiToken: cloudFlareKey,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
dataBase: dataBase,
); );
} catch (e) { } catch (e) {
print(e); print(e);
@ -280,25 +291,45 @@ class ServerInstallationRepository {
} }
} }
Future<void> createDnsRecords( Future<void> onCreationSuccess(
final ServerHostingDetails serverDetails,
final ServerDomain domain,
) async {
if (providerApiFactory == null) {
print("onCreationSuccess: Factory for API provider doesn't exist!");
return;
}
final ProviderApi api = providerApiFactory!.getProvider();
return api.createReverseDns(
serverDetails: serverDetails,
domain: domain,
);
}
Future<bool> createDnsRecords(
final String ip4, final String ip4,
final ServerDomain cloudFlareDomain, { final ServerDomain domain, {
required final void Function() onCancel, required final void Function() onCancel,
}) async { }) async {
final CloudflareApi cloudflareApi = CloudflareApi(); final CloudflareApi cloudflareApi = CloudflareApi();
if (providerApiFactory == null) {
print("createServer: Factory for API provider doesn't exist!");
return false;
}
final ProviderApi api = providerApiFactory!.getProvider();
await cloudflareApi.removeSimilarRecords( await cloudflareApi.removeSimilarRecords(
ip4: ip4, ip4: ip4,
cloudFlareDomain: cloudFlareDomain, cloudFlareDomain: domain,
); );
try { try {
await cloudflareApi.createMultipleDnsRecords( await cloudflareApi.createMultipleDnsRecords(
ip4: ip4, ip4: ip4,
cloudFlareDomain: cloudFlareDomain, cloudFlareDomain: domain,
); );
} on DioError catch (e) { } on DioError catch (e) {
final HetznerApi hetznerApi = HetznerApi();
final NavigationService nav = getIt.get<NavigationService>(); final NavigationService nav = getIt.get<NavigationService>();
nav.showPopUpDialog( nav.showPopUpDialog(
BrandAlert( BrandAlert(
@ -311,8 +342,8 @@ class ServerInstallationRepository {
text: 'basis.delete'.tr(), text: 'basis.delete'.tr(),
isRed: true, isRed: true,
onPressed: () async { onPressed: () async {
await hetznerApi.deleteSelfprivacyServerAndAllVolumes( await api.deleteServer(
domainName: cloudFlareDomain.domainName, domainName: domain.domainName,
); );
onCancel(); onCancel();
@ -325,12 +356,10 @@ class ServerInstallationRepository {
], ],
), ),
); );
return false;
} }
await HetznerApi().createReverseDns( return true;
ip4: ip4,
domainName: cloudFlareDomain.domainName,
);
} }
Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async { Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async {
@ -354,13 +383,13 @@ class ServerInstallationRepository {
} }
Future<ServerHostingDetails> restart() async { Future<ServerHostingDetails> restart() async {
final HetznerApi hetznerApi = HetznerApi(); final ProviderApi api = providerApiFactory!.getProvider();
return hetznerApi.reset(); return api.restart();
} }
Future<ServerHostingDetails> powerOn() async { Future<ServerHostingDetails> powerOn() async {
final HetznerApi hetznerApi = HetznerApi(); final ProviderApi api = providerApiFactory!.getProvider();
return hetznerApi.powerOn(); return api.powerOn();
} }
Future<ServerRecoveryCapabilities> getRecoveryCapabilities( Future<ServerRecoveryCapabilities> getRecoveryCapabilities(
@ -595,12 +624,19 @@ class ServerInstallationRepository {
} }
} }
Future<List<ServerBasicInfo>> getServersOnHetznerAccount() async { Future<List<ServerBasicInfo>> getServersOnProviderAccount() async {
final HetznerApi hetznerApi = HetznerApi(); if (providerApiFactory == null) {
final List<HetznerServerInfo> servers = await hetznerApi.getServers(); print(
'getServersOnProviderAccount: '
"Factory for API provider doesn't exist!",
);
return [];
}
final ProviderApi api = providerApiFactory!.getProvider();
final List<ProviderServerInfo> servers = await api.getServers();
return servers return servers
.map( .map(
(final HetznerServerInfo server) => ServerBasicInfo( (final ProviderServerInfo server) => ServerBasicInfo(
id: server.id, id: server.id,
name: server.name, name: server.name,
ip: server.publicNet.ipv4.ip, ip: server.publicNet.ipv4.ip,
@ -687,10 +723,14 @@ class ServerInstallationRepository {
} }
Future<void> deleteServer(final ServerDomain serverDomain) async { Future<void> deleteServer(final ServerDomain serverDomain) async {
final HetznerApi hetznerApi = HetznerApi(); if (providerApiFactory == null) {
print("deleteServer: Factory for API provider doesn't exist!");
return;
}
final ProviderApi api = providerApiFactory!.getProvider();
final CloudflareApi cloudFlare = CloudflareApi(); final CloudflareApi cloudFlare = CloudflareApi();
await hetznerApi.deleteSelfprivacyServerAndAllVolumes( await api.deleteServer(
domainName: serverDomain.domainName, domainName: serverDomain.domainName,
); );

View File

@ -2,7 +2,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.hetznerKey, required this.providerApiToken,
required this.cloudFlareKey, required this.cloudFlareKey,
required this.backblazeCredential, required this.backblazeCredential,
required this.serverDomain, required this.serverDomain,
@ -15,7 +15,7 @@ abstract class ServerInstallationState extends Equatable {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -25,7 +25,7 @@ abstract class ServerInstallationState extends Equatable {
isServerResetedFirstTime, isServerResetedFirstTime,
]; ];
final String? hetznerKey; final String? providerApiToken;
final String? cloudFlareKey; final String? cloudFlareKey;
final BackblazeCredential? backblazeCredential; final BackblazeCredential? backblazeCredential;
final ServerDomain? serverDomain; final ServerDomain? serverDomain;
@ -35,7 +35,7 @@ abstract class ServerInstallationState extends Equatable {
final bool isServerResetedFirstTime; final bool isServerResetedFirstTime;
final bool isServerResetedSecondTime; final bool isServerResetedSecondTime;
bool get isHetznerFilled => hetznerKey != null; bool get isProviderFilled => providerApiToken != null;
bool get isCloudFlareFilled => cloudFlareKey != null; bool get isCloudFlareFilled => cloudFlareKey != null;
bool get isBackblazeFilled => backblazeCredential != null; bool get isBackblazeFilled => backblazeCredential != null;
bool get isDomainFilled => serverDomain != null; bool get isDomainFilled => serverDomain != null;
@ -58,7 +58,7 @@ abstract class ServerInstallationState extends Equatable {
List<bool?> get _fulfilementList { List<bool?> get _fulfilementList {
final List<bool> res = [ final List<bool> res = [
isHetznerFilled, isProviderFilled,
isCloudFlareFilled, isCloudFlareFilled,
isBackblazeFilled, isBackblazeFilled,
isDomainFilled, isDomainFilled,
@ -80,7 +80,7 @@ class TimerState extends ServerInstallationNotFinished {
this.timerStart, this.timerStart,
this.duration, this.duration,
}) : super( }) : super(
hetznerKey: dataState.hetznerKey, providerApiToken: dataState.providerApiToken,
cloudFlareKey: dataState.cloudFlareKey, cloudFlareKey: dataState.cloudFlareKey,
backblazeCredential: dataState.backblazeCredential, backblazeCredential: dataState.backblazeCredential,
serverDomain: dataState.serverDomain, serverDomain: dataState.serverDomain,
@ -124,7 +124,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
required final super.isServerResetedSecondTime, required final super.isServerResetedSecondTime,
required final this.isLoading, required final this.isLoading,
required this.dnsMatches, required this.dnsMatches,
final super.hetznerKey, final super.providerApiToken,
final super.cloudFlareKey, final super.cloudFlareKey,
final super.backblazeCredential, final super.backblazeCredential,
final super.serverDomain, final super.serverDomain,
@ -136,7 +136,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -149,7 +149,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
]; ];
ServerInstallationNotFinished copyWith({ ServerInstallationNotFinished copyWith({
final String? hetznerKey, final String? providerApiToken,
final String? cloudFlareKey, final String? cloudFlareKey,
final BackblazeCredential? backblazeCredential, final BackblazeCredential? backblazeCredential,
final ServerDomain? serverDomain, final ServerDomain? serverDomain,
@ -162,7 +162,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
final Map<String, bool>? dnsMatches, final Map<String, bool>? dnsMatches,
}) => }) =>
ServerInstallationNotFinished( ServerInstallationNotFinished(
hetznerKey: hetznerKey ?? this.hetznerKey, providerApiToken: providerApiToken ?? this.providerApiToken,
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
backblazeCredential: backblazeCredential ?? this.backblazeCredential, backblazeCredential: backblazeCredential ?? this.backblazeCredential,
serverDomain: serverDomain ?? this.serverDomain, serverDomain: serverDomain ?? this.serverDomain,
@ -178,7 +178,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
); );
ServerInstallationFinished finish() => ServerInstallationFinished( ServerInstallationFinished finish() => ServerInstallationFinished(
hetznerKey: hetznerKey!, providerApiToken: providerApiToken!,
cloudFlareKey: cloudFlareKey!, cloudFlareKey: cloudFlareKey!,
backblazeCredential: backblazeCredential!, backblazeCredential: backblazeCredential!,
serverDomain: serverDomain!, serverDomain: serverDomain!,
@ -193,7 +193,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
class ServerInstallationEmpty extends ServerInstallationNotFinished { class ServerInstallationEmpty extends ServerInstallationNotFinished {
const ServerInstallationEmpty() const ServerInstallationEmpty()
: super( : super(
hetznerKey: null, providerApiToken: null,
cloudFlareKey: null, cloudFlareKey: null,
backblazeCredential: null, backblazeCredential: null,
serverDomain: null, serverDomain: null,
@ -209,7 +209,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished {
class ServerInstallationFinished extends ServerInstallationState { class ServerInstallationFinished extends ServerInstallationState {
const ServerInstallationFinished({ const ServerInstallationFinished({
required final String super.hetznerKey, required final String super.providerApiToken,
required final String super.cloudFlareKey, required final String super.cloudFlareKey,
required final BackblazeCredential super.backblazeCredential, required final BackblazeCredential super.backblazeCredential,
required final ServerDomain super.serverDomain, required final ServerDomain super.serverDomain,
@ -222,7 +222,7 @@ class ServerInstallationFinished extends ServerInstallationState {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -260,7 +260,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
const ServerInstallationRecovery({ const ServerInstallationRecovery({
required this.currentStep, required this.currentStep,
required this.recoveryCapabilities, required this.recoveryCapabilities,
final super.hetznerKey, final super.providerApiToken,
final super.cloudFlareKey, final super.cloudFlareKey,
final super.backblazeCredential, final super.backblazeCredential,
final super.serverDomain, final super.serverDomain,
@ -276,7 +276,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
@override @override
List<Object?> get props => [ List<Object?> get props => [
hetznerKey, providerApiToken,
cloudFlareKey, cloudFlareKey,
backblazeCredential, backblazeCredential,
serverDomain, serverDomain,
@ -288,7 +288,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
]; ];
ServerInstallationRecovery copyWith({ ServerInstallationRecovery copyWith({
final String? hetznerKey, final String? providerApiToken,
final String? cloudFlareKey, final String? cloudFlareKey,
final BackblazeCredential? backblazeCredential, final BackblazeCredential? backblazeCredential,
final ServerDomain? serverDomain, final ServerDomain? serverDomain,
@ -298,7 +298,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
final ServerRecoveryCapabilities? recoveryCapabilities, final ServerRecoveryCapabilities? recoveryCapabilities,
}) => }) =>
ServerInstallationRecovery( ServerInstallationRecovery(
hetznerKey: hetznerKey ?? this.hetznerKey, providerApiToken: providerApiToken ?? this.providerApiToken,
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
backblazeCredential: backblazeCredential ?? this.backblazeCredential, backblazeCredential: backblazeCredential ?? this.backblazeCredential,
serverDomain: serverDomain ?? this.serverDomain, serverDomain: serverDomain ?? this.serverDomain,
@ -309,7 +309,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
); );
ServerInstallationFinished finish() => ServerInstallationFinished( ServerInstallationFinished finish() => ServerInstallationFinished(
hetznerKey: hetznerKey!, providerApiToken: providerApiToken!,
cloudFlareKey: cloudFlareKey!, cloudFlareKey: cloudFlareKey!,
backblazeCredential: backblazeCredential!, backblazeCredential: backblazeCredential!,
serverDomain: serverDomain!, serverDomain: serverDomain!,

View File

@ -1,4 +1,4 @@
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.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';

View File

@ -3,7 +3,7 @@ import 'package:selfprivacy/config/hive_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/models/hive/user.dart'; import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart';
export 'package:provider/provider.dart'; export 'package:provider/provider.dart';

View File

@ -1,6 +1,6 @@
import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/api_maps/rest_maps/providers/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/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart';
@ -12,7 +12,10 @@ class ApiVolumesCubit
ApiVolumesCubit(final ServerInstallationCubit serverInstallationCubit) ApiVolumesCubit(final ServerInstallationCubit serverInstallationCubit)
: super(serverInstallationCubit, const ApiVolumesState.initial()); : super(serverInstallationCubit, const ApiVolumesState.initial());
final ServerApi api = ServerApi(); final VolumeProviderApiFactory providerApi =
VolumeApiFactoryCreator.createVolumeProviderApiFactory(
getIt<ApiConfigModel>().serverDetails!.provider,
);
@override @override
void load() async { void load() async {
@ -27,7 +30,8 @@ class ApiVolumesCubit
} }
void _refetch() async { void _refetch() async {
final List<ServerVolume> volumes = await HetznerApi().getVolumes(); final List<ServerVolume> volumes =
await providerApi.getVolumeProvider().getVolumes();
if (volumes.isNotEmpty) { if (volumes.isNotEmpty) {
emit(ApiVolumesState(volumes, LoadingStatus.success)); emit(ApiVolumesState(volumes, LoadingStatus.success));
} else { } else {
@ -37,29 +41,29 @@ class ApiVolumesCubit
void attachVolume(final ServerVolume volume) async { void attachVolume(final ServerVolume volume) async {
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!; final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
HetznerApi().attachVolume(volume.id, server.id); await providerApi.getVolumeProvider().attachVolume(volume.id, server.id);
refresh(); refresh();
} }
void detachVolume(final ServerVolume volume) async { void detachVolume(final ServerVolume volume) async {
HetznerApi().detachVolume(volume.id); await providerApi.getVolumeProvider().detachVolume(volume.id);
refresh(); refresh();
} }
void resizeVolume(final ServerVolume volume, final int newSizeGb) { void resizeVolume(final ServerVolume volume, final int newSizeGb) async {
if (volume.sizeByte < newSizeGb) { //if (volume.sizeByte < newSizeGb) {
HetznerApi().resizeVolume(volume.id, newSizeGb); await providerApi.getVolumeProvider().resizeVolume(volume.id, newSizeGb);
refresh(); refresh();
} //}
} }
void createVolume() async { void createVolume() async {
HetznerApi().createVolume(); await providerApi.getVolumeProvider().createVolume();
refresh(); refresh();
} }
void deleteVolume(final ServerVolume volume) async { void deleteVolume(final ServerVolume volume) async {
HetznerApi().deleteVolume(volume.id); await providerApi.getVolumeProvider().deleteVolume(volume.id);
refresh(); refresh();
} }

View File

@ -73,17 +73,23 @@ class ServerVolumeAdapter extends TypeAdapter<ServerVolume> {
return ServerVolume( return ServerVolume(
id: fields[1] as int, id: fields[1] as int,
name: fields[2] as String, name: fields[2] as String,
sizeByte: fields[3] == null ? 10737418240 : fields[3] as int,
serverId: fields[4] as int?,
); );
} }
@override @override
void write(BinaryWriter writer, ServerVolume obj) { void write(BinaryWriter writer, ServerVolume obj) {
writer writer
..writeByte(2) ..writeByte(4)
..writeByte(1) ..writeByte(1)
..write(obj.id) ..write(obj.id)
..writeByte(2) ..writeByte(2)
..write(obj.name); ..write(obj.name)
..writeByte(3)
..write(obj.sizeByte)
..writeByte(4)
..write(obj.serverId);
} }
@override @override

View File

@ -11,9 +11,9 @@ class UserAdapter extends TypeAdapter<User> {
final int typeId = 1; final int typeId = 1;
@override @override
User read(final BinaryReader reader) { User read(BinaryReader reader) {
final int numOfFields = reader.readByte(); final numOfFields = reader.readByte();
final Map<int, dynamic> fields = <int, dynamic>{ final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
}; };
return User( return User(
@ -26,7 +26,7 @@ class UserAdapter extends TypeAdapter<User> {
} }
@override @override
void write(final BinaryWriter writer, final User obj) { void write(BinaryWriter writer, User obj) {
writer writer
..writeByte(5) ..writeByte(5)
..writeByte(0) ..writeByte(0)
@ -45,7 +45,7 @@ class UserAdapter extends TypeAdapter<User> {
int get hashCode => typeId.hashCode; int get hashCode => typeId.hashCode;
@override @override
bool operator ==(final Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
other is UserAdapter && other is UserAdapter &&
runtimeType == other.runtimeType && runtimeType == other.runtimeType &&

View File

@ -1,115 +0,0 @@
import 'package:json_annotation/json_annotation.dart';
part 'hetzner_server_info.g.dart';
@JsonSerializable()
class HetznerServerInfo {
HetznerServerInfo(
this.id,
this.name,
this.status,
this.created,
this.serverType,
this.location,
this.publicNet,
this.volumes,
);
final int id;
final String name;
final ServerStatus status;
final DateTime created;
final List<int> volumes;
@JsonKey(name: 'server_type')
final HetznerServerTypeInfo serverType;
@JsonKey(name: 'datacenter', fromJson: HetznerServerInfo.locationFromJson)
final HetznerLocation location;
@JsonKey(name: 'public_net')
final HetznerPublicNetInfo publicNet;
static HetznerLocation locationFromJson(final Map json) =>
HetznerLocation.fromJson(json['location']);
static HetznerServerInfo fromJson(final Map<String, dynamic> json) =>
_$HetznerServerInfoFromJson(json);
}
@JsonSerializable()
class HetznerPublicNetInfo {
HetznerPublicNetInfo(this.ipv4);
final HetznerIp4 ipv4;
static HetznerPublicNetInfo fromJson(final Map<String, dynamic> json) =>
_$HetznerPublicNetInfoFromJson(json);
}
@JsonSerializable()
class HetznerIp4 {
HetznerIp4(this.id, this.ip, this.blocked, this.reverseDns);
final bool blocked;
@JsonKey(name: 'dns_ptr')
final String reverseDns;
final int id;
final String ip;
static HetznerIp4 fromJson(final Map<String, dynamic> json) =>
_$HetznerIp4FromJson(json);
}
enum ServerStatus {
running,
initializing,
starting,
stopping,
off,
deleting,
migrating,
rebuilding,
unknown,
}
@JsonSerializable()
class HetznerServerTypeInfo {
HetznerServerTypeInfo(this.cores, this.memory, this.disk, this.prices);
final int cores;
final num memory;
final int disk;
final List<HetznerPriceInfo> prices;
static HetznerServerTypeInfo fromJson(final Map<String, dynamic> json) =>
_$HetznerServerTypeInfoFromJson(json);
}
@JsonSerializable()
class HetznerPriceInfo {
HetznerPriceInfo(this.hourly, this.monthly);
@JsonKey(name: 'price_hourly', fromJson: HetznerPriceInfo.getPrice)
final double hourly;
@JsonKey(name: 'price_monthly', fromJson: HetznerPriceInfo.getPrice)
final double monthly;
static HetznerPriceInfo fromJson(final Map<String, dynamic> json) =>
_$HetznerPriceInfoFromJson(json);
static double getPrice(final Map json) =>
double.parse(json['gross'] as String);
}
@JsonSerializable()
class HetznerLocation {
HetznerLocation(this.country, this.city, this.description, this.zone);
final String country;
final String city;
final String description;
@JsonKey(name: 'network_zone')
final String zone;
static HetznerLocation fromJson(final Map<String, dynamic> json) =>
_$HetznerLocationFromJson(json);
}

View File

@ -0,0 +1,115 @@
import 'package:json_annotation/json_annotation.dart';
part 'provider_server_info.g.dart';
@JsonSerializable()
class ProviderServerInfo {
ProviderServerInfo(
this.id,
this.name,
this.status,
this.created,
this.serverType,
this.location,
this.publicNet,
this.volumes,
);
final int id;
final String name;
final ServerStatus status;
final DateTime created;
final List<int> volumes;
@JsonKey(name: 'server_type')
final ProviderServerTypeInfo serverType;
@JsonKey(name: 'datacenter', fromJson: ProviderServerInfo.locationFromJson)
final ProviderLocation location;
@JsonKey(name: 'public_net')
final ProviderPublicNetInfo publicNet;
static ProviderLocation locationFromJson(final Map json) =>
ProviderLocation.fromJson(json['location']);
static ProviderServerInfo fromJson(final Map<String, dynamic> json) =>
_$ProviderServerInfoFromJson(json);
}
@JsonSerializable()
class ProviderPublicNetInfo {
ProviderPublicNetInfo(this.ipv4);
final ProviderIp4 ipv4;
static ProviderPublicNetInfo fromJson(final Map<String, dynamic> json) =>
_$ProviderPublicNetInfoFromJson(json);
}
@JsonSerializable()
class ProviderIp4 {
ProviderIp4(this.id, this.ip, this.blocked, this.reverseDns);
final bool blocked;
@JsonKey(name: 'dns_ptr')
final String reverseDns;
final int id;
final String ip;
static ProviderIp4 fromJson(final Map<String, dynamic> json) =>
_$ProviderIp4FromJson(json);
}
enum ServerStatus {
running,
initializing,
starting,
stopping,
off,
deleting,
migrating,
rebuilding,
unknown,
}
@JsonSerializable()
class ProviderServerTypeInfo {
ProviderServerTypeInfo(this.cores, this.memory, this.disk, this.prices);
final int cores;
final num memory;
final int disk;
final List<ProviderPriceInfo> prices;
static ProviderServerTypeInfo fromJson(final Map<String, dynamic> json) =>
_$ProviderServerTypeInfoFromJson(json);
}
@JsonSerializable()
class ProviderPriceInfo {
ProviderPriceInfo(this.hourly, this.monthly);
@JsonKey(name: 'price_hourly', fromJson: ProviderPriceInfo.getPrice)
final double hourly;
@JsonKey(name: 'price_monthly', fromJson: ProviderPriceInfo.getPrice)
final double monthly;
static ProviderPriceInfo fromJson(final Map<String, dynamic> json) =>
_$ProviderPriceInfoFromJson(json);
static double getPrice(final Map json) =>
double.parse(json['gross'] as String);
}
@JsonSerializable()
class ProviderLocation {
ProviderLocation(this.country, this.city, this.description, this.zone);
final String country;
final String city;
final String description;
@JsonKey(name: 'network_zone')
final String zone;
static ProviderLocation fromJson(final Map<String, dynamic> json) =>
_$ProviderLocationFromJson(json);
}

View File

@ -1,21 +1,22 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'hetzner_server_info.dart'; part of 'provider_server_info.dart';
// ************************************************************************** // **************************************************************************
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
HetznerServerInfo _$HetznerServerInfoFromJson(Map<String, dynamic> json) => ProviderServerInfo _$ProviderServerInfoFromJson(Map<String, dynamic> json) =>
HetznerServerInfo( ProviderServerInfo(
json['id'] as int, json['id'] as int,
json['name'] as String, json['name'] as String,
$enumDecode(_$ServerStatusEnumMap, json['status']), $enumDecode(_$ServerStatusEnumMap, json['status']),
DateTime.parse(json['created'] as String), DateTime.parse(json['created'] as String),
HetznerServerTypeInfo.fromJson( ProviderServerTypeInfo.fromJson(
json['server_type'] as Map<String, dynamic>), json['server_type'] as Map<String, dynamic>),
HetznerServerInfo.locationFromJson(json['datacenter'] as Map), ProviderServerInfo.locationFromJson(json['datacenter'] as Map),
HetznerPublicNetInfo.fromJson(json['public_net'] as Map<String, dynamic>), ProviderPublicNetInfo.fromJson(
json['public_net'] as Map<String, dynamic>),
(json['volumes'] as List<dynamic>).map((e) => e as int).toList(), (json['volumes'] as List<dynamic>).map((e) => e as int).toList(),
); );
@ -31,38 +32,38 @@ const _$ServerStatusEnumMap = {
ServerStatus.unknown: 'unknown', ServerStatus.unknown: 'unknown',
}; };
HetznerPublicNetInfo _$HetznerPublicNetInfoFromJson( ProviderPublicNetInfo _$ProviderPublicNetInfoFromJson(
Map<String, dynamic> json) => Map<String, dynamic> json) =>
HetznerPublicNetInfo( ProviderPublicNetInfo(
HetznerIp4.fromJson(json['ipv4'] as Map<String, dynamic>), ProviderIp4.fromJson(json['ipv4'] as Map<String, dynamic>),
); );
HetznerIp4 _$HetznerIp4FromJson(Map<String, dynamic> json) => HetznerIp4( ProviderIp4 _$ProviderIp4FromJson(Map<String, dynamic> json) => ProviderIp4(
json['id'] as int, json['id'] as int,
json['ip'] as String, json['ip'] as String,
json['blocked'] as bool, json['blocked'] as bool,
json['dns_ptr'] as String, json['dns_ptr'] as String,
); );
HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson( ProviderServerTypeInfo _$ProviderServerTypeInfoFromJson(
Map<String, dynamic> json) => Map<String, dynamic> json) =>
HetznerServerTypeInfo( ProviderServerTypeInfo(
json['cores'] as int, json['cores'] as int,
json['memory'] as num, json['memory'] as num,
json['disk'] as int, json['disk'] as int,
(json['prices'] as List<dynamic>) (json['prices'] as List<dynamic>)
.map((e) => HetznerPriceInfo.fromJson(e as Map<String, dynamic>)) .map((e) => ProviderPriceInfo.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
); );
HetznerPriceInfo _$HetznerPriceInfoFromJson(Map<String, dynamic> json) => ProviderPriceInfo _$ProviderPriceInfoFromJson(Map<String, dynamic> json) =>
HetznerPriceInfo( ProviderPriceInfo(
HetznerPriceInfo.getPrice(json['price_hourly'] as Map), ProviderPriceInfo.getPrice(json['price_hourly'] as Map),
HetznerPriceInfo.getPrice(json['price_monthly'] as Map), ProviderPriceInfo.getPrice(json['price_monthly'] as Map),
); );
HetznerLocation _$HetznerLocationFromJson(Map<String, dynamic> json) => ProviderLocation _$ProviderLocationFromJson(Map<String, dynamic> json) =>
HetznerLocation( ProviderLocation(
json['country'] as String, json['country'] as String,
json['city'] as String, json['city'] as String,
json['description'] as String, json['description'] as String,

View File

@ -21,7 +21,9 @@ 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';
class InitializingPage extends StatelessWidget { class InitializingPage extends StatelessWidget {
const InitializingPage({final super.key}); const InitializingPage({
final super.key,
});
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) {
@ -135,10 +137,12 @@ class InitializingPage extends StatelessWidget {
Widget _stepHetzner(final ServerInstallationCubit serverInstallationCubit) => Widget _stepHetzner(final ServerInstallationCubit serverInstallationCubit) =>
BlocProvider( BlocProvider(
create: (final context) => HetznerFormCubit(serverInstallationCubit), create: (final context) => ProviderFormCubit(
serverInstallationCubit,
),
child: Builder( child: Builder(
builder: (final context) { builder: (final context) {
final formCubitState = context.watch<HetznerFormCubit>().state; final formCubitState = context.watch<ProviderFormCubit>().state;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -152,7 +156,7 @@ class InitializingPage extends StatelessWidget {
BrandText.body2('initializing.2'.tr()), BrandText.body2('initializing.2'.tr()),
const Spacer(), const Spacer(),
CubitFormTextField( CubitFormTextField(
formFieldCubit: context.read<HetznerFormCubit>().apiKey, formFieldCubit: context.read<ProviderFormCubit>().apiKey,
textAlign: TextAlign.center, textAlign: TextAlign.center,
scrollPadding: const EdgeInsets.only(bottom: 70), scrollPadding: const EdgeInsets.only(bottom: 70),
decoration: const InputDecoration( decoration: const InputDecoration(
@ -163,7 +167,7 @@ class InitializingPage extends StatelessWidget {
BrandButton.rised( BrandButton.rised(
onPressed: formCubitState.isSubmitting onPressed: formCubitState.isSubmitting
? null ? null
: () => context.read<HetznerFormCubit>().trySubmit(), : () => context.read<ProviderFormCubit>().trySubmit(),
text: 'basis.connect'.tr(), text: 'basis.connect'.tr(),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),

View File

@ -19,11 +19,11 @@ class RecoveryHetznerConnected extends StatelessWidget {
context.watch<ServerInstallationCubit>(); context.watch<ServerInstallationCubit>();
return BlocProvider( return BlocProvider(
create: (final BuildContext context) => HetznerFormCubit(appConfig), create: (final BuildContext context) => ProviderFormCubit(appConfig),
child: Builder( child: Builder(
builder: (final BuildContext context) { builder: (final BuildContext context) {
final FormCubitState formCubitState = final FormCubitState formCubitState =
context.watch<HetznerFormCubit>().state; context.watch<ProviderFormCubit>().state;
return BrandHeroScreen( return BrandHeroScreen(
heroTitle: 'recovering.hetzner_connected'.tr(), heroTitle: 'recovering.hetzner_connected'.tr(),
@ -37,7 +37,7 @@ class RecoveryHetznerConnected extends StatelessWidget {
}, },
children: [ children: [
CubitFormTextField( CubitFormTextField(
formFieldCubit: context.read<HetznerFormCubit>().apiKey, formFieldCubit: context.read<ProviderFormCubit>().apiKey,
decoration: InputDecoration( decoration: InputDecoration(
border: const OutlineInputBorder(), border: const OutlineInputBorder(),
labelText: 'recovering.hetzner_connected_placeholder'.tr(), labelText: 'recovering.hetzner_connected_placeholder'.tr(),
@ -48,7 +48,7 @@ class RecoveryHetznerConnected extends StatelessWidget {
title: 'more.continue'.tr(), title: 'more.continue'.tr(),
onPressed: formCubitState.isSubmitting onPressed: formCubitState.isSubmitting
? null ? null
: () => context.read<HetznerFormCubit>().trySubmit(), : () => context.read<ProviderFormCubit>().trySubmit(),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
BrandButton.text( BrandButton.text(

7
lib/utils/scalars.dart Normal file
View File

@ -0,0 +1,7 @@
String dateTimeToJson(DateTime data) {
return data.toIso8601String();
}
DateTime dateTimeFromJson(dynamic data) {
return DateTime.parse(data as String);
}

View File

@ -169,6 +169,48 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.16.0" version: "1.16.0"
connectivity_plus:
dependency: transitive
description:
name: connectivity_plus
url: "https://pub.dartlang.org"
source: hosted
version: "2.3.5"
connectivity_plus_linux:
dependency: transitive
description:
name: connectivity_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.1"
connectivity_plus_macos:
dependency: transitive
description:
name: connectivity_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.4"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.1"
connectivity_plus_web:
dependency: transitive
description:
name: connectivity_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.2"
connectivity_plus_windows:
dependency: transitive
description:
name: connectivity_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.2"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -218,6 +260,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.3" version: "2.2.3"
dbus:
dependency: transitive
description:
name: dbus
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.3"
device_info_plus: device_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@ -356,6 +405,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "8.0.1" version: "8.0.1"
flutter_hooks:
dependency: transitive
description:
name: flutter_hooks
url: "https://pub.dartlang.org"
source: hosted
version: "0.18.5+1"
flutter_launcher_icons: flutter_launcher_icons:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -462,6 +518,90 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.2"
gql:
dependency: "direct main"
description:
name: gql
url: "https://pub.dartlang.org"
source: hosted
version: "0.13.1"
gql_code_builder:
dependency: transitive
description:
name: gql_code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.1"
gql_dedupe_link:
dependency: transitive
description:
name: gql_dedupe_link
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
gql_error_link:
dependency: transitive
description:
name: gql_error_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.2"
gql_exec:
dependency: transitive
description:
name: gql_exec
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
gql_http_link:
dependency: transitive
description:
name: gql_http_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
gql_link:
dependency: transitive
description:
name: gql_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.2"
gql_transform_link:
dependency: transitive
description:
name: gql_transform_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.2"
graphql:
dependency: "direct main"
description:
name: graphql
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.1"
graphql_codegen:
dependency: "direct main"
description:
name: graphql_codegen
url: "https://pub.dartlang.org"
source: hosted
version: "0.9.0"
graphql_codegen_config:
dependency: transitive
description:
name: graphql_codegen_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.6"
graphql_flutter:
dependency: "direct main"
description:
name: graphql_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.0"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -672,6 +812,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
node_preamble: node_preamble:
dependency: transitive dependency: transitive
description: description:
@ -679,6 +826,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
normalize:
dependency: transitive
description:
name: normalize
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.0+1"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
@ -826,6 +980,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.0" version: "3.1.0"
recase:
dependency: transitive
description:
name: recase
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
rxdart:
dependency: transitive
description:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.27.4"
share_plus: share_plus:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1153,6 +1321,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.1" version: "3.0.1"
uuid:
dependency: transitive
description:
name: uuid
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.6"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:

View File

@ -26,6 +26,10 @@ dependencies:
flutter_markdown: ^0.6.9 flutter_markdown: ^0.6.9
flutter_secure_storage: ^5.0.2 flutter_secure_storage: ^5.0.2
get_it: ^7.2.0 get_it: ^7.2.0
gql: ^0.13.1
graphql: ^5.1.1
graphql_codegen: ^0.9.0
graphql_flutter: ^5.1.0
gtk_theme_fl: ^0.0.1 gtk_theme_fl: ^0.0.1
hive: ^2.0.5 hive: ^2.0.5
hive_flutter: ^1.1.0 hive_flutter: ^1.1.0

View File

@ -6,11 +6,14 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h> #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <system_theme/system_theme_plugin.h> #include <system_theme/system_theme_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar( FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
SystemThemePluginRegisterWithRegistrar( SystemThemePluginRegisterWithRegistrar(

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus_windows
flutter_secure_storage_windows flutter_secure_storage_windows
system_theme system_theme
url_launcher_windows url_launcher_windows