diff --git a/build.yaml b/build.yaml index 709b623e..15417c7c 100644 --- a/build.yaml +++ b/build.yaml @@ -1,7 +1,18 @@ targets: $default: builders: + graphql_codegen: + options: + scalars: + DateTime: + type: DateTime + fromJsonFunctionName: dateTimeFromJson + toJsonFunctionName: dateTimeToJson + import: package:selfprivacy/utils/scalars.dart + clients: + - graphql + - graphql_flutter json_serializable: options: create_factory: true - create_to_json: false \ No newline at end of file + create_to_json: false diff --git a/lib/logic/api_maps/graphql_maps/schema/get_api_tokens.graphql b/lib/logic/api_maps/graphql_maps/schema/get_api_tokens.graphql new file mode 100644 index 00000000..df9569c0 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/get_api_tokens.graphql @@ -0,0 +1,9 @@ +query GetApiTokensQuery { + api { + devices { + creationDate + isCaller + name + } + } +} \ No newline at end of file diff --git a/lib/logic/api_maps/graphql_maps/schema/get_api_tokens.graphql.g.dart b/lib/logic/api_maps/graphql_maps/schema/get_api_tokens.graphql.g.dart new file mode 100644 index 00000000..508d8010 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/get_api_tokens.graphql.g.dart @@ -0,0 +1,34 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'get_api_tokens.graphql.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Query$GetApiTokensQuery _$Query$GetApiTokensQueryFromJson( + Map json) => + Query$GetApiTokensQuery( + api: Query$GetApiTokensQuery$api.fromJson( + json['api'] as Map), + $__typename: json['__typename'] as String, + ); + +Query$GetApiTokensQuery$api _$Query$GetApiTokensQuery$apiFromJson( + Map json) => + Query$GetApiTokensQuery$api( + devices: (json['devices'] as List) + .map((e) => Query$GetApiTokensQuery$api$devices.fromJson( + e as Map)) + .toList(), + $__typename: json['__typename'] as String, + ); + +Query$GetApiTokensQuery$api$devices + _$Query$GetApiTokensQuery$api$devicesFromJson(Map json) => + Query$GetApiTokensQuery$api$devices( + creationDate: dateTimeFromJson(json['creationDate']), + isCaller: json['isCaller'] as bool, + name: json['name'] as String, + $__typename: json['__typename'] as String, + ); diff --git a/lib/logic/api_maps/graphql_maps/schema/get_api_version.graphql b/lib/logic/api_maps/graphql_maps/schema/get_api_version.graphql new file mode 100644 index 00000000..cbfeb870 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/get_api_version.graphql @@ -0,0 +1,5 @@ +query GetApiVersionQuery { + api { + version + } +} \ No newline at end of file diff --git a/lib/logic/api_maps/graphql_maps/schema/get_api_version.graphql.g.dart b/lib/logic/api_maps/graphql_maps/schema/get_api_version.graphql.g.dart new file mode 100644 index 00000000..c0b5a378 --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/get_api_version.graphql.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'get_api_version.graphql.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Query$GetApiVersionQuery _$Query$GetApiVersionQueryFromJson( + Map json) => + Query$GetApiVersionQuery( + api: Query$GetApiVersionQuery$api.fromJson( + json['api'] as Map), + $__typename: json['__typename'] as String, + ); + +Query$GetApiVersionQuery$api _$Query$GetApiVersionQuery$apiFromJson( + Map json) => + Query$GetApiVersionQuery$api( + version: json['version'] as String, + $__typename: json['__typename'] as String, + ); diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql b/lib/logic/api_maps/graphql_maps/schema/schema.graphql new file mode 100644 index 00000000..c4b3246d --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql @@ -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! +} diff --git a/lib/logic/api_maps/graphql_maps/schema/schema.graphql.g.dart b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.g.dart new file mode 100644 index 00000000..84a4c38f --- /dev/null +++ b/lib/logic/api_maps/graphql_maps/schema/schema.graphql.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'schema.graphql.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Input$RecoveryKeyLimitsInput _$Input$RecoveryKeyLimitsInputFromJson( + Map json) => + Input$RecoveryKeyLimitsInput( + expirationDate: _nullable$dateTimeFromJson(json['expirationDate']), + uses: json['uses'] as int?, + ); + +Input$UseNewDeviceKeyInput _$Input$UseNewDeviceKeyInputFromJson( + Map json) => + Input$UseNewDeviceKeyInput( + key: json['key'] as String, + deviceName: json['deviceName'] as String, + ); + +Input$UseRecoveryKeyInput _$Input$UseRecoveryKeyInputFromJson( + Map json) => + Input$UseRecoveryKeyInput( + key: json['key'] as String, + deviceName: json['deviceName'] as String, + ); diff --git a/lib/logic/api_maps/rest_maps/api_factory_creator.dart b/lib/logic/api_maps/rest_maps/api_factory_creator.dart new file mode 100644 index 00000000..0343f402 --- /dev/null +++ b/lib/logic/api_maps/rest_maps/api_factory_creator.dart @@ -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 . . . +} diff --git a/lib/logic/api_maps/api_map.dart b/lib/logic/api_maps/rest_maps/api_map.dart similarity index 100% rename from lib/logic/api_maps/api_map.dart rename to lib/logic/api_maps/rest_maps/api_map.dart diff --git a/lib/logic/api_maps/backblaze.dart b/lib/logic/api_maps/rest_maps/backblaze.dart similarity index 98% rename from lib/logic/api_maps/backblaze.dart rename to lib/logic/api_maps/rest_maps/backblaze.dart index 8d827e78..5140311d 100644 --- a/lib/logic/api_maps/backblaze.dart +++ b/lib/logic/api_maps/rest_maps/backblaze.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/api_map.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart'; class BackblazeApiAuth { diff --git a/lib/logic/api_maps/cloudflare.dart b/lib/logic/api_maps/rest_maps/cloudflare.dart similarity index 99% rename from lib/logic/api_maps/cloudflare.dart rename to lib/logic/api_maps/rest_maps/cloudflare.dart index 9141d5fe..2b78c6fe 100644 --- a/lib/logic/api_maps/cloudflare.dart +++ b/lib/logic/api_maps/rest_maps/cloudflare.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/api_map.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/json/dns_records.dart'; diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart similarity index 78% rename from lib/logic/api_maps/hetzner.dart rename to lib/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart index 3345633f..57582fee 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/rest_maps/providers/hetzner/hetzner.dart @@ -3,18 +3,15 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/api_map.dart'; -import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/providers/volume_provider.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/user.dart'; import 'package:selfprivacy/utils/password_generator.dart'; -class HetznerApi extends ApiMap { - HetznerApi({this.hasLogger = false, this.isWithToken = true}); - @override - bool hasLogger; - @override - bool isWithToken; +class HetznerApi extends VolumeProviderApi { + HetznerApi({final super.hasLogger = false, final super.isWithToken = true}); @override BaseOptions get options { @@ -35,27 +32,43 @@ class HetznerApi extends ApiMap { @override String rootAddress = 'https://api.hetzner.cloud/v1'; - Future isValid(final String token) async { - validateStatus = (final int? status) => - status == HttpStatus.ok || status == HttpStatus.unauthorized; + @override + Future isApiTokenValid(final String token) async { + bool isValid = false; + Response? response; final Dio client = await getClient(); - final Response response = await client.get( - '/servers', - options: Options( - headers: {'Authorization': 'Bearer $token'}, - ), - ); - close(client); - - if (response.statusCode == HttpStatus.ok) { - return true; - } else if (response.statusCode == HttpStatus.unauthorized) { - return false; - } else { - throw Exception('code: ${response.statusCode}'); + try { + response = await client.get( + '/servers', + 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; } + @override + RegExp getApiTokenValidation() => + RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); + + @override Future createVolume() async { ServerVolume? volume; @@ -92,6 +105,7 @@ class HetznerApi extends ApiMap { return volume; } + @override Future> getVolumes({final String? status}) async { final List volumes = []; @@ -127,6 +141,7 @@ class HetznerApi extends ApiMap { return volumes; } + @override Future getVolume(final int id) async { ServerVolume? volume; @@ -153,7 +168,8 @@ class HetznerApi extends ApiMap { return volume; } - void deleteVolume(final int id) async { + @override + Future deleteVolume(final int id) async { final Dio client = await getClient(); try { await client.delete('/volumes/$id'); @@ -164,6 +180,7 @@ class HetznerApi extends ApiMap { } } + @override Future attachVolume(final int volumeId, final int serverId) async { bool success = false; @@ -187,6 +204,7 @@ class HetznerApi extends ApiMap { return success; } + @override Future detachVolume(final int volumeId) async { bool success = false; @@ -204,6 +222,7 @@ class HetznerApi extends ApiMap { return success; } + @override Future resizeVolume(final int volumeId, final int sizeGb) async { bool success = false; @@ -226,8 +245,35 @@ class HetznerApi extends ApiMap { return success; } + @override Future 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 createServerByVolume({ + required final String dnsApiToken, required final User rootUser, required final String domainName, required final ServerVolume dataBase, @@ -250,7 +296,7 @@ class HetznerApi extends ApiMap { /// check the branch name, it could be "development" or "master". /// 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); final Map data = { @@ -285,6 +331,7 @@ class HetznerApi extends ApiMap { ); } on DioError catch (e) { print(e); + deleteVolume(dataBase.id); rethrow; } catch (e) { print(e); @@ -312,7 +359,8 @@ class HetznerApi extends ApiMap { return hostname; } - Future deleteSelfprivacyServerAndAllVolumes({ + @override + Future deleteServer({ required final String domainName, }) async { final Dio client = await getClient(); @@ -339,22 +387,34 @@ class HetznerApi extends ApiMap { close(client); } - Future reset() async { + @override + Future restart() async { final ServerHostingDetails server = getIt().serverDetails!; final Dio client = await getClient(); - await client.post('/servers/${server.id}/actions/reset'); - close(client); + try { + await client.post('/servers/${server.id}/actions/reset'); + } catch (e) { + print(e); + } finally { + close(client); + } return server.copyWith(startTime: DateTime.now()); } + @override Future powerOn() async { final ServerHostingDetails server = getIt().serverDetails!; final Dio client = await getClient(); - await client.post('/servers/${server.id}/actions/poweron'); - close(client); + try { + await client.post('/servers/${server.id}/actions/poweron'); + } catch (e) { + print(e); + } finally { + close(client); + } return server.copyWith(startTime: DateTime.now()); } @@ -381,41 +441,40 @@ class HetznerApi extends ApiMap { return res.data; } - Future getInfo() async { + Future getInfo() async { final ServerHostingDetails? hetznerServer = getIt().serverDetails; final Dio client = await getClient(); final Response response = await client.get('/servers/${hetznerServer!.id}'); close(client); - return HetznerServerInfo.fromJson(response.data!['server']); + return ProviderServerInfo.fromJson(response.data!['server']); } - Future> getServers() async { + @override + Future> getServers() async { final Dio client = await getClient(); final Response response = await client.get('/servers'); close(client); return (response.data!['servers'] as List) // ignore: unnecessary_lambdas - .map((final e) => HetznerServerInfo.fromJson(e)) + .map((final e) => ProviderServerInfo.fromJson(e)) .toList(); } + @override Future createReverseDns({ - required final String ip4, - required final String domainName, + required final ServerHostingDetails serverDetails, + required final ServerDomain domain, }) async { - final ServerHostingDetails? hetznerServer = - getIt().serverDetails; - final Dio client = await getClient(); try { await client.post( - '/servers/${hetznerServer!.id}/actions/change_dns_ptr', + '/servers/${serverDetails.id}/actions/change_dns_ptr', data: { - 'ip': ip4, - 'dns_ptr': domainName, + 'ip': serverDetails.ip4, + 'dns_ptr': domain.domainName, }, ); } catch (e) { diff --git a/lib/logic/api_maps/rest_maps/providers/hetzner/hetzner_factory.dart b/lib/logic/api_maps/rest_maps/providers/hetzner/hetzner_factory.dart new file mode 100644 index 00000000..e047da13 --- /dev/null +++ b/lib/logic/api_maps/rest_maps/providers/hetzner/hetzner_factory.dart @@ -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, + ); +} diff --git a/lib/logic/api_maps/rest_maps/providers/provider.dart b/lib/logic/api_maps/rest_maps/providers/provider.dart new file mode 100644 index 00000000..c431379e --- /dev/null +++ b/lib/logic/api_maps/rest_maps/providers/provider.dart @@ -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> getServers(); + + Future restart(); + Future powerOn(); + + Future deleteServer({required final String domainName}); + Future createServer({ + required final String dnsApiToken, + required final User rootUser, + required final String domainName, + }); + Future createReverseDns({ + required final ServerHostingDetails serverDetails, + required final ServerDomain domain, + }); + + Future isApiTokenValid(final String token); + RegExp getApiTokenValidation(); +} diff --git a/lib/logic/api_maps/rest_maps/providers/provider_factory.dart b/lib/logic/api_maps/rest_maps/providers/provider_factory.dart new file mode 100644 index 00000000..a899b28c --- /dev/null +++ b/lib/logic/api_maps/rest_maps/providers/provider_factory.dart @@ -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(), + }); +} diff --git a/lib/logic/api_maps/rest_maps/providers/volume_provider.dart b/lib/logic/api_maps/rest_maps/providers/volume_provider.dart new file mode 100644 index 00000000..cf598f18 --- /dev/null +++ b/lib/logic/api_maps/rest_maps/providers/volume_provider.dart @@ -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 createVolume(); + Future> getVolumes({final String? status}); + Future getVolume(final int id); + Future attachVolume(final int volumeId, final int serverId); + Future detachVolume(final int volumeId); + Future resizeVolume(final int volumeId, final int sizeGb); + Future deleteVolume(final int id); +} diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/rest_maps/server.dart similarity index 99% rename from lib/logic/api_maps/server.dart rename to lib/logic/api_maps/rest_maps/server.dart index 67a0739c..3cb2ed73 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/rest_maps/server.dart @@ -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/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 { ApiResponse({ diff --git a/lib/logic/cubit/backups/backups_cubit.dart b/lib/logic/cubit/backups/backups_cubit.dart index 63cdfb3e..bb89bb49 100644 --- a/lib/logic/cubit/backups/backups_cubit.dart +++ b/lib/logic/cubit/backups/backups_cubit.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/backblaze.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/backblaze.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/models/hive/backblaze_bucket.dart'; import 'package:selfprivacy/logic/models/json/backup.dart'; diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index a7e80e17..6aa71400 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -1,5 +1,5 @@ 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/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/json/api_token.dart'; diff --git a/lib/logic/cubit/dns_records/dns_records_cubit.dart b/lib/logic/cubit/dns_records/dns_records_cubit.dart index 0590b065..8d8ae496 100644 --- a/lib/logic/cubit/dns_records/dns_records_cubit.dart +++ b/lib/logic/cubit/dns_records/dns_records_cubit.dart @@ -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/json/dns_records.dart'; -import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/server.dart'; part 'dns_records_state.dart'; diff --git a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart index 04d2993e..2a9f4662 100644 --- a/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart @@ -1,6 +1,6 @@ import 'dart:async'; 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/models/hive/backblaze_credential.dart'; import 'package:easy_localization/easy_localization.dart'; diff --git a/lib/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart index 6a4b3b30..20c99f38 100644 --- a/lib/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/cloudflare_form_cubit.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.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/forms/validations/validations.dart'; diff --git a/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart b/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart index 89b50a62..b38e8313 100644 --- a/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart +++ b/lib/logic/cubit/forms/setup/initializing/domain_cloudflare.dart @@ -1,5 +1,5 @@ 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/models/hive/server_domain.dart'; diff --git a/lib/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart b/lib/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart index 11465d9d..24910ad1 100644 --- a/lib/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/initializing/hetzner_form_cubit.dart @@ -2,13 +2,13 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart'; -class HetznerFormCubit extends FormCubit { - HetznerFormCubit(this.serverInstallationCubit) { - final RegExp regExp = RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]'); +class ProviderFormCubit extends FormCubit { + ProviderFormCubit(this.serverInstallationCubit) { + final RegExp regExp = + serverInstallationCubit.getProviderApiTokenValidation(); apiKey = FieldCubit( initalValue: '', validations: [ @@ -30,16 +30,15 @@ class HetznerFormCubit extends FormCubit { } final ServerInstallationCubit serverInstallationCubit; - late final FieldCubit apiKey; @override FutureOr asyncValidation() async { late bool isKeyValid; - final HetznerApi apiClient = HetznerApi(isWithToken: false); try { - isKeyValid = await apiClient.isValid(apiKey.state.value); + isKeyValid = await serverInstallationCubit + .isProviderApiTokenValid(apiKey.state.value); } catch (e) { addError(e); isKeyValid = false; diff --git a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart index 2d029bec..400fdff4 100644 --- a/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart +++ b/lib/logic/cubit/forms/setup/recovering/recovery_domain_form_cubit.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.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/forms/factories/field_cubit_factory.dart'; diff --git a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart b/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart index de7f3d43..9b09d7f6 100644 --- a/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart +++ b/lib/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart @@ -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/models/hetzner_metrics.dart'; diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index 6de64677..8485621f 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -2,7 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/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/users/users_cubit.dart'; import 'package:selfprivacy/logic/models/job.dart'; diff --git a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart index abd7b2fa..9692ca4b 100644 --- a/lib/logic/cubit/recovery_key/recovery_key_cubit.dart +++ b/lib/logic/cubit/recovery_key/recovery_key_cubit.dart @@ -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/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/json/recovery_token_status.dart'; diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart index 613069b0..3de8970f 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart @@ -3,7 +3,7 @@ import 'package:equatable/equatable.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/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'; part 'server_detailed_info_state.dart'; diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart index 97dc6292..9dc2d46e 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_repository.dart @@ -1,7 +1,7 @@ -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/providers/hetzner/hetzner.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/hetzner_server_info.dart'; +import 'package:selfprivacy/logic/models/json/provider_server_info.dart'; import 'package:selfprivacy/logic/models/timezone_settings.dart'; class ServerDetailsRepository { @@ -24,7 +24,7 @@ class ServerDetailsRepositoryDto { required this.serverTimezone, required this.autoUpgradeSettings, }); - final HetznerServerInfo hetznerServerInfo; + final ProviderServerInfo hetznerServerInfo; final TimeZoneSettings serverTimezone; diff --git a/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart index ef226c1e..d4cd113a 100644 --- a/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart +++ b/lib/logic/cubit/server_detailed_info/server_detailed_info_state.dart @@ -22,7 +22,7 @@ class Loaded extends ServerDetailsState { required this.autoUpgradeSettings, required this.checkTime, }); - final HetznerServerInfo serverInfo; + final ProviderServerInfo serverInfo; final TimeZoneSettings serverTimezone; diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 30bc828b..7261fec2 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:equatable/equatable.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/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; @@ -49,13 +50,37 @@ class ServerInstallationCubit extends Cubit { } } + RegExp getProviderApiTokenValidation() { + if (repository.providerApiFactory == null) { + print( + "validateProviderApiToken: Factory for API provider doesn't exist!", + ); + return RegExp(r''); + } + + return repository.providerApiFactory!.getProvider().getApiTokenValidation(); + } + + Future 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 { await repository.saveHetznerKey(hetznerKey); if (state is ServerInstallationRecovery) { emit( (state as ServerInstallationRecovery).copyWith( - hetznerKey: hetznerKey, + providerApiToken: hetznerKey, currentStep: RecoveryStep.serverSelection, ), ); @@ -63,7 +88,9 @@ class ServerInstallationCubit extends Cubit { } emit( - (state as ServerInstallationNotFinished).copyWith(hetznerKey: hetznerKey), + (state as ServerInstallationNotFinished).copyWith( + providerApiToken: hetznerKey, + ), ); } @@ -116,12 +143,16 @@ class ServerInstallationCubit extends Cubit { ); Future onSuccess(final ServerHostingDetails serverDetails) async { - await repository.createDnsRecords( + final bool dnsRecordsCreated = await repository.createDnsRecords( serverDetails.ip4, state.serverDomain!, onCancel: onCancel, ); + if (dnsRecordsCreated) { + repository.onCreationSuccess(serverDetails, state.serverDomain!); + } + emit( (state as ServerInstallationNotFinished).copyWith( isLoading: false, @@ -164,9 +195,24 @@ class ServerInstallationCubit extends Cubit { ); if (matches.values.every((final bool value) => value)) { - final ServerHostingDetails server = await repository.startServer( + final ServerHostingDetails? server = await repository.startServer( 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.saveIsServerStarted(true); @@ -464,7 +510,7 @@ class ServerInstallationCubit extends Cubit { final ServerInstallationRecovery dataState = state as ServerInstallationRecovery; final List servers = - await repository.getServersOnHetznerAccount(); + await repository.getServersOnProviderAccount(); final Iterable validated = servers.map( (final ServerBasicInfo server) => ServerBasicInfoWithValidators.fromServerBasicInfo( @@ -566,7 +612,7 @@ class ServerInstallationCubit extends Cubit { print('================================'); print('ServerInstallationState changed!'); 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('Domain: ${change.nextState.serverDomain}'); print('BackblazeCredential: ${change.nextState.backblazeCredential}'); @@ -599,7 +645,7 @@ class ServerInstallationCubit extends Cubit { await repository.deleteServerRelatedRecords(); emit( ServerInstallationNotFinished( - hetznerKey: state.hetznerKey, + providerApiToken: state.providerApiToken, serverDomain: state.serverDomain, cloudFlareKey: state.cloudFlareKey, backblazeCredential: state.backblazeCredential, diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index 842c1db2..920dcbf9 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -9,16 +9,18 @@ import 'package:hive/hive.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/config/hive_config.dart'; -import 'package:selfprivacy/logic/api_maps/cloudflare.dart'; -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/cloudflare.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/models/hive/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; import 'package:selfprivacy/logic/models/hive/server_domain.dart'; import 'package:selfprivacy/logic/models/hive/user.dart'; import 'package:selfprivacy/logic/models/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/server_basic_info.dart'; import 'package:selfprivacy/ui/components/action_button/action_button.dart'; @@ -39,9 +41,13 @@ class ServerAuthorizationException implements Exception { class ServerInstallationRepository { Box box = Hive.box(BNames.serverInstallationBox); Box usersBox = Hive.box(BNames.usersBox); + ProviderApiFactory? providerApiFactory = + ApiFactoryCreator.createProviderApiFactory( + ServerProvider.hetzner, // HARDCODE FOR NOW!!! + ); // Remove when provider selection is implemented. Future load() async { - final String? hetznerToken = getIt().hetznerKey; + final String? providerApiToken = getIt().hetznerKey; final String? cloudflareToken = getIt().cloudFlareKey; final ServerDomain? serverDomain = getIt().serverDomain; final BackblazeCredential? backblazeCredential = @@ -49,9 +55,14 @@ class ServerInstallationRepository { final ServerHostingDetails? serverDetails = getIt().serverDetails; + if (serverDetails != null) { + providerApiFactory = + ApiFactoryCreator.createProviderApiFactory(serverDetails.provider); + } + if (box.get(BNames.hasFinalChecked, defaultValue: false)) { return ServerInstallationFinished( - hetznerKey: hetznerToken!, + providerApiToken: providerApiToken!, cloudFlareKey: cloudflareToken!, serverDomain: serverDomain!, backblazeCredential: backblazeCredential!, @@ -68,14 +79,14 @@ class ServerInstallationRepository { if (box.get(BNames.isRecoveringServer, defaultValue: false) && serverDomain != null) { return ServerInstallationRecovery( - hetznerKey: hetznerToken, + providerApiToken: providerApiToken, cloudFlareKey: cloudflareToken, serverDomain: serverDomain, backblazeCredential: backblazeCredential, serverDetails: serverDetails, rootUser: box.get(BNames.rootUser), currentStep: _getCurrentRecoveryStep( - hetznerToken, + providerApiToken, cloudflareToken, serverDomain, serverDetails, @@ -85,7 +96,7 @@ class ServerInstallationRepository { } return ServerInstallationNotFinished( - hetznerKey: hetznerToken, + providerApiToken: providerApiToken, cloudFlareKey: cloudflareToken, serverDomain: serverDomain, backblazeCredential: backblazeCredential, @@ -127,11 +138,18 @@ class ServerInstallationRepository { usersBox.clear(); } - Future startServer( + Future startServer( final ServerHostingDetails hetznerServer, ) async { - final HetznerApi hetznerApi = HetznerApi(); - final ServerHostingDetails serverDetails = await hetznerApi.powerOn(); + ServerHostingDetails? details; + + 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; } @@ -208,23 +226,17 @@ class ServerInstallationRepository { required final Future Function(ServerHostingDetails serverDetails) onSuccess, }) async { - final HetznerApi hetznerApi = HetznerApi(); - late ServerVolume dataBase; + if (providerApiFactory == null) { + print("createServer: Factory for API provider doesn't exist!"); + return; + } + final ProviderApi api = providerApiFactory!.getProvider(); try { - final ServerVolume? createdVolume = await hetznerApi.createVolume(); - if (createdVolume == null) { - print('Volume is not created!'); - return; - } - - dataBase = createdVolume; - - final ServerHostingDetails? serverDetails = await hetznerApi.createServer( - cloudFlareKey: cloudFlareKey, + final ServerHostingDetails? serverDetails = await api.createServer( + dnsApiToken: cloudFlareKey, rootUser: rootUser, domainName: domainName, - dataBase: dataBase, ); if (serverDetails == null) { @@ -245,17 +257,16 @@ class ServerInstallationRepository { text: 'basis.delete'.tr(), isRed: true, onPressed: () async { - await hetznerApi.deleteSelfprivacyServerAndAllVolumes( + await api.deleteServer( domainName: domainName, ); ServerHostingDetails? serverDetails; try { - serverDetails = await hetznerApi.createServer( - cloudFlareKey: cloudFlareKey, + serverDetails = await api.createServer( + dnsApiToken: cloudFlareKey, rootUser: rootUser, domainName: domainName, - dataBase: dataBase, ); } catch (e) { print(e); @@ -280,25 +291,45 @@ class ServerInstallationRepository { } } - Future createDnsRecords( + Future 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 createDnsRecords( final String ip4, - final ServerDomain cloudFlareDomain, { + final ServerDomain domain, { required final void Function() onCancel, }) async { 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( ip4: ip4, - cloudFlareDomain: cloudFlareDomain, + cloudFlareDomain: domain, ); try { await cloudflareApi.createMultipleDnsRecords( ip4: ip4, - cloudFlareDomain: cloudFlareDomain, + cloudFlareDomain: domain, ); } on DioError catch (e) { - final HetznerApi hetznerApi = HetznerApi(); final NavigationService nav = getIt.get(); nav.showPopUpDialog( BrandAlert( @@ -311,8 +342,8 @@ class ServerInstallationRepository { text: 'basis.delete'.tr(), isRed: true, onPressed: () async { - await hetznerApi.deleteSelfprivacyServerAndAllVolumes( - domainName: cloudFlareDomain.domainName, + await api.deleteServer( + domainName: domain.domainName, ); onCancel(); @@ -325,12 +356,10 @@ class ServerInstallationRepository { ], ), ); + return false; } - await HetznerApi().createReverseDns( - ip4: ip4, - domainName: cloudFlareDomain.domainName, - ); + return true; } Future createDkimRecord(final ServerDomain cloudFlareDomain) async { @@ -354,13 +383,13 @@ class ServerInstallationRepository { } Future restart() async { - final HetznerApi hetznerApi = HetznerApi(); - return hetznerApi.reset(); + final ProviderApi api = providerApiFactory!.getProvider(); + return api.restart(); } Future powerOn() async { - final HetznerApi hetznerApi = HetznerApi(); - return hetznerApi.powerOn(); + final ProviderApi api = providerApiFactory!.getProvider(); + return api.powerOn(); } Future getRecoveryCapabilities( @@ -595,12 +624,19 @@ class ServerInstallationRepository { } } - Future> getServersOnHetznerAccount() async { - final HetznerApi hetznerApi = HetznerApi(); - final List servers = await hetznerApi.getServers(); + Future> getServersOnProviderAccount() async { + if (providerApiFactory == null) { + print( + 'getServersOnProviderAccount: ' + "Factory for API provider doesn't exist!", + ); + return []; + } + final ProviderApi api = providerApiFactory!.getProvider(); + final List servers = await api.getServers(); return servers .map( - (final HetznerServerInfo server) => ServerBasicInfo( + (final ProviderServerInfo server) => ServerBasicInfo( id: server.id, name: server.name, ip: server.publicNet.ipv4.ip, @@ -687,10 +723,14 @@ class ServerInstallationRepository { } Future 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(); - await hetznerApi.deleteSelfprivacyServerAndAllVolumes( + await api.deleteServer( domainName: serverDomain.domainName, ); diff --git a/lib/logic/cubit/server_installation/server_installation_state.dart b/lib/logic/cubit/server_installation/server_installation_state.dart index b3128e71..5bb59275 100644 --- a/lib/logic/cubit/server_installation/server_installation_state.dart +++ b/lib/logic/cubit/server_installation/server_installation_state.dart @@ -2,7 +2,7 @@ part of '../server_installation/server_installation_cubit.dart'; abstract class ServerInstallationState extends Equatable { const ServerInstallationState({ - required this.hetznerKey, + required this.providerApiToken, required this.cloudFlareKey, required this.backblazeCredential, required this.serverDomain, @@ -15,7 +15,7 @@ abstract class ServerInstallationState extends Equatable { @override List get props => [ - hetznerKey, + providerApiToken, cloudFlareKey, backblazeCredential, serverDomain, @@ -25,7 +25,7 @@ abstract class ServerInstallationState extends Equatable { isServerResetedFirstTime, ]; - final String? hetznerKey; + final String? providerApiToken; final String? cloudFlareKey; final BackblazeCredential? backblazeCredential; final ServerDomain? serverDomain; @@ -35,7 +35,7 @@ abstract class ServerInstallationState extends Equatable { final bool isServerResetedFirstTime; final bool isServerResetedSecondTime; - bool get isHetznerFilled => hetznerKey != null; + bool get isProviderFilled => providerApiToken != null; bool get isCloudFlareFilled => cloudFlareKey != null; bool get isBackblazeFilled => backblazeCredential != null; bool get isDomainFilled => serverDomain != null; @@ -58,7 +58,7 @@ abstract class ServerInstallationState extends Equatable { List get _fulfilementList { final List res = [ - isHetznerFilled, + isProviderFilled, isCloudFlareFilled, isBackblazeFilled, isDomainFilled, @@ -80,7 +80,7 @@ class TimerState extends ServerInstallationNotFinished { this.timerStart, this.duration, }) : super( - hetznerKey: dataState.hetznerKey, + providerApiToken: dataState.providerApiToken, cloudFlareKey: dataState.cloudFlareKey, backblazeCredential: dataState.backblazeCredential, serverDomain: dataState.serverDomain, @@ -124,7 +124,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { required final super.isServerResetedSecondTime, required final this.isLoading, required this.dnsMatches, - final super.hetznerKey, + final super.providerApiToken, final super.cloudFlareKey, final super.backblazeCredential, final super.serverDomain, @@ -136,7 +136,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { @override List get props => [ - hetznerKey, + providerApiToken, cloudFlareKey, backblazeCredential, serverDomain, @@ -149,7 +149,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { ]; ServerInstallationNotFinished copyWith({ - final String? hetznerKey, + final String? providerApiToken, final String? cloudFlareKey, final BackblazeCredential? backblazeCredential, final ServerDomain? serverDomain, @@ -162,7 +162,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { final Map? dnsMatches, }) => ServerInstallationNotFinished( - hetznerKey: hetznerKey ?? this.hetznerKey, + providerApiToken: providerApiToken ?? this.providerApiToken, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, backblazeCredential: backblazeCredential ?? this.backblazeCredential, serverDomain: serverDomain ?? this.serverDomain, @@ -178,7 +178,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { ); ServerInstallationFinished finish() => ServerInstallationFinished( - hetznerKey: hetznerKey!, + providerApiToken: providerApiToken!, cloudFlareKey: cloudFlareKey!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, @@ -193,7 +193,7 @@ class ServerInstallationNotFinished extends ServerInstallationState { class ServerInstallationEmpty extends ServerInstallationNotFinished { const ServerInstallationEmpty() : super( - hetznerKey: null, + providerApiToken: null, cloudFlareKey: null, backblazeCredential: null, serverDomain: null, @@ -209,7 +209,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished { class ServerInstallationFinished extends ServerInstallationState { const ServerInstallationFinished({ - required final String super.hetznerKey, + required final String super.providerApiToken, required final String super.cloudFlareKey, required final BackblazeCredential super.backblazeCredential, required final ServerDomain super.serverDomain, @@ -222,7 +222,7 @@ class ServerInstallationFinished extends ServerInstallationState { @override List get props => [ - hetznerKey, + providerApiToken, cloudFlareKey, backblazeCredential, serverDomain, @@ -260,7 +260,7 @@ class ServerInstallationRecovery extends ServerInstallationState { const ServerInstallationRecovery({ required this.currentStep, required this.recoveryCapabilities, - final super.hetznerKey, + final super.providerApiToken, final super.cloudFlareKey, final super.backblazeCredential, final super.serverDomain, @@ -276,7 +276,7 @@ class ServerInstallationRecovery extends ServerInstallationState { @override List get props => [ - hetznerKey, + providerApiToken, cloudFlareKey, backblazeCredential, serverDomain, @@ -288,7 +288,7 @@ class ServerInstallationRecovery extends ServerInstallationState { ]; ServerInstallationRecovery copyWith({ - final String? hetznerKey, + final String? providerApiToken, final String? cloudFlareKey, final BackblazeCredential? backblazeCredential, final ServerDomain? serverDomain, @@ -298,7 +298,7 @@ class ServerInstallationRecovery extends ServerInstallationState { final ServerRecoveryCapabilities? recoveryCapabilities, }) => ServerInstallationRecovery( - hetznerKey: hetznerKey ?? this.hetznerKey, + providerApiToken: providerApiToken ?? this.providerApiToken, cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey, backblazeCredential: backblazeCredential ?? this.backblazeCredential, serverDomain: serverDomain ?? this.serverDomain, @@ -309,7 +309,7 @@ class ServerInstallationRecovery extends ServerInstallationState { ); ServerInstallationFinished finish() => ServerInstallationFinished( - hetznerKey: hetznerKey!, + providerApiToken: providerApiToken!, cloudFlareKey: cloudFlareKey!, backblazeCredential: backblazeCredential!, serverDomain: serverDomain!, diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart index f83a2a9a..19160af1 100644 --- a/lib/logic/cubit/services/services_cubit.dart +++ b/lib/logic/cubit/services/services_cubit.dart @@ -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/cubit/app_config_dependent/authentication_dependend_cubit.dart'; diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index 9b86c109..3ce0d18e 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -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/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'; diff --git a/lib/logic/cubit/volumes/volumes_cubit.dart b/lib/logic/cubit/volumes/volumes_cubit.dart index 8fcc6d55..d724e121 100644 --- a/lib/logic/cubit/volumes/volumes_cubit.dart +++ b/lib/logic/cubit/volumes/volumes_cubit.dart @@ -1,6 +1,6 @@ import 'package:selfprivacy/config/get_it_config.dart'; -import 'package:selfprivacy/logic/api_maps/hetzner.dart'; -import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.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/cubit/app_config_dependent/authentication_dependend_cubit.dart'; import 'package:selfprivacy/logic/models/hive/server_details.dart'; @@ -12,7 +12,10 @@ class ApiVolumesCubit ApiVolumesCubit(final ServerInstallationCubit serverInstallationCubit) : super(serverInstallationCubit, const ApiVolumesState.initial()); - final ServerApi api = ServerApi(); + final VolumeProviderApiFactory providerApi = + VolumeApiFactoryCreator.createVolumeProviderApiFactory( + getIt().serverDetails!.provider, + ); @override void load() async { @@ -27,7 +30,8 @@ class ApiVolumesCubit } void _refetch() async { - final List volumes = await HetznerApi().getVolumes(); + final List volumes = + await providerApi.getVolumeProvider().getVolumes(); if (volumes.isNotEmpty) { emit(ApiVolumesState(volumes, LoadingStatus.success)); } else { @@ -37,29 +41,29 @@ class ApiVolumesCubit void attachVolume(final ServerVolume volume) async { final ServerHostingDetails server = getIt().serverDetails!; - HetznerApi().attachVolume(volume.id, server.id); + await providerApi.getVolumeProvider().attachVolume(volume.id, server.id); refresh(); } void detachVolume(final ServerVolume volume) async { - HetznerApi().detachVolume(volume.id); + await providerApi.getVolumeProvider().detachVolume(volume.id); refresh(); } - void resizeVolume(final ServerVolume volume, final int newSizeGb) { - if (volume.sizeByte < newSizeGb) { - HetznerApi().resizeVolume(volume.id, newSizeGb); - refresh(); - } + void resizeVolume(final ServerVolume volume, final int newSizeGb) async { + //if (volume.sizeByte < newSizeGb) { + await providerApi.getVolumeProvider().resizeVolume(volume.id, newSizeGb); + refresh(); + //} } void createVolume() async { - HetznerApi().createVolume(); + await providerApi.getVolumeProvider().createVolume(); refresh(); } void deleteVolume(final ServerVolume volume) async { - HetznerApi().deleteVolume(volume.id); + await providerApi.getVolumeProvider().deleteVolume(volume.id); refresh(); } diff --git a/lib/logic/models/hive/server_details.g.dart b/lib/logic/models/hive/server_details.g.dart index f10628e7..d4ea9b01 100644 --- a/lib/logic/models/hive/server_details.g.dart +++ b/lib/logic/models/hive/server_details.g.dart @@ -73,17 +73,23 @@ class ServerVolumeAdapter extends TypeAdapter { return ServerVolume( id: fields[1] as int, name: fields[2] as String, + sizeByte: fields[3] == null ? 10737418240 : fields[3] as int, + serverId: fields[4] as int?, ); } @override void write(BinaryWriter writer, ServerVolume obj) { writer - ..writeByte(2) + ..writeByte(4) ..writeByte(1) ..write(obj.id) ..writeByte(2) - ..write(obj.name); + ..write(obj.name) + ..writeByte(3) + ..write(obj.sizeByte) + ..writeByte(4) + ..write(obj.serverId); } @override diff --git a/lib/logic/models/hive/user.g.dart b/lib/logic/models/hive/user.g.dart index d9b28d65..a1889dc1 100644 --- a/lib/logic/models/hive/user.g.dart +++ b/lib/logic/models/hive/user.g.dart @@ -11,9 +11,9 @@ class UserAdapter extends TypeAdapter { final int typeId = 1; @override - User read(final BinaryReader reader) { - final int numOfFields = reader.readByte(); - final Map fields = { + User read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return User( @@ -26,7 +26,7 @@ class UserAdapter extends TypeAdapter { } @override - void write(final BinaryWriter writer, final User obj) { + void write(BinaryWriter writer, User obj) { writer ..writeByte(5) ..writeByte(0) @@ -45,7 +45,7 @@ class UserAdapter extends TypeAdapter { int get hashCode => typeId.hashCode; @override - bool operator ==(final Object other) => + bool operator ==(Object other) => identical(this, other) || other is UserAdapter && runtimeType == other.runtimeType && diff --git a/lib/logic/models/json/hetzner_server_info.dart b/lib/logic/models/json/hetzner_server_info.dart deleted file mode 100644 index ccf036a1..00000000 --- a/lib/logic/models/json/hetzner_server_info.dart +++ /dev/null @@ -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 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 json) => - _$HetznerServerInfoFromJson(json); -} - -@JsonSerializable() -class HetznerPublicNetInfo { - HetznerPublicNetInfo(this.ipv4); - final HetznerIp4 ipv4; - - static HetznerPublicNetInfo fromJson(final Map 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 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 prices; - - static HetznerServerTypeInfo fromJson(final Map 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 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 json) => - _$HetznerLocationFromJson(json); -} diff --git a/lib/logic/models/json/provider_server_info.dart b/lib/logic/models/json/provider_server_info.dart new file mode 100644 index 00000000..cc0166db --- /dev/null +++ b/lib/logic/models/json/provider_server_info.dart @@ -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 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 json) => + _$ProviderServerInfoFromJson(json); +} + +@JsonSerializable() +class ProviderPublicNetInfo { + ProviderPublicNetInfo(this.ipv4); + final ProviderIp4 ipv4; + + static ProviderPublicNetInfo fromJson(final Map 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 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 prices; + + static ProviderServerTypeInfo fromJson(final Map 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 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 json) => + _$ProviderLocationFromJson(json); +} diff --git a/lib/logic/models/json/hetzner_server_info.g.dart b/lib/logic/models/json/provider_server_info.g.dart similarity index 58% rename from lib/logic/models/json/hetzner_server_info.g.dart rename to lib/logic/models/json/provider_server_info.g.dart index e8c21917..6a4f7aac 100644 --- a/lib/logic/models/json/hetzner_server_info.g.dart +++ b/lib/logic/models/json/provider_server_info.g.dart @@ -1,21 +1,22 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'hetzner_server_info.dart'; +part of 'provider_server_info.dart'; // ************************************************************************** // JsonSerializableGenerator // ************************************************************************** -HetznerServerInfo _$HetznerServerInfoFromJson(Map json) => - HetznerServerInfo( +ProviderServerInfo _$ProviderServerInfoFromJson(Map json) => + ProviderServerInfo( json['id'] as int, json['name'] as String, $enumDecode(_$ServerStatusEnumMap, json['status']), DateTime.parse(json['created'] as String), - HetznerServerTypeInfo.fromJson( + ProviderServerTypeInfo.fromJson( json['server_type'] as Map), - HetznerServerInfo.locationFromJson(json['datacenter'] as Map), - HetznerPublicNetInfo.fromJson(json['public_net'] as Map), + ProviderServerInfo.locationFromJson(json['datacenter'] as Map), + ProviderPublicNetInfo.fromJson( + json['public_net'] as Map), (json['volumes'] as List).map((e) => e as int).toList(), ); @@ -31,38 +32,38 @@ const _$ServerStatusEnumMap = { ServerStatus.unknown: 'unknown', }; -HetznerPublicNetInfo _$HetznerPublicNetInfoFromJson( +ProviderPublicNetInfo _$ProviderPublicNetInfoFromJson( Map json) => - HetznerPublicNetInfo( - HetznerIp4.fromJson(json['ipv4'] as Map), + ProviderPublicNetInfo( + ProviderIp4.fromJson(json['ipv4'] as Map), ); -HetznerIp4 _$HetznerIp4FromJson(Map json) => HetznerIp4( +ProviderIp4 _$ProviderIp4FromJson(Map json) => ProviderIp4( json['id'] as int, json['ip'] as String, json['blocked'] as bool, json['dns_ptr'] as String, ); -HetznerServerTypeInfo _$HetznerServerTypeInfoFromJson( +ProviderServerTypeInfo _$ProviderServerTypeInfoFromJson( Map json) => - HetznerServerTypeInfo( + ProviderServerTypeInfo( json['cores'] as int, json['memory'] as num, json['disk'] as int, (json['prices'] as List) - .map((e) => HetznerPriceInfo.fromJson(e as Map)) + .map((e) => ProviderPriceInfo.fromJson(e as Map)) .toList(), ); -HetznerPriceInfo _$HetznerPriceInfoFromJson(Map json) => - HetznerPriceInfo( - HetznerPriceInfo.getPrice(json['price_hourly'] as Map), - HetznerPriceInfo.getPrice(json['price_monthly'] as Map), +ProviderPriceInfo _$ProviderPriceInfoFromJson(Map json) => + ProviderPriceInfo( + ProviderPriceInfo.getPrice(json['price_hourly'] as Map), + ProviderPriceInfo.getPrice(json['price_monthly'] as Map), ); -HetznerLocation _$HetznerLocationFromJson(Map json) => - HetznerLocation( +ProviderLocation _$ProviderLocationFromJson(Map json) => + ProviderLocation( json['country'] as String, json['city'] as String, json['description'] as String, diff --git a/lib/ui/pages/setup/initializing.dart b/lib/ui/pages/setup/initializing.dart index 9c92f161..6025d621 100644 --- a/lib/ui/pages/setup/initializing.dart +++ b/lib/ui/pages/setup/initializing.dart @@ -21,7 +21,9 @@ import 'package:selfprivacy/ui/pages/setup/recovering/recovery_routing.dart'; import 'package:selfprivacy/utils/route_transitions/basic.dart'; class InitializingPage extends StatelessWidget { - const InitializingPage({final super.key}); + const InitializingPage({ + final super.key, + }); @override Widget build(final BuildContext context) { @@ -135,10 +137,12 @@ class InitializingPage extends StatelessWidget { Widget _stepHetzner(final ServerInstallationCubit serverInstallationCubit) => BlocProvider( - create: (final context) => HetznerFormCubit(serverInstallationCubit), + create: (final context) => ProviderFormCubit( + serverInstallationCubit, + ), child: Builder( builder: (final context) { - final formCubitState = context.watch().state; + final formCubitState = context.watch().state; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -152,7 +156,7 @@ class InitializingPage extends StatelessWidget { BrandText.body2('initializing.2'.tr()), const Spacer(), CubitFormTextField( - formFieldCubit: context.read().apiKey, + formFieldCubit: context.read().apiKey, textAlign: TextAlign.center, scrollPadding: const EdgeInsets.only(bottom: 70), decoration: const InputDecoration( @@ -163,7 +167,7 @@ class InitializingPage extends StatelessWidget { BrandButton.rised( onPressed: formCubitState.isSubmitting ? null - : () => context.read().trySubmit(), + : () => context.read().trySubmit(), text: 'basis.connect'.tr(), ), const SizedBox(height: 10), diff --git a/lib/ui/pages/setup/recovering/recovery_hentzner_connected.dart b/lib/ui/pages/setup/recovering/recovery_hentzner_connected.dart index 6976283c..819cbea2 100644 --- a/lib/ui/pages/setup/recovering/recovery_hentzner_connected.dart +++ b/lib/ui/pages/setup/recovering/recovery_hentzner_connected.dart @@ -19,11 +19,11 @@ class RecoveryHetznerConnected extends StatelessWidget { context.watch(); return BlocProvider( - create: (final BuildContext context) => HetznerFormCubit(appConfig), + create: (final BuildContext context) => ProviderFormCubit(appConfig), child: Builder( builder: (final BuildContext context) { final FormCubitState formCubitState = - context.watch().state; + context.watch().state; return BrandHeroScreen( heroTitle: 'recovering.hetzner_connected'.tr(), @@ -37,7 +37,7 @@ class RecoveryHetznerConnected extends StatelessWidget { }, children: [ CubitFormTextField( - formFieldCubit: context.read().apiKey, + formFieldCubit: context.read().apiKey, decoration: InputDecoration( border: const OutlineInputBorder(), labelText: 'recovering.hetzner_connected_placeholder'.tr(), @@ -48,7 +48,7 @@ class RecoveryHetznerConnected extends StatelessWidget { title: 'more.continue'.tr(), onPressed: formCubitState.isSubmitting ? null - : () => context.read().trySubmit(), + : () => context.read().trySubmit(), ), const SizedBox(height: 16), BrandButton.text( diff --git a/lib/utils/scalars.dart b/lib/utils/scalars.dart new file mode 100644 index 00000000..71a571e7 --- /dev/null +++ b/lib/utils/scalars.dart @@ -0,0 +1,7 @@ +String dateTimeToJson(DateTime data) { + return data.toIso8601String(); +} + +DateTime dateTimeFromJson(dynamic data) { + return DateTime.parse(data as String); +} diff --git a/pubspec.lock b/pubspec.lock index e3faf1b6..cb308ae6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -169,6 +169,48 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: @@ -218,6 +260,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + dbus: + dependency: transitive + description: + name: dbus + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.3" device_info_plus: dependency: "direct main" description: @@ -356,6 +405,13 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: "direct dev" description: @@ -462,6 +518,90 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: transitive description: @@ -672,6 +812,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" node_preamble: dependency: transitive description: @@ -679,6 +826,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + normalize: + dependency: transitive + description: + name: normalize + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.0+1" package_config: dependency: transitive description: @@ -826,6 +980,20 @@ packages: url: "https://pub.dartlang.org" source: hosted 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: dependency: "direct main" description: @@ -1153,6 +1321,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.1" + uuid: + dependency: transitive + description: + name: uuid + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.6" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1e052155..f5367d99 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,6 +26,10 @@ dependencies: flutter_markdown: ^0.6.9 flutter_secure_storage: ^5.0.2 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 hive: ^2.0.5 hive_flutter: ^1.1.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 37b7695e..070eb3b5 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,11 +6,14 @@ #include "generated_plugin_registrant.h" +#include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); SystemThemePluginRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index a1aaa278..a26e37b0 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus_windows flutter_secure_storage_windows system_theme url_launcher_windows