mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-08 00:51:20 +00:00
Added on-demand server removal option
Reviewed-on: https://git.selfprivacy.org/kherel/selfprivacy.org.app/pulls/21
This commit is contained in:
commit
739ff1aadf
|
@ -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"
|
||||
|
|
|
@ -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": "{} сек"
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class AppConfigState extends Equatable {
|
|||
|
||||
final bool hasFinalChecked;
|
||||
|
||||
final bool? isLoading;
|
||||
final bool isLoading;
|
||||
final Exception? error;
|
||||
|
||||
AppConfigState copyWith({
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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(),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue