diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 89722e38..0447dfd3 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -68,13 +68,8 @@ class HetznerApi extends ApiMap { return server == null; } - Future createServer({ - required String cloudFlareKey, - required User rootUser, - required String domainName, - }) async { + Future createVolume() async { var client = await getClient(); - Response dbCreateResponse = await client.post( '/volumes', data: { @@ -86,9 +81,36 @@ class HetznerApi extends ApiMap { "format": "ext4" }, ); + var dbId = dbCreateResponse.data['volume']['id']; + return HetznerDataBase( + id: dbId, + name: dbCreateResponse.data['volume']['name'], + ); + } + + Future createServer({ + required String cloudFlareKey, + required User rootUser, + required String domainName, + required HetznerDataBase dataBase, + }) async { + var client = await getClient(); + + // Response dbCreateResponse = await client.post( + // '/volumes', + // data: { + // "size": 10, + // "name": StringGenerators.dbStorageName(), + // "labels": {"labelkey": "value"}, + // "location": "fsn1", + // "automount": false, + // "format": "ext4" + // }, + // ); var dbPassword = StringGenerators.dbPassword(); - var dbId = dbCreateResponse.data['volume']['id']; + // var dbId = dbCreateResponse.data['volume']['id']; + var dbId = dataBase.id; /// add ssh key when you need it: e.g. "ssh_keys":["kherel"] /// check the branch name, it could be "development" or "master". @@ -106,10 +128,7 @@ class HetznerApi extends ApiMap { 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'], - ), + dataBase: dataBase, ); } @@ -120,28 +139,22 @@ class HetznerApi extends ApiMap { Response serversReponse = await client.get('/servers'); List servers = serversReponse.data['servers']; - var server = servers.firstWhere((el) => el['name'] == domainName); - await client.delete('/servers/${server['id']}'); - - Response volumesReponse = await client.get('/volumes'); - List volumes = volumesReponse.data['volumes']; - + Map server = servers.firstWhere((el) => el['name'] == domainName); + List volumes = server['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)); + for (var volumeId in volumes) { + await client.post('/volumes/$volumeId/actions/detach'); } + await Future.delayed(Duration(seconds: 10)); + + for (var volumeId in volumes) { + laterFutures.add(client.delete('/volumes/$volumeId')); + } + laterFutures.add(client.delete('/servers/${server['id']}')); + + await Future.wait(laterFutures); + close(client); } Future reset() async { diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 668c1ac0..04c0f206 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -53,8 +53,8 @@ class ServerApi extends ApiMap { options: Options( headers: { "X-User": user.login, - "X-Password": - '\$6\$${user.hashPassword.salt}\$${user.hashPassword.hash}', + "X-Password": user.password, + "X-Domain": getIt().cloudFlareDomain!.domainName }, ), ); diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index 9bdf5471..44533817 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -275,7 +275,18 @@ class AppConfigCubit extends Cubit { await getIt().clear(); } await repository.deleteRecords(); - emit(AppConfigEmpty()); + emit(AppConfigNotFinished( + hetznerKey: state.hetznerKey, + cloudFlareDomain: state.cloudFlareDomain, + cloudFlareKey: state.cloudFlareKey, + backblazeCredential: state.backblazeCredential, + rootUser: state.rootUser, + hetznerServer: null, + isServerStarted: false, + isServerResetedFirstTime: false, + isServerResetedSecondTime: false, + isLoading: false, + )); } void setHetznerKey(String hetznerKey) async { diff --git a/lib/logic/cubit/app_config/app_config_repository.dart b/lib/logic/cubit/app_config/app_config_repository.dart index 6e8c3251..312ae7d4 100644 --- a/lib/logic/cubit/app_config/app_config_repository.dart +++ b/lib/logic/cubit/app_config/app_config_repository.dart @@ -116,12 +116,16 @@ class AppConfigRepository { onSuccess, }) async { var hetznerApi = HetznerApi(); + late HetznerDataBase dataBase; try { + dataBase = await hetznerApi.createVolume(); + var serverDetails = await hetznerApi.createServer( cloudFlareKey: cloudFlareKey, rootUser: rootUser, domainName: domainName, + dataBase: dataBase, ); saveServerDetails(serverDetails); onSuccess(serverDetails); @@ -144,6 +148,7 @@ class AppConfigRepository { cloudFlareKey: cloudFlareKey, rootUser: rootUser, domainName: domainName, + dataBase: dataBase, ); await saveServerDetails(serverDetails); @@ -245,10 +250,11 @@ class AppConfigRepository { var hetznerApi = HetznerApi(); var cloudFlare = CloudflareApi(); - hetznerApi.deleteSelfprivacyServerAndAllVolumes( + await hetznerApi.deleteSelfprivacyServerAndAllVolumes( domainName: cloudFlareDomain.domainName, ); - cloudFlare.removeSimilarRecords(cloudFlareDomain: cloudFlareDomain); + + await cloudFlare.removeSimilarRecords(cloudFlareDomain: cloudFlareDomain); } Future deleteRecords() async { diff --git a/lib/logic/cubit/app_config/app_config_state.dart b/lib/logic/cubit/app_config/app_config_state.dart index d5e4b621..1bcf5a04 100644 --- a/lib/logic/cubit/app_config/app_config_state.dart +++ b/lib/logic/cubit/app_config/app_config_state.dart @@ -1,6 +1,6 @@ part of 'app_config_cubit.dart'; -class AppConfigState extends Equatable { +abstract class AppConfigState extends Equatable { const AppConfigState({ required this.hetznerKey, required this.cloudFlareKey, diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index a9a15c99..96cb83ce 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -119,7 +119,7 @@ class _AppSettingsPageState extends State { ], ), ), - // deleteServer(context) + deleteServer(context) ], ), ); @@ -128,7 +128,8 @@ class _AppSettingsPageState extends State { } Widget deleteServer(BuildContext context) { - // todo: need to check + var isDisabled = + context.watch().state.hetznerServer == null; return Container( padding: EdgeInsets.only(top: 20, bottom: 5), decoration: BoxDecoration( @@ -157,29 +158,41 @@ class _AppSettingsPageState extends State { fontWeight: NamedFontWeight.demiBold, ), ), - onPressed: () { - showDialog( - context: context, - builder: (_) { - return BrandAlert( - title: 'modals.3'.tr(), - contentText: 'modals.6'.tr(), - acitons: [ - ActionButton( - text: 'modals.7'.tr(), - isRed: true, - onPressed: () async { - await context.read().serverDelete(); - Navigator.of(context).pop(); - }), - ActionButton( - text: 'basis.cancel'.tr(), - ), - ], - ); - }, - ); - }, + onPressed: isDisabled + ? null + : () { + showDialog( + context: context, + builder: (_) { + return BrandAlert( + title: 'modals.3'.tr(), + contentText: 'modals.6'.tr(), + acitons: [ + ActionButton( + text: 'modals.7'.tr(), + isRed: true, + onPressed: () async { + showDialog( + context: context, + builder: (context) { + return Container( + alignment: Alignment.center, + child: CircularProgressIndicator(), + ); + }); + await context + .read() + .serverDelete(); + Navigator.of(context).pop(); + }), + ActionButton( + text: 'basis.cancel'.tr(), + ), + ], + ); + }, + ); + }, ), ], ), diff --git a/lib/ui/pages/users/user.dart b/lib/ui/pages/users/user.dart index 314f29ec..1d1fb9a8 100644 --- a/lib/ui/pages/users/user.dart +++ b/lib/ui/pages/users/user.dart @@ -1,11 +1,11 @@ part of 'users.dart'; class _User extends StatelessWidget { - const _User({Key? key, required this.user, required this.rootUser}) + const _User({Key? key, required this.user, required this.isRootUser}) : super(key: key); final User user; - final bool rootUser; + final bool isRootUser; @override Widget build(BuildContext context) { return InkWell( @@ -13,7 +13,7 @@ class _User extends StatelessWidget { showBrandBottomSheet( context: context, builder: (BuildContext context) { - return _UserDetails(user: user); + return _UserDetails(user: user, isRootUser: isRootUser); }, ); }, @@ -32,7 +32,7 @@ class _User extends StatelessWidget { ), SizedBox(width: 20), Flexible( - child: rootUser + child: isRootUser ? BrandText.h4Underlined(user.login) : BrandText.h4(user.login), ), diff --git a/lib/ui/pages/users/user_details.dart b/lib/ui/pages/users/user_details.dart index 36116856..869a9004 100644 --- a/lib/ui/pages/users/user_details.dart +++ b/lib/ui/pages/users/user_details.dart @@ -4,10 +4,11 @@ class _UserDetails extends StatelessWidget { const _UserDetails({ Key? key, required this.user, + required this.isRootUser, }) : super(key: key); final User user; - + final bool isRootUser; @override Widget build(BuildContext context) { var config = context.watch().state; @@ -31,86 +32,87 @@ class _UserDetails extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Align( - alignment: Alignment.centerRight, - child: Padding( - padding: EdgeInsets.symmetric( - vertical: 4, - horizontal: 2, - ), - child: PopupMenuButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10.0), + if (!isRootUser) + Align( + alignment: Alignment.centerRight, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: 4, + horizontal: 2, ), - onSelected: (PopupMenuItemType result) { - switch (result) { - // case PopupMenuItemType.reset: - // break; - case PopupMenuItemType.delete: - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text('basis.confirmation'.tr()), - content: SingleChildScrollView( - child: ListBody( - children: [ - Text('users.delete_confirm_question' - .tr()), - ], - ), - ), - actions: [ - TextButton( - child: Text('basis.cancel'.tr()), - onPressed: () { - Navigator.of(context)..pop(); - }, - ), - TextButton( - child: Text( - 'basis.delete'.tr(), - style: TextStyle( - color: BrandColors.red1, - ), + child: PopupMenuButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.0), + ), + onSelected: (PopupMenuItemType result) { + switch (result) { + case PopupMenuItemType.delete: + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text('basis.confirmation'.tr()), + content: SingleChildScrollView( + child: ListBody( + children: [ + Text('users.delete_confirm_question' + .tr()), + ], ), - onPressed: () { - context.read().remove(user); - Navigator.of(context) - ..pop() - ..pop(); - }, ), - ], - ); - }, - ); - break; - } - }, - icon: Icon(Icons.more_vert), - itemBuilder: (BuildContext context) => [ - // PopupMenuItem( - // value: PopupMenuItemType.reset, - // child: Container( - // padding: EdgeInsets.only(left: 5), - // child: Text('users.reset_password'.tr()), - // ), - // ), - PopupMenuItem( - value: PopupMenuItemType.delete, - child: Container( - padding: EdgeInsets.only(left: 5), - child: Text( - 'basis.delete'.tr(), - style: TextStyle(color: BrandColors.red1), + actions: [ + TextButton( + child: Text('basis.cancel'.tr()), + onPressed: () { + Navigator.of(context)..pop(); + }, + ), + TextButton( + child: Text( + 'basis.delete'.tr(), + style: TextStyle( + color: BrandColors.red1, + ), + ), + onPressed: () { + context + .read() + .remove(user); + Navigator.of(context) + ..pop() + ..pop(); + }, + ), + ], + ); + }, + ); + break; + } + }, + icon: Icon(Icons.more_vert), + itemBuilder: (BuildContext context) => [ + // PopupMenuItem( + // value: PopupMenuItemType.reset, + // child: Container( + // padding: EdgeInsets.only(left: 5), + // child: Text('users.reset_password'.tr()), + // ), + // ), + PopupMenuItem( + value: PopupMenuItemType.delete, + child: Container( + padding: EdgeInsets.only(left: 5), + child: Text( + 'basis.delete'.tr(), + style: TextStyle(color: BrandColors.red1), + ), ), ), - ), - ], + ], + ), ), ), - ), Spacer(), Padding( padding: EdgeInsets.symmetric( diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index bc878a98..c68e56e7 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -37,7 +37,7 @@ class UsersPage extends StatelessWidget { Widget build(BuildContext context) { final usersCubitState = context.watch().state; var isReady = context.watch().state is AppConfigFinished; - final users = usersCubitState.users; + final users = [...usersCubitState.users]; //Todo: listen box events User? user = Hive.box(BNames.appConfig).get(BNames.rootUser); if (user != null) { @@ -61,7 +61,7 @@ class UsersPage extends StatelessWidget { itemBuilder: (BuildContext context, int index) { return _User( user: users[index], - rootUser: index == 0, + isRootUser: index == 0, ); }, );