From ae3ec309cbf0da0eea5f63fb84d82033fac77744 Mon Sep 17 00:00:00 2001 From: Kherel Date: Tue, 30 Mar 2021 19:38:40 +0200 Subject: [PATCH] add volume --- lib/config/hive_config.dart | 1 + lib/logic/api_maps/hetzner.dart | 73 ++++++++++++++++--- .../cubit/app_config/app_config_cubit.dart | 7 +- .../app_config/app_config_repository.dart | 2 +- .../cubit/app_config/app_config_state.dart | 2 + lib/logic/get_it/timer.dart | 2 +- lib/logic/models/server_details.dart | 22 +++++- lib/logic/models/server_details.g.dart | 48 +++++++++++- lib/ui/pages/initializing/initializing.dart | 5 +- lib/utils/password_generator2.dart | 6 +- 10 files changed, 141 insertions(+), 27 deletions(-) diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 17a10fb3b1..67d175f427 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -16,6 +16,7 @@ class HiveConfig { Hive.registerAdapter(HetznerServerDetailsAdapter()); Hive.registerAdapter(CloudFlareDomainAdapter()); Hive.registerAdapter(BackblazeCredentialAdapter()); + Hive.registerAdapter(HetznerDataBaseAdapter()); await Hive.openBox(BNames.appSettings); var cipher = HiveAesCipher(await getEncriptedKey()); diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index a9cad08623..f7f6d46a8f 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -55,6 +55,19 @@ class HetznerApi extends ApiMap { } } + Future isFreeToCreate() async { + var client = await getClient(); + + Response serversReponse = await client.get('/servers'); + List servers = serversReponse.data['servers']; + var server = servers.firstWhere( + (el) => el['name'] == 'selfprivacy-server', + orElse: null, + ); + client.close(); + return server == null; + } + Future createServer({ required String cloudFlareKey, required User rootUser, @@ -62,31 +75,71 @@ class HetznerApi extends ApiMap { }) async { var dbPassword = getRandomString(40); + const chars = + 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; + + var dbStorageName = getRandomString(6, chars); + var client = await getClient(); + + Response dbCreateResponse = await client.post( + '/volumes', + data: { + "size": 10, + "name": dbStorageName, + "labels": {"labelkey": "value"}, + "location": "fsn1", + "automount": false, + "format": "ext4" + }, + ); + var dbId = dbCreateResponse.data['volume']['id']; var data = jsonDecode( - '''{"name":"selfprivacy-server","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":false}''', + '''{"name":"selfprivacy-server","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId],"networks":[],"user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''', ); - var client = await getClient(); - Response response = await client.post( + Response serverCreateResponse = await client.post( '/servers', data: data, ); client.close(); return HetznerServerDetails( - id: response.data['server']['id'], - ip4: response.data['server']['public_net']['ipv4']['ip'], + id: serverCreateResponse.data['server']['id'], + ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'], createTime: DateTime.now(), + dataBase: HetznerDataBase( + id: dbId, + name: dbCreateResponse.data['volume']['name'], + ), ); } - Future deleteSelfprivacyServer() async { + Future deleteSelfprivacyServerAndAllVolumes() async { var client = await getClient(); - Response response = await client.get('/servers'); - List list = response.data['servers']; - var server = list.firstWhere((el) => el['name'] == 'selfprivacy-server'); + Response serversReponse = await client.get('/servers'); + List servers = serversReponse.data['servers']; + var server = servers.firstWhere((el) => el['name'] == 'selfprivacy-server'); await client.delete('/servers/${server['id']}'); - close(client); + + Response volumesReponse = await client.get('/volumes'); + List volumes = volumesReponse.data['volumes']; + + var laterFutures = []; + for (var volume in volumes) { + if (volume['server'] == null) { + await client.delete('/volumes/${volume['id']}'); + } else { + laterFutures.add(Future.delayed(Duration(seconds: 60)).then( + (_) => client.delete('/volumes/${volume['id']}'), + )); + } + } + + if (laterFutures.isEmpty) { + close(client); + } else { + Future.wait(laterFutures).then((value) => close(client)); + } } Future startServer({ diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 15e5f1faee..10ebeb6849 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -202,7 +202,7 @@ class AppConfigCubit extends Cubit { } void clearAppConfig() { - _closeTimer(); + closeTimer(); repository.clearAppConfig(); emit(InitialAppConfigState()); } @@ -263,17 +263,16 @@ class AppConfigCubit extends Cubit { onSuccess: onSuccess, ); } catch (e) { - addError(e); emit(_stateCopy); } } close() { - _closeTimer(); + closeTimer(); return super.close(); } - void _closeTimer() { + void closeTimer() { if (timer != null && timer!.isActive) { timer!.cancel(); } diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index e4197d06fd..0d2aae5d6f 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -119,7 +119,7 @@ class AppConfigRepository { text: 'basis.delete'.tr(), isRed: true, onPressed: () async { - await hetznerApi.deleteSelfprivacyServer(); + await hetznerApi.deleteSelfprivacyServerAndAllVolumes(); var serverDetails = await hetznerApi.createServer( cloudFlareKey: cloudFlareKey, diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index 5a89ed042d..5a22c29a48 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -92,6 +92,8 @@ class AppConfigState extends Equatable { isServerReseted, hasFinalChecked, ]; + + print(res); return res; } } diff --git a/lib/logic/get_it/timer.dart b/lib/logic/get_it/timer.dart index 0444b44edb..e91d3a2011 100644 --- a/lib/logic/get_it/timer.dart +++ b/lib/logic/get_it/timer.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class TimerModel extends ChangeNotifier { DateTime _time = DateTime.now(); - DateTime get messages => _time; + DateTime get time => _time; void restart() { _time = DateTime.now(); diff --git a/lib/logic/models/server_details.dart b/lib/logic/models/server_details.dart index e014de1434..4afbc5cd52 100644 --- a/lib/logic/models/server_details.dart +++ b/lib/logic/models/server_details.dart @@ -8,14 +8,15 @@ class HetznerServerDetails { required this.ip4, required this.id, required this.createTime, + required this.dataBase, this.startTime, }); @HiveField(0) - final String? ip4; + final String ip4; @HiveField(1) - final int? id; + final int id; @HiveField(3) final DateTime? createTime; @@ -23,14 +24,31 @@ class HetznerServerDetails { @HiveField(2) final DateTime? startTime; + @HiveField(4) + final HetznerDataBase dataBase; + HetznerServerDetails copyWith({DateTime? startTime}) { return HetznerServerDetails( startTime: startTime ?? this.startTime, createTime: createTime, id: id, ip4: ip4, + dataBase: dataBase, ); } String toString() => id.toString(); } + +@HiveType(typeId: 5) +class HetznerDataBase { + HetznerDataBase({ + required this.id, + required this.name, + }); + + @HiveField(1) + int id; + @HiveField(2) + String name; +} diff --git a/lib/logic/models/server_details.g.dart b/lib/logic/models/server_details.g.dart index 0f08e2f248..d59c90206e 100644 --- a/lib/logic/models/server_details.g.dart +++ b/lib/logic/models/server_details.g.dart @@ -17,9 +17,10 @@ class HetznerServerDetailsAdapter extends TypeAdapter { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return HetznerServerDetails( - ip4: fields[0] as String?, - id: fields[1] as int?, + ip4: fields[0] as String, + id: fields[1] as int, createTime: fields[3] as DateTime?, + dataBase: fields[4] as HetznerDataBase, startTime: fields[2] as DateTime?, ); } @@ -27,7 +28,7 @@ class HetznerServerDetailsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, HetznerServerDetails obj) { writer - ..writeByte(4) + ..writeByte(5) ..writeByte(0) ..write(obj.ip4) ..writeByte(1) @@ -35,7 +36,9 @@ class HetznerServerDetailsAdapter extends TypeAdapter { ..writeByte(3) ..write(obj.createTime) ..writeByte(2) - ..write(obj.startTime); + ..write(obj.startTime) + ..writeByte(4) + ..write(obj.dataBase); } @override @@ -48,3 +51,40 @@ class HetznerServerDetailsAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +class HetznerDataBaseAdapter extends TypeAdapter { + @override + final int typeId = 5; + + @override + HetznerDataBase read(BinaryReader reader) { + final numOfFields = reader.readByte(); + final fields = { + for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), + }; + return HetznerDataBase( + id: fields[1] as int, + name: fields[2] as String, + ); + } + + @override + void write(BinaryWriter writer, HetznerDataBase obj) { + writer + ..writeByte(2) + ..writeByte(1) + ..write(obj.id) + ..writeByte(2) + ..write(obj.name); + } + + @override + int get hashCode => typeId.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is HetznerDataBaseAdapter && + runtimeType == other.runtimeType && + typeId == other.typeId; +} diff --git a/lib/ui/pages/initializing/initializing.dart b/lib/ui/pages/initializing/initializing.dart index 589ce6540b..ed41b6f55f 100644 --- a/lib/ui/pages/initializing/initializing.dart +++ b/lib/ui/pages/initializing/initializing.dart @@ -407,8 +407,9 @@ class InitializingPage extends StatelessWidget { BrandText.body2('initializing.11'.tr()), Spacer(), BrandButton.rised( - onPressed: - isLoading! ? null : () => appConfigCubit.createServerAndSetDnsRecords(), + onPressed: isLoading! + ? null + : () => appConfigCubit.createServerAndSetDnsRecords(), title: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(), ), Spacer(flex: 2), diff --git a/lib/utils/password_generator2.dart b/lib/utils/password_generator2.dart index cea7895c9a..36114d5574 100644 --- a/lib/utils/password_generator2.dart +++ b/lib/utils/password_generator2.dart @@ -4,11 +4,11 @@ const _chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_'; Random _rnd = Random(); -String getRandomString(int length) => String.fromCharCodes( +String getRandomString(int length, [chars = _chars]) => String.fromCharCodes( Iterable.generate( length, - (_) => _chars.codeUnitAt( - _rnd.nextInt(_chars.length), + (_) => chars.codeUnitAt( + _rnd.nextInt(chars.length), ), ), );