diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 4de4f36f..3345633f 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -56,24 +56,174 @@ class HetznerApi extends ApiMap { } } - Future createVolume() async { + Future createVolume() async { + ServerVolume? volume; + + final Response dbCreateResponse; final Dio client = await getClient(); - final Response dbCreateResponse = await client.post( - '/volumes', - data: { - 'size': 10, - 'name': StringGenerators.dbStorageName(), - 'labels': {'labelkey': 'value'}, - 'location': 'fsn1', - 'automount': false, - 'format': 'ext4' - }, - ); - final dbId = dbCreateResponse.data['volume']['id']; - return ServerVolume( - id: dbId, - name: dbCreateResponse.data['volume']['name'], - ); + try { + dbCreateResponse = await client.post( + '/volumes', + data: { + 'size': 10, + 'name': StringGenerators.dbStorageName(), + 'labels': {'labelkey': 'value'}, + 'location': 'fsn1', + 'automount': false, + 'format': 'ext4' + }, + ); + final dbId = dbCreateResponse.data['volume']['id']; + final dbSize = dbCreateResponse.data['volume']['size']; + final dbServer = dbCreateResponse.data['volume']['server']; + final dbName = dbCreateResponse.data['volume']['name']; + volume = ServerVolume( + id: dbId, + name: dbName, + sizeByte: dbSize, + serverId: dbServer, + ); + } catch (e) { + print(e); + } finally { + client.close(); + } + + return volume; + } + + Future> getVolumes({final String? status}) async { + final List volumes = []; + + final Response dbGetResponse; + final Dio client = await getClient(); + try { + dbGetResponse = await client.get( + '/volumes', + queryParameters: { + 'status': status, + }, + ); + final List rawVolumes = dbGetResponse.data['volumes']; + for (final rawVolume in rawVolumes) { + final int dbId = rawVolume['id']; + final int dbSize = rawVolume['size']; + final dbServer = rawVolume['server']; + final String dbName = rawVolume['name']; + final volume = ServerVolume( + id: dbId, + name: dbName, + sizeByte: dbSize, + serverId: dbServer, + ); + volumes.add(volume); + } + } catch (e) { + print(e); + } finally { + client.close(); + } + + return volumes; + } + + Future getVolume(final int id) async { + ServerVolume? volume; + + final Response dbGetResponse; + final Dio client = await getClient(); + try { + dbGetResponse = await client.get('/volumes/$id'); + final int dbId = dbGetResponse.data['volume']['id']; + final int dbSize = dbGetResponse.data['volume']['size']; + final int dbServer = dbGetResponse.data['volume']['server']; + final String dbName = dbGetResponse.data['volume']['name']; + volume = ServerVolume( + id: dbId, + name: dbName, + sizeByte: dbSize, + serverId: dbServer, + ); + } catch (e) { + print(e); + } finally { + client.close(); + } + + return volume; + } + + void deleteVolume(final int id) async { + final Dio client = await getClient(); + try { + await client.delete('/volumes/$id'); + } catch (e) { + print(e); + } finally { + client.close(); + } + } + + Future attachVolume(final int volumeId, final int serverId) async { + bool success = false; + + final Response dbPostResponse; + final Dio client = await getClient(); + try { + dbPostResponse = await client.post( + '/volumes/$volumeId/actions/attach', + data: { + 'automount': true, + 'server': serverId, + }, + ); + success = dbPostResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + } finally { + client.close(); + } + + return success; + } + + Future detachVolume(final int volumeId) async { + bool success = false; + + final Response dbPostResponse; + final Dio client = await getClient(); + try { + dbPostResponse = await client.post('/volumes/$volumeId/actions/detach'); + success = dbPostResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + } finally { + client.close(); + } + + return success; + } + + Future resizeVolume(final int volumeId, final int sizeGb) async { + bool success = false; + + final Response dbPostResponse; + final Dio client = await getClient(); + try { + dbPostResponse = await client.post( + '/volumes/$volumeId/actions/resize', + data: { + 'size': sizeGb, + }, + ); + success = dbPostResponse.data['action']['status'].toString() != 'error'; + } catch (e) { + print(e); + } finally { + client.close(); + } + + return success; } Future createServer({ diff --git a/lib/logic/cubit/devices/devices_cubit.dart b/lib/logic/cubit/devices/devices_cubit.dart index f0380635..a7e80e17 100644 --- a/lib/logic/cubit/devices/devices_cubit.dart +++ b/lib/logic/cubit/devices/devices_cubit.dart @@ -16,17 +16,16 @@ class ApiDevicesCubit @override void load() async { if (serverInstallationCubit.state is ServerInstallationFinished) { - final List? devices = await _getApiTokens(); - if (devices != null) { - emit(ApiDevicesState(devices, LoadingStatus.success)); - } else { - emit(const ApiDevicesState([], LoadingStatus.error)); - } + _refetch(); } } Future refresh() async { emit(const ApiDevicesState([], LoadingStatus.refreshing)); + _refetch(); + } + + void _refetch() async { final List? devices = await _getApiTokens(); if (devices != null) { emit(ApiDevicesState(devices, LoadingStatus.success)); diff --git a/lib/logic/cubit/server_installation/server_installation_cubit.dart b/lib/logic/cubit/server_installation/server_installation_cubit.dart index 05198229..30bc828b 100644 --- a/lib/logic/cubit/server_installation/server_installation_cubit.dart +++ b/lib/logic/cubit/server_installation/server_installation_cubit.dart @@ -491,6 +491,8 @@ class ServerInstallationCubit extends Cubit { volume: ServerVolume( id: server.volumeId, name: 'recovered_volume', + sizeByte: 0, + serverId: server.id, ), apiToken: dataState.serverDetails!.apiToken, provider: ServerProvider.hetzner, diff --git a/lib/logic/cubit/server_installation/server_installation_repository.dart b/lib/logic/cubit/server_installation/server_installation_repository.dart index e6eaf3c6..842c1db2 100644 --- a/lib/logic/cubit/server_installation/server_installation_repository.dart +++ b/lib/logic/cubit/server_installation/server_installation_repository.dart @@ -212,7 +212,13 @@ class ServerInstallationRepository { late ServerVolume dataBase; try { - dataBase = await hetznerApi.createVolume(); + 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, @@ -220,6 +226,7 @@ class ServerInstallationRepository { domainName: domainName, dataBase: dataBase, ); + if (serverDetails == null) { print('Server is not initialized!'); return; @@ -444,6 +451,8 @@ class ServerInstallationRepository { volume: ServerVolume( id: 0, name: '', + sizeByte: 0, + serverId: 0, ), provider: ServerProvider.unknown, id: 0, @@ -478,6 +487,8 @@ class ServerInstallationRepository { volume: ServerVolume( id: 0, name: '', + sizeByte: 0, + serverId: 0, ), provider: ServerProvider.unknown, id: 0, @@ -512,6 +523,8 @@ class ServerInstallationRepository { volume: ServerVolume( id: 0, name: '', + serverId: 0, + sizeByte: 0, ), provider: ServerProvider.unknown, id: 0, @@ -537,6 +550,8 @@ class ServerInstallationRepository { volume: ServerVolume( id: 0, name: '', + sizeByte: 0, + serverId: 0, ), provider: ServerProvider.unknown, id: 0, diff --git a/lib/logic/cubit/volumes/volumes_cubit.dart b/lib/logic/cubit/volumes/volumes_cubit.dart new file mode 100644 index 00000000..8fcc6d55 --- /dev/null +++ b/lib/logic/cubit/volumes/volumes_cubit.dart @@ -0,0 +1,70 @@ +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/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'; + +part 'volumes_state.dart'; + +class ApiVolumesCubit + extends ServerInstallationDependendCubit { + ApiVolumesCubit(final ServerInstallationCubit serverInstallationCubit) + : super(serverInstallationCubit, const ApiVolumesState.initial()); + + final ServerApi api = ServerApi(); + + @override + void load() async { + if (serverInstallationCubit.state is ServerInstallationFinished) { + _refetch(); + } + } + + void refresh() async { + emit(const ApiVolumesState([], LoadingStatus.refreshing)); + _refetch(); + } + + void _refetch() async { + final List volumes = await HetznerApi().getVolumes(); + if (volumes.isNotEmpty) { + emit(ApiVolumesState(volumes, LoadingStatus.success)); + } else { + emit(const ApiVolumesState([], LoadingStatus.error)); + } + } + + void attachVolume(final ServerVolume volume) async { + final ServerHostingDetails server = getIt().serverDetails!; + HetznerApi().attachVolume(volume.id, server.id); + refresh(); + } + + void detachVolume(final ServerVolume volume) async { + HetznerApi().detachVolume(volume.id); + refresh(); + } + + void resizeVolume(final ServerVolume volume, final int newSizeGb) { + if (volume.sizeByte < newSizeGb) { + HetznerApi().resizeVolume(volume.id, newSizeGb); + refresh(); + } + } + + void createVolume() async { + HetznerApi().createVolume(); + refresh(); + } + + void deleteVolume(final ServerVolume volume) async { + HetznerApi().deleteVolume(volume.id); + refresh(); + } + + @override + void clear() { + emit(const ApiVolumesState.initial()); + } +} diff --git a/lib/logic/cubit/volumes/volumes_state.dart b/lib/logic/cubit/volumes/volumes_state.dart new file mode 100644 index 00000000..cdc14272 --- /dev/null +++ b/lib/logic/cubit/volumes/volumes_state.dart @@ -0,0 +1,23 @@ +part of 'volumes_cubit.dart'; + +class ApiVolumesState extends ServerInstallationDependendState { + const ApiVolumesState(this._volumes, this.status); + + const ApiVolumesState.initial() : this(const [], LoadingStatus.uninitialized); + final List _volumes; + final LoadingStatus status; + + List get volumes => _volumes; + + ApiVolumesState copyWith({ + final List? volumes, + final LoadingStatus? status, + }) => + ApiVolumesState( + volumes ?? _volumes, + status ?? this.status, + ); + + @override + List get props => [_volumes]; +} diff --git a/lib/logic/models/hive/server_details.dart b/lib/logic/models/hive/server_details.dart index 5188e62e..44c8d5b3 100644 --- a/lib/logic/models/hive/server_details.dart +++ b/lib/logic/models/hive/server_details.dart @@ -55,12 +55,18 @@ class ServerVolume { ServerVolume({ required this.id, required this.name, + required this.sizeByte, + required this.serverId, }); @HiveField(1) int id; @HiveField(2) String name; + @HiveField(3, defaultValue: 10737418240) // 10 Gb + int sizeByte; + @HiveField(4, defaultValue: null) + int? serverId; } @HiveType(typeId: 101)