Merge pull request 'fix' (#69) from fix-deleting-server into no-hash

Reviewed-on: kherel/selfprivacy.org.app#69
pull/70/head
kherel 2021-10-12 00:15:19 +03:00
commit d414b22251
8 changed files with 184 additions and 139 deletions

View File

@ -68,13 +68,8 @@ class HetznerApi extends ApiMap {
return server == null;
}
Future<HetznerServerDetails> createServer({
required String cloudFlareKey,
required User rootUser,
required String domainName,
}) async {
Future<HetznerDataBase> 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<HetznerServerDetails> 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 = <Future>[];
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<HetznerServerDetails> reset() async {

View File

@ -275,7 +275,18 @@ class AppConfigCubit extends Cubit<AppConfigState> {
await getIt<SSHModel>().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 {

View File

@ -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<void> deleteRecords() async {

View File

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

View File

@ -119,7 +119,7 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
],
),
),
// deleteServer(context)
deleteServer(context)
],
),
);
@ -128,7 +128,8 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
}
Widget deleteServer(BuildContext context) {
// todo: need to check
var isDisabled =
context.watch<AppConfigCubit>().state.hetznerServer == null;
return Container(
padding: EdgeInsets.only(top: 20, bottom: 5),
decoration: BoxDecoration(
@ -157,29 +158,41 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
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<AppConfigCubit>().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<AppConfigCubit>()
.serverDelete();
Navigator.of(context).pop();
}),
ActionButton(
text: 'basis.cancel'.tr(),
),
],
);
},
);
},
),
],
),

View File

@ -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<void>(
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),
),

View File

@ -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<AppConfigCubit>().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<PopupMenuItemType>(
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: <Widget>[
Text('users.delete_confirm_question'
.tr()),
],
),
),
actions: <Widget>[
TextButton(
child: Text('basis.cancel'.tr()),
onPressed: () {
Navigator.of(context)..pop();
},
),
TextButton(
child: Text(
'basis.delete'.tr(),
style: TextStyle(
color: BrandColors.red1,
),
child: PopupMenuButton<PopupMenuItemType>(
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: <Widget>[
Text('users.delete_confirm_question'
.tr()),
],
),
onPressed: () {
context.read<UsersCubit>().remove(user);
Navigator.of(context)
..pop()
..pop();
},
),
],
);
},
);
break;
}
},
icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => [
// PopupMenuItem<PopupMenuItemType>(
// value: PopupMenuItemType.reset,
// child: Container(
// padding: EdgeInsets.only(left: 5),
// child: Text('users.reset_password'.tr()),
// ),
// ),
PopupMenuItem<PopupMenuItemType>(
value: PopupMenuItemType.delete,
child: Container(
padding: EdgeInsets.only(left: 5),
child: Text(
'basis.delete'.tr(),
style: TextStyle(color: BrandColors.red1),
actions: <Widget>[
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<UsersCubit>()
.remove(user);
Navigator.of(context)
..pop()
..pop();
},
),
],
);
},
);
break;
}
},
icon: Icon(Icons.more_vert),
itemBuilder: (BuildContext context) => [
// PopupMenuItem<PopupMenuItemType>(
// value: PopupMenuItemType.reset,
// child: Container(
// padding: EdgeInsets.only(left: 5),
// child: Text('users.reset_password'.tr()),
// ),
// ),
PopupMenuItem<PopupMenuItemType>(
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(

View File

@ -37,7 +37,7 @@ class UsersPage extends StatelessWidget {
Widget build(BuildContext context) {
final usersCubitState = context.watch<UsersCubit>().state;
var isReady = context.watch<AppConfigCubit>().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,
);
},
);