Merge pull request 'volumes-hetzner' (#97) from volumes-hetzner into develop

Reviewed-on: kherel/selfprivacy.org.app#97
pull/99/head
Inex Code 2022-07-04 02:16:25 +03:00
commit f40749ca57
7 changed files with 289 additions and 24 deletions

View File

@ -56,24 +56,174 @@ class HetznerApi extends ApiMap {
}
}
Future<ServerVolume> createVolume() async {
Future<ServerVolume?> 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<List<ServerVolume>> getVolumes({final String? status}) async {
final List<ServerVolume> volumes = [];
final Response dbGetResponse;
final Dio client = await getClient();
try {
dbGetResponse = await client.get(
'/volumes',
queryParameters: {
'status': status,
},
);
final List<dynamic> 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<ServerVolume?> 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<bool> 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<bool> 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<bool> 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<ServerHostingDetails?> createServer({

View File

@ -16,17 +16,16 @@ class ApiDevicesCubit
@override
void load() async {
if (serverInstallationCubit.state is ServerInstallationFinished) {
final List<ApiToken>? devices = await _getApiTokens();
if (devices != null) {
emit(ApiDevicesState(devices, LoadingStatus.success));
} else {
emit(const ApiDevicesState([], LoadingStatus.error));
}
_refetch();
}
}
Future<void> refresh() async {
emit(const ApiDevicesState([], LoadingStatus.refreshing));
_refetch();
}
void _refetch() async {
final List<ApiToken>? devices = await _getApiTokens();
if (devices != null) {
emit(ApiDevicesState(devices, LoadingStatus.success));

View File

@ -491,6 +491,8 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
volume: ServerVolume(
id: server.volumeId,
name: 'recovered_volume',
sizeByte: 0,
serverId: server.id,
),
apiToken: dataState.serverDetails!.apiToken,
provider: ServerProvider.hetzner,

View File

@ -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,

View File

@ -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<ApiVolumesState> {
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<ServerVolume> 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<ApiConfigModel>().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());
}
}

View File

@ -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<ServerVolume> _volumes;
final LoadingStatus status;
List<ServerVolume> get volumes => _volumes;
ApiVolumesState copyWith({
final List<ServerVolume>? volumes,
final LoadingStatus? status,
}) =>
ApiVolumesState(
volumes ?? _volumes,
status ?? this.status,
);
@override
List<Object?> get props => [_volumes];
}

View File

@ -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)