mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2024-11-17 22:29:15 +00:00
Merge pull request 'no-hash' (#70) from no-hash into master
Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/70
This commit is contained in:
commit
bdf5a3b1cc
|
@ -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 {
|
||||
|
|
|
@ -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<ApiConfigModel>().cloudFlareDomain!.domainName
|
||||
},
|
||||
),
|
||||
);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue