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

Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/69
This commit is contained in:
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; return server == null;
} }
Future<HetznerServerDetails> createServer({ Future<HetznerDataBase> createVolume() async {
required String cloudFlareKey,
required User rootUser,
required String domainName,
}) async {
var client = await getClient(); var client = await getClient();
Response dbCreateResponse = await client.post( Response dbCreateResponse = await client.post(
'/volumes', '/volumes',
data: { data: {
@ -86,9 +81,36 @@ class HetznerApi extends ApiMap {
"format": "ext4" "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 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"] /// add ssh key when you need it: e.g. "ssh_keys":["kherel"]
/// check the branch name, it could be "development" or "master". /// check the branch name, it could be "development" or "master".
@ -106,10 +128,7 @@ class HetznerApi extends ApiMap {
id: serverCreateResponse.data['server']['id'], id: serverCreateResponse.data['server']['id'],
ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'], ip4: serverCreateResponse.data['server']['public_net']['ipv4']['ip'],
createTime: DateTime.now(), createTime: DateTime.now(),
dataBase: HetznerDataBase( dataBase: dataBase,
id: dbId,
name: dbCreateResponse.data['volume']['name'],
),
); );
} }
@ -120,28 +139,22 @@ class HetznerApi extends ApiMap {
Response serversReponse = await client.get('/servers'); Response serversReponse = await client.get('/servers');
List servers = serversReponse.data['servers']; List servers = serversReponse.data['servers'];
var server = servers.firstWhere((el) => el['name'] == domainName); Map server = servers.firstWhere((el) => el['name'] == domainName);
await client.delete('/servers/${server['id']}'); List volumes = server['volumes'];
Response volumesReponse = await client.get('/volumes');
List volumes = volumesReponse.data['volumes'];
var laterFutures = <Future>[]; 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) { for (var volumeId in volumes) {
close(client); await client.post('/volumes/$volumeId/actions/detach');
} else {
Future.wait(laterFutures).then((value) => close(client));
} }
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 { Future<HetznerServerDetails> reset() async {

View file

@ -275,7 +275,18 @@ class AppConfigCubit extends Cubit<AppConfigState> {
await getIt<SSHModel>().clear(); await getIt<SSHModel>().clear();
} }
await repository.deleteRecords(); 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 { void setHetznerKey(String hetznerKey) async {

View file

@ -116,12 +116,16 @@ class AppConfigRepository {
onSuccess, onSuccess,
}) async { }) async {
var hetznerApi = HetznerApi(); var hetznerApi = HetznerApi();
late HetznerDataBase dataBase;
try { try {
dataBase = await hetznerApi.createVolume();
var serverDetails = await hetznerApi.createServer( var serverDetails = await hetznerApi.createServer(
cloudFlareKey: cloudFlareKey, cloudFlareKey: cloudFlareKey,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
dataBase: dataBase,
); );
saveServerDetails(serverDetails); saveServerDetails(serverDetails);
onSuccess(serverDetails); onSuccess(serverDetails);
@ -144,6 +148,7 @@ class AppConfigRepository {
cloudFlareKey: cloudFlareKey, cloudFlareKey: cloudFlareKey,
rootUser: rootUser, rootUser: rootUser,
domainName: domainName, domainName: domainName,
dataBase: dataBase,
); );
await saveServerDetails(serverDetails); await saveServerDetails(serverDetails);
@ -245,10 +250,11 @@ class AppConfigRepository {
var hetznerApi = HetznerApi(); var hetznerApi = HetznerApi();
var cloudFlare = CloudflareApi(); var cloudFlare = CloudflareApi();
hetznerApi.deleteSelfprivacyServerAndAllVolumes( await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
domainName: cloudFlareDomain.domainName, domainName: cloudFlareDomain.domainName,
); );
cloudFlare.removeSimilarRecords(cloudFlareDomain: cloudFlareDomain);
await cloudFlare.removeSimilarRecords(cloudFlareDomain: cloudFlareDomain);
} }
Future<void> deleteRecords() async { Future<void> deleteRecords() async {

View file

@ -1,6 +1,6 @@
part of 'app_config_cubit.dart'; part of 'app_config_cubit.dart';
class AppConfigState extends Equatable { abstract class AppConfigState extends Equatable {
const AppConfigState({ const AppConfigState({
required this.hetznerKey, required this.hetznerKey,
required this.cloudFlareKey, 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) { Widget deleteServer(BuildContext context) {
// todo: need to check var isDisabled =
context.watch<AppConfigCubit>().state.hetznerServer == null;
return Container( return Container(
padding: EdgeInsets.only(top: 20, bottom: 5), padding: EdgeInsets.only(top: 20, bottom: 5),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -157,7 +158,9 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
fontWeight: NamedFontWeight.demiBold, fontWeight: NamedFontWeight.demiBold,
), ),
), ),
onPressed: () { onPressed: isDisabled
? null
: () {
showDialog( showDialog(
context: context, context: context,
builder: (_) { builder: (_) {
@ -169,7 +172,17 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
text: 'modals.7'.tr(), text: 'modals.7'.tr(),
isRed: true, isRed: true,
onPressed: () async { onPressed: () async {
await context.read<AppConfigCubit>().serverDelete(); showDialog(
context: context,
builder: (context) {
return Container(
alignment: Alignment.center,
child: CircularProgressIndicator(),
);
});
await context
.read<AppConfigCubit>()
.serverDelete();
Navigator.of(context).pop(); Navigator.of(context).pop();
}), }),
ActionButton( ActionButton(

View file

@ -1,11 +1,11 @@
part of 'users.dart'; part of 'users.dart';
class _User extends StatelessWidget { 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); : super(key: key);
final User user; final User user;
final bool rootUser; final bool isRootUser;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InkWell( return InkWell(
@ -13,7 +13,7 @@ class _User extends StatelessWidget {
showBrandBottomSheet<void>( showBrandBottomSheet<void>(
context: context, context: context,
builder: (BuildContext 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), SizedBox(width: 20),
Flexible( Flexible(
child: rootUser child: isRootUser
? BrandText.h4Underlined(user.login) ? BrandText.h4Underlined(user.login)
: BrandText.h4(user.login), : BrandText.h4(user.login),
), ),

View file

@ -4,10 +4,11 @@ class _UserDetails extends StatelessWidget {
const _UserDetails({ const _UserDetails({
Key? key, Key? key,
required this.user, required this.user,
required this.isRootUser,
}) : super(key: key); }) : super(key: key);
final User user; final User user;
final bool isRootUser;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var config = context.watch<AppConfigCubit>().state; var config = context.watch<AppConfigCubit>().state;
@ -31,6 +32,7 @@ class _UserDetails extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
if (!isRootUser)
Align( Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Padding( child: Padding(
@ -44,8 +46,6 @@ class _UserDetails extends StatelessWidget {
), ),
onSelected: (PopupMenuItemType result) { onSelected: (PopupMenuItemType result) {
switch (result) { switch (result) {
// case PopupMenuItemType.reset:
// break;
case PopupMenuItemType.delete: case PopupMenuItemType.delete:
showDialog( showDialog(
context: context, context: context,
@ -75,7 +75,9 @@ class _UserDetails extends StatelessWidget {
), ),
), ),
onPressed: () { onPressed: () {
context.read<UsersCubit>().remove(user); context
.read<UsersCubit>()
.remove(user);
Navigator.of(context) Navigator.of(context)
..pop() ..pop()
..pop(); ..pop();

View file

@ -37,7 +37,7 @@ class UsersPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final usersCubitState = context.watch<UsersCubit>().state; final usersCubitState = context.watch<UsersCubit>().state;
var isReady = context.watch<AppConfigCubit>().state is AppConfigFinished; var isReady = context.watch<AppConfigCubit>().state is AppConfigFinished;
final users = usersCubitState.users; final users = [...usersCubitState.users];
//Todo: listen box events //Todo: listen box events
User? user = Hive.box(BNames.appConfig).get(BNames.rootUser); User? user = Hive.box(BNames.appConfig).get(BNames.rootUser);
if (user != null) { if (user != null) {
@ -61,7 +61,7 @@ class UsersPage extends StatelessWidget {
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return _User( return _User(
user: users[index], user: users[index],
rootUser: index == 0, isRootUser: index == 0,
); );
}, },
); );