Added on-demand server removal option

Reviewed-on: kherel/selfprivacy.org.app#21
fdroid
ilchub 2021-04-22 21:22:38 +03:00
commit 739ff1aadf
9 changed files with 186 additions and 56 deletions

View File

@ -41,7 +41,9 @@
"1": "Dark Theme",
"2": "Change your the app theme",
"3": "Reset app config",
"4": "Reset api keys and root user"
"4": "Reset api keys and root user",
"5": "Delete Server",
"6": "This removes the Server. It will be no longer accessible"
}
},
"onboarding": {
@ -162,7 +164,7 @@
},
"initializing": {
"_comment": "initializing page",
"1": "Connect Hetzner server",
"1": "Connect a server",
"2": "Here, your data and SelfPrivacy services wiil reside",
"how": "How to obtain API token",
"3": "Connect CloudFlare",
@ -184,7 +186,10 @@
"18": "How to obtain Hetzner API Token",
"19": "1 Go via this link ",
"20": "\n",
"21": "One more restart to apply your security certificates."
"21": "One more restart to apply your security certificates.",
"22": "Create master account",
"23": "Enter a nickname and strong password"
},
"modals": {
"_comment": "messages in modals",
@ -192,7 +197,9 @@
"2": "Destroy server and create a new one?",
"3": "Are you sure?",
"4": "Purge all authentication keys?",
"5": "Yes, purge all my tokens"
"5": "Yes, purge all my tokens",
"6": "Delete the server and volume?",
"7": "Yes"
},
"timer": {
"sec": "{} sec"

View File

@ -41,7 +41,9 @@
"1": "Темная тема",
"2": "Сменить цветовую тему",
"3": "Сброс настроек",
"4": "Сбросить API ключи а так же root пользвателя"
"4": "Сбросить API ключи а так же root пользвателя",
"5": "Удалить сервер",
"6": "Действие приведет к удалению сервера. После чего он не будет доступен."
}
},
"onboarding": {
@ -162,10 +164,10 @@
},
"initializing": {
"_comment": "initializing page",
"1": "Подключите сервер Hetzner",
"1": "Подключите сервер",
"2": "Здесь будут жить наши данные и SelfPrivacy-сервисы",
"how": "Как получить API Token",
"3": "'Подключите CloudFlare'",
"3": "Подключите CloudFlare",
"4": "Для управления DNS вашего домена",
"5": "CloudFlare API Token",
"6": "Подключите облачное хранилище Backblaze",
@ -184,7 +186,9 @@
"18": "Как получить Hetzner API Token'",
"19": "1 Переходим по ссылке ",
"20": "\n2 Заходим в созданный нами проект. Если такового - нет, значит создаём.\n3 Наводим мышкой на боковую панель. Она должна раскрыться, показав нам пункты меню. Нас интересует последний — Security (с иконкой ключика).\n4 Далее, в верхней части интерфейса видим примерно такой список: SSH Keys, API Tokens, Certificates, Members. Нам нужен API Tokens. Переходим по нему.\n5 В правой части интерфейса, нас будет ожидать кнопка Generate API token. Если же вы используете мобильную версию сайта, в нижнем правом углу вы увидите красный плюсик. Нажимаем на эту кнопку.\n6 В поле Description, даём нашему токену название (это может быть любое название, которые вам нравиться. Сути оно не меняет.",
"21": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопастности"
"21": "Сейчас будет дополнительная перезагрузка для активации сертификатов безопастности",
"22": "Создайте главную учетную запись",
"23": "Введите никнейм и сложный пароль"
},
"modals": {
"_comment": "messages in modals",
@ -192,7 +196,9 @@
"2": "Уничтожить сервер и создать новый?",
"3": "Вы уверенны",
"4": "Сбросить все ключи?",
"5": "Да, сбросить"
"5": "Да, сбросить",
"6": "Удалить сервер и диск?",
"7": "Да, удалить"
},
"timer": {
"sec": "{} сек"

View File

@ -263,6 +263,28 @@ class AppConfigCubit extends Cubit<AppConfigState> {
emit(InitialAppConfigState());
}
Future<void> serverDelete() async {
closeTimer();
if (state.hetznerServer != null) {
await repository.deleteServer(state.cloudFlareDomain!);
}
await repository.deleteRecords();
emit(AppConfigState(
hetznerKey: state.hetznerKey,
cloudFlareKey: state.cloudFlareKey,
backblazeCredential: state.backblazeCredential,
cloudFlareDomain: state.cloudFlareDomain,
rootUser: state.rootUser,
hetznerServer: null,
isServerStarted: false,
isServerResetedFirstTime: false,
isServerResetedSecondTime: false,
hasFinalChecked: false,
isLoading: false,
error: null,
));
}
void setHetznerKey(String hetznerKey) async {
await repository.saveHetznerKey(hetznerKey);
emit(state.copyWith(hetznerKey: hetznerKey));

View File

@ -220,4 +220,26 @@ class AppConfigRepository {
Future<void> saveHasFinalChecked(bool value) async {
await box.put(BNames.hasFinalChecked, value);
}
Future<void> deleteServer(CloudFlareDomain cloudFlareDomain) async {
var hetznerApi = HetznerApi();
var cloudFlare = CloudflareApi();
hetznerApi.deleteSelfprivacyServerAndAllVolumes(
domainName: cloudFlareDomain.domainName,
);
cloudFlare.removeSimilarRecords(cloudFlareDomain: cloudFlareDomain);
}
Future<void> deleteRecords() async {
await box.deleteAll([
BNames.hetznerServer,
BNames.isServerStarted,
BNames.isServerResetedFirstTime,
BNames.isServerResetedSecondTime,
BNames.hasFinalChecked,
BNames.isLoading,
]);
getIt<ApiConfigModel>().init();
}
}

View File

@ -43,7 +43,7 @@ class AppConfigState extends Equatable {
final bool hasFinalChecked;
final bool? isLoading;
final bool isLoading;
final Exception? error;
AppConfigState copyWith({

View File

@ -39,7 +39,9 @@ class _BrandMarkdownState extends State<BrandMarkdown> {
Widget build(BuildContext context) {
var isDark = Theme.of(context).brightness == Brightness.dark;
var markdown = MarkdownStyleSheet(
p: defaultTextStyle,
p: defaultTextStyle.copyWith(
color: isDark ? BrandColors.white : null,
),
h1: headline1Style.copyWith(
color: isDark ? BrandColors.white : null,
),

View File

@ -33,9 +33,7 @@ class NotReadyCard extends StatelessWidget {
child: Text(
'not_ready_card.2'.tr(),
style: body1Style.copyWith(
color: Theme.of(context).brightness == Brightness.dark
? Colors.black
: BrandColors.white,
color: Colors.white,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
// height: 1.1,

View File

@ -1,5 +1,6 @@
import 'package:cubit_form/cubit_form.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/config/brand_theme.dart';
import 'package:selfprivacy/logic/cubit/forms/initializing/backblaze_form_cubit.dart';
@ -45,43 +46,56 @@ class InitializingPage extends StatelessWidget {
},
child: SafeArea(
child: Scaffold(
body: ListView(
children: [
Padding(
padding: brandPagePadding2.copyWith(top: 10, bottom: 10),
child: ProgressBar(
steps: [
'Hetzner',
'CloudFlare',
'Backblaze',
'Domain',
'User',
'Server',
'',
'',
'',
'',
],
activeIndex: cubit.state.progress,
body: SingleChildScrollView(
child: Column(
children: [
Padding(
padding: brandPagePadding2.copyWith(top: 10, bottom: 10),
child: ProgressBar(
steps: [
'Hetzner',
'CloudFlare',
'Backblaze',
'Domain',
'User',
'Server',
'',
'',
'',
'',
],
activeIndex: cubit.state.progress,
),
),
),
_addCard(
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
child: actualPage,
_addCard(
AnimatedSwitcher(
duration: Duration(milliseconds: 300),
child: actualPage,
),
),
),
BrandButton.text(
title: cubit.state.isFullyInitilized
? 'basis.close'.tr()
: 'basis.later'.tr(),
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
materialRoute(RootPage()),
(predicate) => false,
);
}),
],
ConstrainedBox(
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height -
MediaQuery.of(context).padding.top -
MediaQuery.of(context).padding.bottom -
566,
),
child: Container(
alignment: Alignment.center,
child: BrandButton.text(
title: cubit.state.isFullyInitilized
? 'basis.close'.tr()
: 'basis.later'.tr(),
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
materialRoute(RootPage()),
(predicate) => false,
);
},
),
)),
],
),
),
),
),
@ -341,8 +355,10 @@ class InitializingPage extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Spacer(),
BrandText.h2('initializing.22'.tr()),
SizedBox(height: 10),
BrandText.body2('initializing.23'.tr()),
Spacer(),
CubitFormTextField(
formFieldCubit: context.read<RootUserFormCubit>().userName,
textAlign: TextAlign.center,
@ -409,7 +425,7 @@ class InitializingPage extends StatelessWidget {
BrandText.body2('initializing.11'.tr()),
Spacer(),
BrandButton.rised(
onPressed: isLoading!
onPressed: isLoading
? null
: () => appConfigCubit.createServerAndSetDnsRecords(),
title: isLoading ? 'basis.loading'.tr() : 'initializing.11'.tr(),
@ -446,7 +462,7 @@ class InitializingPage extends StatelessWidget {
SizedBox(height: 10),
BrandText.body2(text),
SizedBox(height: 10),
if (!state.isLoading!)
if (!state.isLoading)
Row(
children: [
BrandText.body2('initializing.16'.tr()),
@ -456,7 +472,7 @@ class InitializingPage extends StatelessWidget {
)
],
),
if (state.isLoading!) BrandText.body2('initializing.17'.tr()),
if (state.isLoading) BrandText.body2('initializing.17'.tr()),
Spacer(
flex: 2,
),

View File

@ -27,8 +27,8 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
child: Builder(builder: (context) {
return Scaffold(
appBar: PreferredSize(
child:
BrandHeader(title: 'more.settings.title'.tr(), hasBackButton: true),
child: BrandHeader(
title: 'more.settings.title'.tr(), hasBackButton: true),
preferredSize: Size.fromHeight(52),
),
body: ListView(
@ -116,7 +116,64 @@ class _AppSettingsPageState extends State<AppSettingsPage> {
},
);
},
)
),
],
),
),
Container(
padding: EdgeInsets.only(top: 20, bottom: 5),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(width: 1, color: BrandColors.dividerColor),
)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: _TextColumn(
title: 'more.settings.5'.tr(),
value: 'more.settings.6'.tr(),
),
),
SizedBox(width: 5),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: BrandColors.red1,
),
child: Text(
'basis.delete'.tr(),
style: TextStyle(
color: BrandColors.white,
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(),
),
],
);
},
);
},
),
],
),
)