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 Dio client = await getClient();
final Response dbCreateResponse = await client.post( try {
'/volumes', dbCreateResponse = await client.post(
data: { '/volumes',
'size': 10, data: {
'name': StringGenerators.dbStorageName(), 'size': 10,
'labels': {'labelkey': 'value'}, 'name': StringGenerators.dbStorageName(),
'location': 'fsn1', 'labels': {'labelkey': 'value'},
'automount': false, 'location': 'fsn1',
'format': 'ext4' 'automount': false,
}, 'format': 'ext4'
); },
final dbId = dbCreateResponse.data['volume']['id']; );
return ServerVolume( final dbId = dbCreateResponse.data['volume']['id'];
id: dbId, final dbSize = dbCreateResponse.data['volume']['size'];
name: dbCreateResponse.data['volume']['name'], 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({ Future<ServerHostingDetails?> createServer({

View File

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

View File

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

View File

@ -212,7 +212,13 @@ class ServerInstallationRepository {
late ServerVolume dataBase; late ServerVolume dataBase;
try { 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( final ServerHostingDetails? serverDetails = await hetznerApi.createServer(
cloudFlareKey: cloudFlareKey, cloudFlareKey: cloudFlareKey,
@ -220,6 +226,7 @@ class ServerInstallationRepository {
domainName: domainName, domainName: domainName,
dataBase: dataBase, dataBase: dataBase,
); );
if (serverDetails == null) { if (serverDetails == null) {
print('Server is not initialized!'); print('Server is not initialized!');
return; return;
@ -444,6 +451,8 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
sizeByte: 0,
serverId: 0,
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, id: 0,
@ -478,6 +487,8 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
sizeByte: 0,
serverId: 0,
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, id: 0,
@ -512,6 +523,8 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
serverId: 0,
sizeByte: 0,
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, id: 0,
@ -537,6 +550,8 @@ class ServerInstallationRepository {
volume: ServerVolume( volume: ServerVolume(
id: 0, id: 0,
name: '', name: '',
sizeByte: 0,
serverId: 0,
), ),
provider: ServerProvider.unknown, provider: ServerProvider.unknown,
id: 0, 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({ ServerVolume({
required this.id, required this.id,
required this.name, required this.name,
required this.sizeByte,
required this.serverId,
}); });
@HiveField(1) @HiveField(1)
int id; int id;
@HiveField(2) @HiveField(2)
String name; String name;
@HiveField(3, defaultValue: 10737418240) // 10 Gb
int sizeByte;
@HiveField(4, defaultValue: null)
int? serverId;
} }
@HiveType(typeId: 101) @HiveType(typeId: 101)