refactor(server-api): Make general server info polymorphic

Removing Hetzner type hardcode from server page and replacing it with generic String-based metadata container
pull/140/head
NaiJi ✨ 2022-11-11 07:32:01 +04:00
parent b3395915da
commit 10bdd4c800
11 changed files with 189 additions and 67 deletions

View File

@ -110,6 +110,7 @@
"disk": "Disk local",
"monthly_cost": "Monthly cost",
"location": "Location",
"provider": "Provider",
"core_count": {
"one": "{} core",
"two": "{} cores",

View File

@ -110,6 +110,7 @@
"disk": "Диск",
"monthly_cost": "Ежемесячная стоимость",
"location": "Размещение",
"provider": "Провайдер",
"core_count": {
"one": "{} ядро",
"two": "{} ядра",

View File

@ -2,18 +2,20 @@ import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
import 'package:selfprivacy/logic/models/disk_size.dart';
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/price.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/logic/models/server_provider_location.dart';
import 'package:selfprivacy/logic/models/server_type.dart';
import 'package:selfprivacy/utils/extensions/string_extensions.dart';
import 'package:selfprivacy/utils/password_generator.dart';
class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
@ -313,8 +315,6 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
final String formattedHostname = getHostnameFromDomain(domainName);
// TODO: change to 'master' change to 'master' change to 'master'
const String infectBranch = 'providers/digital-ocean';
final String userdataString =
@ -474,14 +474,59 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
return metrics;
}
Future<HetznerServerInfo> getInfo() async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient();
final Response response = await client.get('/servers/${hetznerServer!.id}');
close(client);
@override
Future<List<ServerMetadataEntity>> getMetadata(final int serverId) async {
List<ServerMetadataEntity> metadata = [];
return HetznerServerInfo.fromJson(response.data!['server']);
final Dio client = await getClient();
try {
final Response response = await client.get('/droplets/$serverId');
final droplet = response.data!['droplet'];
metadata = [
ServerMetadataEntity(
type: MetadataType.id,
name: 'server.server_id'.tr(),
value: droplet['id'].toString(),
),
ServerMetadataEntity(
type: MetadataType.status,
name: 'server.status'.tr(),
value: droplet['status'].toString().capitalize(),
),
ServerMetadataEntity(
type: MetadataType.cpu,
name: 'server.cpu'.tr(),
value: 'server.core_count'.plural(droplet['vcpus']),
),
ServerMetadataEntity(
type: MetadataType.ram,
name: 'server.ram'.tr(),
value: "${droplet['memory'].toString()} MB",
),
ServerMetadataEntity(
type: MetadataType.cost,
name: 'server.monthly_cost'.tr(),
value: droplet['size']['price_monthly'].toString(),
),
ServerMetadataEntity(
type: MetadataType.location,
name: 'server.location'.tr(),
value:
'${droplet['region']['name']} ${getEmojiFlag(droplet['region']['slug'].toString()) ?? ''}',
),
ServerMetadataEntity(
type: MetadataType.other,
name: 'server.provider'.tr(),
value: 'Digital Ocean',
),
];
} catch (e) {
print(e);
} finally {
close(client);
}
return metadata;
}
@override

View File

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/volume_provider.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider.dart';
@ -12,8 +13,10 @@ import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/hive/user.dart';
import 'package:selfprivacy/logic/models/price.dart';
import 'package:selfprivacy/logic/models/server_basic_info.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/logic/models/server_provider_location.dart';
import 'package:selfprivacy/logic/models/server_type.dart';
import 'package:selfprivacy/utils/extensions/string_extensions.dart';
import 'package:selfprivacy/utils/password_generator.dart';
class HetznerApi extends ServerProviderApi with VolumeProviderApi {
@ -513,14 +516,59 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
return metrics;
}
Future<HetznerServerInfo> getInfo() async {
final ServerHostingDetails? hetznerServer =
getIt<ApiConfigModel>().serverDetails;
final Dio client = await getClient();
final Response response = await client.get('/servers/${hetznerServer!.id}');
close(client);
@override
Future<List<ServerMetadataEntity>> getMetadata(final int serverId) async {
List<ServerMetadataEntity> metadata = [];
return HetznerServerInfo.fromJson(response.data!['server']);
final Dio client = await getClient();
try {
final Response response = await client.get('/servers/$serverId');
final hetznerInfo = HetznerServerInfo.fromJson(response.data!['server']);
metadata = [
ServerMetadataEntity(
type: MetadataType.id,
name: 'server.server_id'.tr(),
value: hetznerInfo.id.toString(),
),
ServerMetadataEntity(
type: MetadataType.status,
name: 'server.status'.tr(),
value: hetznerInfo.status.toString().split('.')[1].capitalize(),
),
ServerMetadataEntity(
type: MetadataType.cpu,
name: 'server.cpu'.tr(),
value: 'server.core_count'.plural(hetznerInfo.serverType.cores),
),
ServerMetadataEntity(
type: MetadataType.ram,
name: 'server.ram'.tr(),
value: '${hetznerInfo.serverType.memory.toString()} GB',
),
ServerMetadataEntity(
type: MetadataType.cost,
name: 'server.monthly_cost'.tr(),
value: hetznerInfo.serverType.prices[1].monthly.toStringAsFixed(2),
),
ServerMetadataEntity(
type: MetadataType.location,
name: 'server.location'.tr(),
value:
'${hetznerInfo.location.city}, ${hetznerInfo.location.country}',
),
ServerMetadataEntity(
type: MetadataType.other,
name: 'server.provider'.tr(),
value: 'Hetzner',
),
];
} catch (e) {
print(e);
} finally {
close(client);
}
return metadata;
}
@override

View File

@ -3,6 +3,7 @@ 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/server_basic_info.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/logic/models/server_provider_location.dart';
import 'package:selfprivacy/logic/models/server_type.dart';
@ -39,6 +40,7 @@ abstract class ServerProviderApi extends ApiMap {
Future<bool> isApiTokenValid(final String token);
ProviderApiTokenValidation getApiTokenValidation();
Future<List<ServerMetadataEntity>> getMetadata(final int serverId);
abstract final String infectProviderName;
}

View File

@ -2,7 +2,7 @@ import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_repository.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/logic/models/timezone_settings.dart';
part 'server_detailed_info_state.dart';
@ -22,7 +22,7 @@ class ServerDetailsCubit
final ServerDetailsRepositoryDto data = await repository.load();
emit(
Loaded(
serverInfo: data.hetznerServerInfo,
metadata: data.metadata,
autoUpgradeSettings: data.autoUpgradeSettings,
serverTimezone: data.serverTimezone,
checkTime: DateTime.now(),

View File

@ -1,22 +1,42 @@
import 'package:selfprivacy/config/get_it_config.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/hetzner/hetzner.dart';
import 'package:selfprivacy/logic/api_maps/graphql_maps/server_api/server.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_creator.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/api_factory_settings.dart';
import 'package:selfprivacy/logic/api_maps/rest_maps/server_providers/server_provider_factory.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
import 'package:selfprivacy/logic/models/hive/server_details.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/logic/models/timezone_settings.dart';
class ServerDetailsRepository {
HetznerApi hetzner = HetznerApi(
/// TODO: Hetzner hardcode (???)
region: getIt<ApiConfigModel>().serverLocation,
);
ServerApi server = ServerApi();
ServerProviderApiFactory? serverProviderApiFactory;
void _buildServerProviderFactory() {
final ServerProvider? providerType = getIt<ApiConfigModel>().serverProvider;
final String? location = getIt<ApiConfigModel>().serverLocation;
serverProviderApiFactory = ApiFactoryCreator.createServerProviderApiFactory(
ServerProviderApiFactorySettings(
provider: providerType ?? ServerProvider.unknown,
location: location,
),
);
}
Future<ServerDetailsRepositoryDto> load() async {
if (serverProviderApiFactory == null) {
_buildServerProviderFactory();
}
final settings = await server.getSystemSettings();
final serverId = getIt<ApiConfigModel>().serverDetails!.id;
final metadata = await serverProviderApiFactory!
.getServerProvider()
.getMetadata(serverId);
return ServerDetailsRepositoryDto(
autoUpgradeSettings: settings.autoUpgradeSettings,
hetznerServerInfo: await hetzner.getInfo(),
metadata: metadata,
serverTimezone: TimeZoneSettings.fromString(
settings.timezone,
),
@ -40,13 +60,11 @@ class ServerDetailsRepository {
class ServerDetailsRepositoryDto {
ServerDetailsRepositoryDto({
required this.hetznerServerInfo,
required this.metadata,
required this.serverTimezone,
required this.autoUpgradeSettings,
});
final HetznerServerInfo hetznerServerInfo;
final List<ServerMetadataEntity> metadata;
final TimeZoneSettings serverTimezone;
final AutoUpgradeSettings autoUpgradeSettings;
}

View File

@ -17,21 +17,19 @@ class Loading extends ServerDetailsState {}
class Loaded extends ServerDetailsState {
const Loaded({
required this.serverInfo,
required this.metadata,
required this.serverTimezone,
required this.autoUpgradeSettings,
required this.checkTime,
});
final HetznerServerInfo serverInfo;
final List<ServerMetadataEntity> metadata;
final TimeZoneSettings serverTimezone;
final AutoUpgradeSettings autoUpgradeSettings;
final DateTime checkTime;
@override
List<Object> get props => [
serverInfo,
metadata,
serverTimezone,
autoUpgradeSettings,
checkTime,

View File

@ -0,0 +1,21 @@
enum MetadataType {
id,
status,
cpu,
ram,
cost,
location,
other,
}
class ServerMetadataEntity {
ServerMetadataEntity({
required this.name,
required this.value,
this.type = MetadataType.other,
});
final MetadataType type;
final String name;
final String value;
}

View File

@ -10,6 +10,7 @@ import 'package:selfprivacy/logic/cubit/server_installation/server_installation_
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
import 'package:selfprivacy/logic/models/auto_upgrade_settings.dart';
import 'package:selfprivacy/logic/models/job.dart';
import 'package:selfprivacy/logic/models/server_metadata.dart';
import 'package:selfprivacy/ui/components/brand_button/segmented_buttons.dart';
import 'package:selfprivacy/ui/components/brand_cards/filled_card.dart';
import 'package:selfprivacy/ui/components/brand_hero_screen/brand_hero_screen.dart';

View File

@ -1,6 +1,16 @@
part of 'server_details_screen.dart';
class _TextDetails extends StatelessWidget {
final Map<MetadataType, IconData> metadataToIcon = const {
MetadataType.id: Icons.numbers_outlined,
MetadataType.status: Icons.mode_standby_outlined,
MetadataType.cpu: Icons.memory_outlined,
MetadataType.ram: Icons.memory_outlined,
MetadataType.cost: Icons.euro_outlined,
MetadataType.location: Icons.location_on_outlined,
MetadataType.other: Icons.info_outlined,
};
@override
Widget build(final BuildContext context) {
final details = context.watch<ServerDetailsCubit>().state;
@ -10,7 +20,6 @@ class _TextDetails extends StatelessWidget {
} else if (details is ServerDetailsNotReady) {
return _TempMessage(message: 'basis.no_data'.tr());
} else if (details is Loaded) {
final data = details.serverInfo;
return FilledCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -24,37 +33,15 @@ class _TextDetails extends StatelessWidget {
),
),
),
ListTileOnSurfaceVariant(
leadingIcon: Icons.numbers_outlined,
title: data.id.toString(),
subtitle: 'server.server_id'.tr(),
),
ListTileOnSurfaceVariant(
leadingIcon: Icons.mode_standby_outlined,
title: data.status.toString().split('.')[1].capitalize(),
subtitle: 'server.status'.tr(),
),
ListTileOnSurfaceVariant(
leadingIcon: Icons.memory_outlined,
title: 'server.core_count'.plural(data.serverType.cores),
subtitle: 'server.cpu'.tr(),
),
ListTileOnSurfaceVariant(
leadingIcon: Icons.memory_outlined,
title: '${data.serverType.memory.toString()} GB',
subtitle: 'server.ram'.tr(),
),
ListTileOnSurfaceVariant(
leadingIcon: Icons.euro_outlined,
title: data.serverType.prices[1].monthly.toStringAsFixed(2),
subtitle: 'server.monthly_cost'.tr(),
),
// Server location
ListTileOnSurfaceVariant(
leadingIcon: Icons.location_on_outlined,
title: '${data.location.city}, ${data.location.country}',
subtitle: 'server.location'.tr(),
),
...details.metadata
.map(
(final metadata) => ListTileOnSurfaceVariant(
leadingIcon: metadataToIcon[metadata.type],
title: metadata.name,
subtitle: metadata.value,
),
)
.toList(),
],
),
);