mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-23 09:16:54 +00:00
refactor(server-api): Generalize and encapsulate server metrics endpoints
This commit is contained in:
parent
e66b24d869
commit
a7cbde663e
|
@ -10,6 +10,7 @@ import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/logic/models/price.dart';
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
|
@ -444,34 +445,14 @@ class DigitalOceanApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return server.copyWith(startTime: DateTime.now());
|
return server.copyWith(startTime: DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getMetrics(
|
@override
|
||||||
|
Future<ServerMetrics?> getMetrics(
|
||||||
|
final int serverId,
|
||||||
final DateTime start,
|
final DateTime start,
|
||||||
final DateTime end,
|
final DateTime end,
|
||||||
final String type,
|
|
||||||
) async {
|
) async {
|
||||||
final ServerHostingDetails? hetznerServer =
|
ServerMetrics? metrics;
|
||||||
getIt<ApiConfigModel>().serverDetails;
|
return metrics!;
|
||||||
|
|
||||||
Map<String, dynamic> metrics = {};
|
|
||||||
final Dio client = await getClient();
|
|
||||||
try {
|
|
||||||
final Map<String, dynamic> queryParameters = {
|
|
||||||
'start': start.toUtc().toIso8601String(),
|
|
||||||
'end': end.toUtc().toIso8601String(),
|
|
||||||
'type': type
|
|
||||||
};
|
|
||||||
final Response res = await client.get(
|
|
||||||
'/servers/${hetznerServer!.id}/metrics',
|
|
||||||
queryParameters: queryParameters,
|
|
||||||
);
|
|
||||||
metrics = res.data;
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
} finally {
|
|
||||||
close(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
return metrics;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/logic/models/price.dart';
|
import 'package:selfprivacy/logic/models/price.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
|
@ -486,14 +487,12 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return server.copyWith(startTime: DateTime.now());
|
return server.copyWith(startTime: DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getMetrics(
|
Future<Map<String, dynamic>> requestRawMetrics(
|
||||||
|
final int serverId,
|
||||||
final DateTime start,
|
final DateTime start,
|
||||||
final DateTime end,
|
final DateTime end,
|
||||||
final String type,
|
final String type,
|
||||||
) async {
|
) async {
|
||||||
final ServerHostingDetails? hetznerServer =
|
|
||||||
getIt<ApiConfigModel>().serverDetails;
|
|
||||||
|
|
||||||
Map<String, dynamic> metrics = {};
|
Map<String, dynamic> metrics = {};
|
||||||
final Dio client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
|
@ -503,10 +502,10 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
'type': type
|
'type': type
|
||||||
};
|
};
|
||||||
final Response res = await client.get(
|
final Response res = await client.get(
|
||||||
'/servers/${hetznerServer!.id}/metrics',
|
'/servers/$serverId/metrics',
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
);
|
);
|
||||||
metrics = res.data;
|
metrics = res.data['metrics'];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -516,6 +515,70 @@ class HetznerApi extends ServerProviderApi with VolumeProviderApi {
|
||||||
return metrics;
|
return metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<TimeSeriesData> timeSeriesSerializer(
|
||||||
|
final Map<String, dynamic> json,
|
||||||
|
final String type,
|
||||||
|
) {
|
||||||
|
final List list = json['time_series'][type]['values'];
|
||||||
|
return list
|
||||||
|
.map((final el) => TimeSeriesData(el[0], double.parse(el[1])))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<ServerMetrics?> getMetrics(
|
||||||
|
final int serverId,
|
||||||
|
final DateTime start,
|
||||||
|
final DateTime end,
|
||||||
|
) async {
|
||||||
|
ServerMetrics? metrics;
|
||||||
|
|
||||||
|
final Map<String, dynamic> rawCpuMetrics = await requestRawMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
'cpu',
|
||||||
|
);
|
||||||
|
final Map<String, dynamic> rawNetworkMetrics = await requestRawMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
'network',
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rawNetworkMetrics.isEmpty || rawCpuMetrics.isEmpty) {
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics = ServerMetrics(
|
||||||
|
cpu: timeSeriesSerializer(
|
||||||
|
rawCpuMetrics,
|
||||||
|
'cpu',
|
||||||
|
),
|
||||||
|
ppsIn: timeSeriesSerializer(
|
||||||
|
rawNetworkMetrics,
|
||||||
|
'network.0.pps.in',
|
||||||
|
),
|
||||||
|
ppsOut: timeSeriesSerializer(
|
||||||
|
rawNetworkMetrics,
|
||||||
|
'network.0.pps.out',
|
||||||
|
),
|
||||||
|
bandwidthIn: timeSeriesSerializer(
|
||||||
|
rawNetworkMetrics,
|
||||||
|
'network.0.bandwidth.in',
|
||||||
|
),
|
||||||
|
bandwidthOut: timeSeriesSerializer(
|
||||||
|
rawNetworkMetrics,
|
||||||
|
'network.0.bandwidth.out',
|
||||||
|
),
|
||||||
|
end: end,
|
||||||
|
start: start,
|
||||||
|
stepsInSecond: rawCpuMetrics['step'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<ServerMetadataEntity>> getMetadata(final int serverId) async {
|
Future<List<ServerMetadataEntity>> getMetadata(final int serverId) async {
|
||||||
List<ServerMetadataEntity> metadata = [];
|
List<ServerMetadataEntity> metadata = [];
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'package:selfprivacy/logic/api_maps/rest_maps/api_map.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
import 'package:selfprivacy/logic/models/server_metadata.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
import 'package:selfprivacy/logic/models/server_provider_location.dart';
|
||||||
|
@ -41,6 +42,11 @@ abstract class ServerProviderApi extends ApiMap {
|
||||||
Future<bool> isApiTokenValid(final String token);
|
Future<bool> isApiTokenValid(final String token);
|
||||||
ProviderApiTokenValidation getApiTokenValidation();
|
ProviderApiTokenValidation getApiTokenValidation();
|
||||||
Future<List<ServerMetadataEntity>> getMetadata(final int serverId);
|
Future<List<ServerMetadataEntity>> getMetadata(final int serverId);
|
||||||
|
Future<ServerMetrics?> getMetrics(
|
||||||
|
final int serverId,
|
||||||
|
final DateTime start,
|
||||||
|
final DateTime end,
|
||||||
|
);
|
||||||
|
|
||||||
abstract final String infectProviderName;
|
abstract final String infectProviderName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
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/common_enum/common_enum.dart';
|
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart';
|
|
||||||
|
|
||||||
class MetricsLoadException implements Exception {
|
|
||||||
MetricsLoadException(this.message);
|
|
||||||
final String message;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HetznerMetricsRepository {
|
|
||||||
Future<HetznerMetricsLoaded> getMetrics(final Period period) async {
|
|
||||||
final DateTime end = DateTime.now();
|
|
||||||
DateTime start;
|
|
||||||
|
|
||||||
switch (period) {
|
|
||||||
case Period.hour:
|
|
||||||
start = end.subtract(const Duration(hours: 1));
|
|
||||||
break;
|
|
||||||
case Period.day:
|
|
||||||
start = end.subtract(const Duration(days: 1));
|
|
||||||
break;
|
|
||||||
case Period.month:
|
|
||||||
start = end.subtract(const Duration(days: 15));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
final HetznerApi api = HetznerApi(
|
|
||||||
/// TODO: Hetzner hardcode (???)
|
|
||||||
hasLogger: false,
|
|
||||||
region: getIt<ApiConfigModel>().serverLocation,
|
|
||||||
);
|
|
||||||
|
|
||||||
final List<Map<String, dynamic>> results = await Future.wait([
|
|
||||||
api.getMetrics(start, end, 'cpu'),
|
|
||||||
api.getMetrics(start, end, 'network'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
final cpuMetricsData = results[0]['metrics'];
|
|
||||||
final networkMetricsData = results[1]['metrics'];
|
|
||||||
|
|
||||||
if (cpuMetricsData == null || networkMetricsData == null) {
|
|
||||||
throw MetricsLoadException('Metrics data is null');
|
|
||||||
}
|
|
||||||
|
|
||||||
return HetznerMetricsLoaded(
|
|
||||||
period: period,
|
|
||||||
start: start,
|
|
||||||
end: end,
|
|
||||||
stepInSeconds: cpuMetricsData['step'],
|
|
||||||
cpu: timeSeriesSerializer(cpuMetricsData, 'cpu'),
|
|
||||||
ppsIn: timeSeriesSerializer(networkMetricsData, 'network.0.pps.in'),
|
|
||||||
ppsOut: timeSeriesSerializer(networkMetricsData, 'network.0.pps.out'),
|
|
||||||
bandwidthIn:
|
|
||||||
timeSeriesSerializer(networkMetricsData, 'network.0.bandwidth.in'),
|
|
||||||
bandwidthOut: timeSeriesSerializer(
|
|
||||||
networkMetricsData,
|
|
||||||
'network.0.bandwidth.out',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<TimeSeriesData> timeSeriesSerializer(
|
|
||||||
final Map<String, dynamic> json,
|
|
||||||
final String type,
|
|
||||||
) {
|
|
||||||
final List list = json['time_series'][type]['values'];
|
|
||||||
return list
|
|
||||||
.map((final el) => TimeSeriesData(el[0], double.parse(el[1])))
|
|
||||||
.toList();
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
part of 'hetzner_metrics_cubit.dart';
|
|
||||||
|
|
||||||
abstract class HetznerMetricsState extends Equatable {
|
|
||||||
const HetznerMetricsState();
|
|
||||||
|
|
||||||
abstract final Period period;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HetznerMetricsLoading extends HetznerMetricsState {
|
|
||||||
const HetznerMetricsLoading(this.period);
|
|
||||||
@override
|
|
||||||
final Period period;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [period];
|
|
||||||
}
|
|
||||||
|
|
||||||
class HetznerMetricsLoaded extends HetznerMetricsState {
|
|
||||||
const HetznerMetricsLoaded({
|
|
||||||
required this.period,
|
|
||||||
required this.start,
|
|
||||||
required this.end,
|
|
||||||
required this.stepInSeconds,
|
|
||||||
required this.cpu,
|
|
||||||
required this.ppsIn,
|
|
||||||
required this.ppsOut,
|
|
||||||
required this.bandwidthIn,
|
|
||||||
required this.bandwidthOut,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
final Period period;
|
|
||||||
final DateTime start;
|
|
||||||
final DateTime end;
|
|
||||||
final num stepInSeconds;
|
|
||||||
|
|
||||||
final List<TimeSeriesData> cpu;
|
|
||||||
final List<TimeSeriesData> ppsIn;
|
|
||||||
final List<TimeSeriesData> ppsOut;
|
|
||||||
final List<TimeSeriesData> bandwidthIn;
|
|
||||||
final List<TimeSeriesData> bandwidthOut;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [period, start, end];
|
|
||||||
}
|
|
|
@ -3,16 +3,16 @@ import 'dart:async';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart';
|
import 'package:selfprivacy/logic/cubit/metrics/metrics_repository.dart';
|
||||||
|
|
||||||
part 'hetzner_metrics_state.dart';
|
part 'metrics_state.dart';
|
||||||
|
|
||||||
class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
class MetricsCubit extends Cubit<MetricsState> {
|
||||||
HetznerMetricsCubit() : super(const HetznerMetricsLoading(Period.day));
|
MetricsCubit() : super(const MetricsLoading(Period.day));
|
||||||
|
|
||||||
final HetznerMetricsRepository repository = HetznerMetricsRepository();
|
final MetricsRepository repository = MetricsRepository();
|
||||||
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
||||||
|
|
||||||
void changePeriod(final Period period) async {
|
void changePeriod(final Period period) async {
|
||||||
closeTimer();
|
closeTimer();
|
||||||
emit(HetznerMetricsLoading(period));
|
emit(MetricsLoading(period));
|
||||||
load(period);
|
load(period);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,14 +40,14 @@ class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
||||||
|
|
||||||
void load(final Period period) async {
|
void load(final Period period) async {
|
||||||
try {
|
try {
|
||||||
final HetznerMetricsLoaded newState = await repository.getMetrics(period);
|
final MetricsLoaded newState = await repository.getMetrics(period);
|
||||||
timer = Timer(
|
timer = Timer(
|
||||||
Duration(seconds: newState.stepInSeconds.toInt()),
|
Duration(seconds: newState.metrics.stepsInSecond.toInt()),
|
||||||
() => load(newState.period),
|
() => load(newState.period),
|
||||||
);
|
);
|
||||||
emit(newState);
|
emit(newState);
|
||||||
} on StateError {
|
} on StateError {
|
||||||
print('Tried to emit Hetzner metrics when cubit is closed');
|
print('Tried to emit metrics when cubit is closed');
|
||||||
} on MetricsLoadException {
|
} on MetricsLoadException {
|
||||||
timer = Timer(
|
timer = Timer(
|
||||||
Duration(seconds: state.period.stepPeriodInSeconds),
|
Duration(seconds: state.period.stepPeriodInSeconds),
|
67
lib/logic/cubit/metrics/metrics_repository.dart
Normal file
67
lib/logic/cubit/metrics/metrics_repository.dart
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import 'package:selfprivacy/config/get_it_config.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/common_enum/common_enum.dart';
|
||||||
|
|
||||||
|
import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
|
|
||||||
|
class MetricsLoadException implements Exception {
|
||||||
|
MetricsLoadException(this.message);
|
||||||
|
final String message;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetricsRepository {
|
||||||
|
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<MetricsLoaded> getMetrics(final Period period) async {
|
||||||
|
if (serverProviderApiFactory == null) {
|
||||||
|
_buildServerProviderFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateTime end = DateTime.now();
|
||||||
|
DateTime start;
|
||||||
|
|
||||||
|
switch (period) {
|
||||||
|
case Period.hour:
|
||||||
|
start = end.subtract(const Duration(hours: 1));
|
||||||
|
break;
|
||||||
|
case Period.day:
|
||||||
|
start = end.subtract(const Duration(days: 1));
|
||||||
|
break;
|
||||||
|
case Period.month:
|
||||||
|
start = end.subtract(const Duration(days: 15));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final serverId = getIt<ApiConfigModel>().serverDetails!.id;
|
||||||
|
final ServerMetrics? metrics =
|
||||||
|
await serverProviderApiFactory!.getServerProvider().getMetrics(
|
||||||
|
serverId,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (metrics == null) {
|
||||||
|
throw MetricsLoadException('Metrics data is null');
|
||||||
|
}
|
||||||
|
|
||||||
|
return MetricsLoaded(
|
||||||
|
period: period,
|
||||||
|
metrics: metrics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
31
lib/logic/cubit/metrics/metrics_state.dart
Normal file
31
lib/logic/cubit/metrics/metrics_state.dart
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
part of 'metrics_cubit.dart';
|
||||||
|
|
||||||
|
abstract class MetricsState extends Equatable {
|
||||||
|
const MetricsState();
|
||||||
|
|
||||||
|
abstract final Period period;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetricsLoading extends MetricsState {
|
||||||
|
const MetricsLoading(this.period);
|
||||||
|
@override
|
||||||
|
final Period period;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [period];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MetricsLoaded extends MetricsState {
|
||||||
|
const MetricsLoaded({
|
||||||
|
required this.period,
|
||||||
|
required this.metrics,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final Period period;
|
||||||
|
|
||||||
|
final ServerMetrics metrics;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [period, metrics];
|
||||||
|
}
|
|
@ -98,13 +98,13 @@ class ApiProviderVolumeCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
getIt<NavigationService>().showSnackBar(
|
getIt<NavigationService>().showSnackBar(
|
||||||
'Hetzner resized, waiting 10 seconds',
|
'Provider volume resized, waiting 10 seconds',
|
||||||
);
|
);
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
|
||||||
await ServerApi().resizeVolume(volume.name);
|
await ServerApi().resizeVolume(volume.name);
|
||||||
getIt<NavigationService>().showSnackBar(
|
getIt<NavigationService>().showSnackBar(
|
||||||
'Server api resized, waiting 20 seconds',
|
'Server volume resized, waiting 20 seconds',
|
||||||
);
|
);
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 20));
|
await Future.delayed(const Duration(seconds: 20));
|
||||||
|
|
|
@ -305,14 +305,13 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
timer = Timer(pauseDuration, () async {
|
timer = Timer(pauseDuration, () async {
|
||||||
final ServerHostingDetails hetznerServerDetails =
|
final ServerHostingDetails serverDetails = await repository.restart();
|
||||||
await repository.restart();
|
|
||||||
await repository.saveIsServerResetedFirstTime(true);
|
await repository.saveIsServerResetedFirstTime(true);
|
||||||
await repository.saveServerDetails(hetznerServerDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
|
|
||||||
final ServerInstallationNotFinished newState = dataState.copyWith(
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
isServerResetedFirstTime: true,
|
isServerResetedFirstTime: true,
|
||||||
serverDetails: hetznerServerDetails,
|
serverDetails: serverDetails,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -347,14 +346,13 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
timer = Timer(pauseDuration, () async {
|
timer = Timer(pauseDuration, () async {
|
||||||
final ServerHostingDetails hetznerServerDetails =
|
final ServerHostingDetails serverDetails = await repository.restart();
|
||||||
await repository.restart();
|
|
||||||
await repository.saveIsServerResetedSecondTime(true);
|
await repository.saveIsServerResetedSecondTime(true);
|
||||||
await repository.saveServerDetails(hetznerServerDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
|
|
||||||
final ServerInstallationNotFinished newState = dataState.copyWith(
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
isServerResetedSecondTime: true,
|
isServerResetedSecondTime: true,
|
||||||
serverDetails: hetznerServerDetails,
|
serverDetails: serverDetails,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -560,8 +558,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ServerBasicInfoWithValidators>>
|
Future<List<ServerBasicInfoWithValidators>> getAvailableServers() async {
|
||||||
getServersOnHetznerAccount() async {
|
|
||||||
final ServerInstallationRecovery dataState =
|
final ServerInstallationRecovery dataState =
|
||||||
state as ServerInstallationRecovery;
|
state as ServerInstallationRecovery;
|
||||||
final List<ServerBasicInfo> servers =
|
final List<ServerBasicInfo> servers =
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
class TimeSeriesData {
|
|
||||||
TimeSeriesData(
|
|
||||||
this.secondsSinceEpoch,
|
|
||||||
this.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
final int secondsSinceEpoch;
|
|
||||||
DateTime get time =>
|
|
||||||
DateTime.fromMillisecondsSinceEpoch(secondsSinceEpoch * 1000);
|
|
||||||
final double value;
|
|
||||||
}
|
|
34
lib/logic/models/metrics.dart
Normal file
34
lib/logic/models/metrics.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
class TimeSeriesData {
|
||||||
|
TimeSeriesData(
|
||||||
|
this.secondsSinceEpoch,
|
||||||
|
this.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
final int secondsSinceEpoch;
|
||||||
|
DateTime get time =>
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(secondsSinceEpoch * 1000);
|
||||||
|
final double value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServerMetrics {
|
||||||
|
ServerMetrics({
|
||||||
|
required this.stepsInSecond,
|
||||||
|
required this.cpu,
|
||||||
|
required this.ppsIn,
|
||||||
|
required this.ppsOut,
|
||||||
|
required this.bandwidthIn,
|
||||||
|
required this.bandwidthOut,
|
||||||
|
required this.start,
|
||||||
|
required this.end,
|
||||||
|
});
|
||||||
|
|
||||||
|
final num stepsInSecond;
|
||||||
|
final List<TimeSeriesData> cpu;
|
||||||
|
final List<TimeSeriesData> ppsIn;
|
||||||
|
final List<TimeSeriesData> ppsOut;
|
||||||
|
final List<TimeSeriesData> bandwidthIn;
|
||||||
|
final List<TimeSeriesData> bandwidthOut;
|
||||||
|
|
||||||
|
final DateTime start;
|
||||||
|
final DateTime end;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
String bottomTitle(
|
String bottomTitle(
|
||||||
|
|
|
@ -3,11 +3,11 @@ part of '../server_details_screen.dart';
|
||||||
class _Chart extends StatelessWidget {
|
class _Chart extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(final BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final HetznerMetricsCubit cubit = context.watch<HetznerMetricsCubit>();
|
final MetricsCubit cubit = context.watch<MetricsCubit>();
|
||||||
final Period period = cubit.state.period;
|
final Period period = cubit.state.period;
|
||||||
final HetznerMetricsState state = cubit.state;
|
final MetricsState state = cubit.state;
|
||||||
List<Widget> charts;
|
List<Widget> charts;
|
||||||
if (state is HetznerMetricsLoaded || state is HetznerMetricsLoading) {
|
if (state is MetricsLoaded || state is MetricsLoading) {
|
||||||
charts = [
|
charts = [
|
||||||
FilledCard(
|
FilledCard(
|
||||||
clipped: false,
|
clipped: false,
|
||||||
|
@ -26,10 +26,10 @@ class _Chart extends StatelessWidget {
|
||||||
Stack(
|
Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (state is HetznerMetricsLoaded) getCpuChart(state),
|
if (state is MetricsLoaded) getCpuChart(state),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
opacity: state is HetznerMetricsLoading ? 1 : 0,
|
opacity: state is MetricsLoading ? 1 : 0,
|
||||||
child: const _GraphLoadingCardContent(),
|
child: const _GraphLoadingCardContent(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -72,10 +72,10 @@ class _Chart extends StatelessWidget {
|
||||||
Stack(
|
Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (state is HetznerMetricsLoaded) getBandwidthChart(state),
|
if (state is MetricsLoaded) getBandwidthChart(state),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
opacity: state is HetznerMetricsLoading ? 1 : 0,
|
opacity: state is MetricsLoading ? 1 : 0,
|
||||||
child: const _GraphLoadingCardContent(),
|
child: const _GraphLoadingCardContent(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -122,29 +122,29 @@ class _Chart extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getCpuChart(final HetznerMetricsLoaded state) {
|
Widget getCpuChart(final MetricsLoaded state) {
|
||||||
final data = state.cpu;
|
final data = state.metrics.cpu;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: CpuChart(
|
child: CpuChart(
|
||||||
data: data,
|
data: data,
|
||||||
period: state.period,
|
period: state.period,
|
||||||
start: state.start,
|
start: state.metrics.start,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getBandwidthChart(final HetznerMetricsLoaded state) {
|
Widget getBandwidthChart(final MetricsLoaded state) {
|
||||||
final ppsIn = state.bandwidthIn;
|
final ppsIn = state.metrics.bandwidthIn;
|
||||||
final ppsOut = state.bandwidthOut;
|
final ppsOut = state.metrics.bandwidthOut;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: NetworkChart(
|
child: NetworkChart(
|
||||||
listData: [ppsIn, ppsOut],
|
listData: [ppsIn, ppsOut],
|
||||||
period: state.period,
|
period: state.period,
|
||||||
start: state.start,
|
start: state.metrics.start,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/disk_size.dart';
|
import 'package:selfprivacy/logic/models/disk_size.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/metrics.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
import 'package:selfprivacy/ui/pages/server_details/charts/bottom_title.dart';
|
||||||
|
|
||||||
class NetworkChart extends StatelessWidget {
|
class NetworkChart extends StatelessWidget {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/metrics/metrics_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_detailed_info/server_detailed_info_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_volumes/server_volume_cubit.dart';
|
||||||
|
@ -22,7 +22,6 @@ import 'package:selfprivacy/ui/pages/server_details/charts/cpu_chart.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_details/charts/network_charts.dart';
|
import 'package:selfprivacy/ui/pages/server_details/charts/network_charts.dart';
|
||||||
import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart';
|
import 'package:selfprivacy/ui/pages/server_storage/storage_card.dart';
|
||||||
import 'package:selfprivacy/utils/extensions/duration.dart';
|
import 'package:selfprivacy/utils/extensions/duration.dart';
|
||||||
import 'package:selfprivacy/utils/extensions/string_extensions.dart';
|
|
||||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
import 'package:timezone/timezone.dart';
|
import 'package:timezone/timezone.dart';
|
||||||
|
@ -93,7 +92,7 @@ class _ServerDetailsScreenState extends State<ServerDetailsScreen>
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (final context) => HetznerMetricsCubit()..restart(),
|
create: (final context) => MetricsCubit()..restart(),
|
||||||
child: _Chart(),
|
child: _Chart(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
|
@ -45,9 +45,8 @@ class _RecoveryConfirmServerState extends State<RecoveryConfirmServer> {
|
||||||
hasFlashButton: false,
|
hasFlashButton: false,
|
||||||
children: [
|
children: [
|
||||||
FutureBuilder<List<ServerBasicInfoWithValidators>>(
|
FutureBuilder<List<ServerBasicInfoWithValidators>>(
|
||||||
future: context
|
future:
|
||||||
.read<ServerInstallationCubit>()
|
context.read<ServerInstallationCubit>().getAvailableServers(),
|
||||||
.getServersOnHetznerAccount(),
|
|
||||||
builder: (final context, final snapshot) {
|
builder: (final context, final snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
final servers = snapshot.data;
|
final servers = snapshot.data;
|
||||||
|
|
Loading…
Reference in a new issue