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;
|
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 {
|
||||||
|
|
|
@ -53,8 +53,8 @@ class ServerApi extends ApiMap {
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {
|
headers: {
|
||||||
"X-User": user.login,
|
"X-User": user.login,
|
||||||
"X-Password":
|
"X-Password": user.password,
|
||||||
'\$6\$${user.hashPassword.salt}\$${user.hashPassword.hash}',
|
"X-Domain": getIt<ApiConfigModel>().cloudFlareDomain!.domainName
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,29 +158,41 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
fontWeight: NamedFontWeight.demiBold,
|
fontWeight: NamedFontWeight.demiBold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: isDisabled
|
||||||
showDialog(
|
? null
|
||||||
context: context,
|
: () {
|
||||||
builder: (_) {
|
showDialog(
|
||||||
return BrandAlert(
|
context: context,
|
||||||
title: 'modals.3'.tr(),
|
builder: (_) {
|
||||||
contentText: 'modals.6'.tr(),
|
return BrandAlert(
|
||||||
acitons: [
|
title: 'modals.3'.tr(),
|
||||||
ActionButton(
|
contentText: 'modals.6'.tr(),
|
||||||
text: 'modals.7'.tr(),
|
acitons: [
|
||||||
isRed: true,
|
ActionButton(
|
||||||
onPressed: () async {
|
text: 'modals.7'.tr(),
|
||||||
await context.read<AppConfigCubit>().serverDelete();
|
isRed: true,
|
||||||
Navigator.of(context).pop();
|
onPressed: () async {
|
||||||
}),
|
showDialog(
|
||||||
ActionButton(
|
context: context,
|
||||||
text: 'basis.cancel'.tr(),
|
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';
|
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),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,86 +32,87 @@ class _UserDetails extends StatelessWidget {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Align(
|
if (!isRootUser)
|
||||||
alignment: Alignment.centerRight,
|
Align(
|
||||||
child: Padding(
|
alignment: Alignment.centerRight,
|
||||||
padding: EdgeInsets.symmetric(
|
child: Padding(
|
||||||
vertical: 4,
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: 2,
|
vertical: 4,
|
||||||
),
|
horizontal: 2,
|
||||||
child: PopupMenuButton<PopupMenuItemType>(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
|
||||||
),
|
),
|
||||||
onSelected: (PopupMenuItemType result) {
|
child: PopupMenuButton<PopupMenuItemType>(
|
||||||
switch (result) {
|
shape: RoundedRectangleBorder(
|
||||||
// case PopupMenuItemType.reset:
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
// break;
|
),
|
||||||
case PopupMenuItemType.delete:
|
onSelected: (PopupMenuItemType result) {
|
||||||
showDialog(
|
switch (result) {
|
||||||
context: context,
|
case PopupMenuItemType.delete:
|
||||||
builder: (context) {
|
showDialog(
|
||||||
return AlertDialog(
|
context: context,
|
||||||
title: Text('basis.confirmation'.tr()),
|
builder: (context) {
|
||||||
content: SingleChildScrollView(
|
return AlertDialog(
|
||||||
child: ListBody(
|
title: Text('basis.confirmation'.tr()),
|
||||||
children: <Widget>[
|
content: SingleChildScrollView(
|
||||||
Text('users.delete_confirm_question'
|
child: ListBody(
|
||||||
.tr()),
|
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,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onPressed: () {
|
|
||||||
context.read<UsersCubit>().remove(user);
|
|
||||||
Navigator.of(context)
|
|
||||||
..pop()
|
|
||||||
..pop();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
actions: <Widget>[
|
||||||
);
|
TextButton(
|
||||||
},
|
child: Text('basis.cancel'.tr()),
|
||||||
);
|
onPressed: () {
|
||||||
break;
|
Navigator.of(context)..pop();
|
||||||
}
|
},
|
||||||
},
|
),
|
||||||
icon: Icon(Icons.more_vert),
|
TextButton(
|
||||||
itemBuilder: (BuildContext context) => [
|
child: Text(
|
||||||
// PopupMenuItem<PopupMenuItemType>(
|
'basis.delete'.tr(),
|
||||||
// value: PopupMenuItemType.reset,
|
style: TextStyle(
|
||||||
// child: Container(
|
color: BrandColors.red1,
|
||||||
// padding: EdgeInsets.only(left: 5),
|
),
|
||||||
// child: Text('users.reset_password'.tr()),
|
),
|
||||||
// ),
|
onPressed: () {
|
||||||
// ),
|
context
|
||||||
PopupMenuItem<PopupMenuItemType>(
|
.read<UsersCubit>()
|
||||||
value: PopupMenuItemType.delete,
|
.remove(user);
|
||||||
child: Container(
|
Navigator.of(context)
|
||||||
padding: EdgeInsets.only(left: 5),
|
..pop()
|
||||||
child: Text(
|
..pop();
|
||||||
'basis.delete'.tr(),
|
},
|
||||||
style: TextStyle(color: BrandColors.red1),
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
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(),
|
Spacer(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue