mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-27 11:16:45 +00:00
Linting
This commit is contained in:
parent
5909b9a3e6
commit
4db0413c42
|
@ -12,6 +12,7 @@ include: package:flutter_lints/flutter.yaml
|
||||||
analyzer:
|
analyzer:
|
||||||
exclude:
|
exclude:
|
||||||
- lib/generated_plugin_registrant.dart
|
- lib/generated_plugin_registrant.dart
|
||||||
|
- lib/**.g.dart
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# The lint rules applied to this project can be customized in the
|
# The lint rules applied to this project can be customized in the
|
||||||
|
@ -28,6 +29,42 @@ linter:
|
||||||
rules:
|
rules:
|
||||||
avoid_print: false # Uncomment to disable the `avoid_print` rule
|
avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
always_use_package_imports: true
|
||||||
|
invariant_booleans: true
|
||||||
|
no_adjacent_strings_in_list: true
|
||||||
|
unnecessary_statements: true
|
||||||
|
always_declare_return_types: true
|
||||||
|
always_put_required_named_parameters_first: true
|
||||||
|
always_put_control_body_on_new_line: true
|
||||||
|
always_specify_types: true
|
||||||
|
avoid_escaping_inner_quotes: true
|
||||||
|
avoid_setters_without_getters: true
|
||||||
|
eol_at_end_of_file: true
|
||||||
|
prefer_constructors_over_static_methods: true
|
||||||
|
prefer_expression_function_bodies: true
|
||||||
|
prefer_final_in_for_each: true
|
||||||
|
prefer_final_locals: true
|
||||||
|
prefer_final_parameters: true
|
||||||
|
prefer_foreach: true
|
||||||
|
prefer_if_elements_to_conditional_expressions: true
|
||||||
|
prefer_mixin: true
|
||||||
|
prefer_null_aware_method_calls: true
|
||||||
|
require_trailing_commas: true
|
||||||
|
sized_box_shrink_expand: true
|
||||||
|
sort_constructors_first: true
|
||||||
|
unnecessary_await_in_return: true
|
||||||
|
unnecessary_lambdas: true
|
||||||
|
unnecessary_null_checks: true
|
||||||
|
unnecessary_parenthesis: true
|
||||||
|
use_enums: true
|
||||||
|
use_if_null_to_convert_nulls_to_bools: true
|
||||||
|
use_is_even_rather_than_modulo: true
|
||||||
|
use_late_for_private_fields_and_variables: true
|
||||||
|
use_named_constants: true
|
||||||
|
use_setters_to_change_properties: true
|
||||||
|
use_string_buffers: true
|
||||||
|
use_super_parameters: true
|
||||||
|
use_to_and_as_if_applicable: true
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/devices/devices_cubit.dart';
|
||||||
|
@ -12,38 +14,38 @@ import 'package:selfprivacy/logic/cubit/services/services_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
|
||||||
|
|
||||||
class BlocAndProviderConfig extends StatelessWidget {
|
class BlocAndProviderConfig extends StatelessWidget {
|
||||||
const BlocAndProviderConfig({Key? key, this.child}) : super(key: key);
|
const BlocAndProviderConfig({final super.key, this.child});
|
||||||
|
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
var isDark = false;
|
const bool isDark = false;
|
||||||
var serverInstallationCubit = ServerInstallationCubit()..load();
|
final ServerInstallationCubit serverInstallationCubit = ServerInstallationCubit()..load();
|
||||||
var usersCubit = UsersCubit(serverInstallationCubit);
|
final UsersCubit usersCubit = UsersCubit(serverInstallationCubit);
|
||||||
var servicesCubit = ServicesCubit(serverInstallationCubit);
|
final ServicesCubit servicesCubit = ServicesCubit(serverInstallationCubit);
|
||||||
var backupsCubit = BackupsCubit(serverInstallationCubit);
|
final BackupsCubit backupsCubit = BackupsCubit(serverInstallationCubit);
|
||||||
var dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit);
|
final DnsRecordsCubit dnsRecordsCubit = DnsRecordsCubit(serverInstallationCubit);
|
||||||
var recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit);
|
final RecoveryKeyCubit recoveryKeyCubit = RecoveryKeyCubit(serverInstallationCubit);
|
||||||
var apiDevicesCubit = ApiDevicesCubit(serverInstallationCubit);
|
final ApiDevicesCubit apiDevicesCubit = ApiDevicesCubit(serverInstallationCubit);
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (_) => AppSettingsCubit(
|
create: (final _) => AppSettingsCubit(
|
||||||
isDarkModeOn: isDark,
|
isDarkModeOn: isDark,
|
||||||
isOnboardingShowing: true,
|
isOnboardingShowing: true,
|
||||||
)..load(),
|
)..load(),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (_) => serverInstallationCubit, lazy: false),
|
BlocProvider(create: (final _) => serverInstallationCubit, lazy: false),
|
||||||
BlocProvider(create: (_) => ProvidersCubit()),
|
BlocProvider(create: (final _) => ProvidersCubit()),
|
||||||
BlocProvider(create: (_) => usersCubit..load(), lazy: false),
|
BlocProvider(create: (final _) => usersCubit..load(), lazy: false),
|
||||||
BlocProvider(create: (_) => servicesCubit..load(), lazy: false),
|
BlocProvider(create: (final _) => servicesCubit..load(), lazy: false),
|
||||||
BlocProvider(create: (_) => backupsCubit..load(), lazy: false),
|
BlocProvider(create: (final _) => backupsCubit..load(), lazy: false),
|
||||||
BlocProvider(create: (_) => dnsRecordsCubit..load()),
|
BlocProvider(create: (final _) => dnsRecordsCubit..load()),
|
||||||
BlocProvider(create: (_) => recoveryKeyCubit..load()),
|
BlocProvider(create: (final _) => recoveryKeyCubit..load()),
|
||||||
BlocProvider(create: (_) => apiDevicesCubit..load()),
|
BlocProvider(create: (final _) => apiDevicesCubit..load()),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (_) =>
|
create: (final _) =>
|
||||||
JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit),
|
JobsCubit(usersCubit: usersCubit, servicesCubit: servicesCubit),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:selfprivacy/ui/components/error/error.dart';
|
import 'package:selfprivacy/ui/components/error/error.dart';
|
||||||
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
import 'package:selfprivacy/utils/route_transitions/basic.dart';
|
||||||
|
|
||||||
import './get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
|
|
||||||
class SimpleBlocObserver extends BlocObserver {
|
class SimpleBlocObserver extends BlocObserver {
|
||||||
SimpleBlocObserver();
|
SimpleBlocObserver();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
|
void onError(final BlocBase bloc, final Object error, final StackTrace stackTrace) {
|
||||||
final navigator = getIt.get<NavigationService>().navigator!;
|
final NavigatorState navigator = getIt.get<NavigationService>().navigator!;
|
||||||
|
|
||||||
navigator.push(
|
navigator.push(
|
||||||
materialRoute(
|
materialRoute(
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BrandColors {
|
class BrandColors {
|
||||||
|
@ -20,8 +22,8 @@ class BrandColors {
|
||||||
|
|
||||||
static const Color green2 = Color(0xFF0F8849);
|
static const Color green2 = Color(0xFF0F8849);
|
||||||
|
|
||||||
static get navBackgroundLight => white.withOpacity(0.8);
|
static Color get navBackgroundLight => white.withOpacity(0.8);
|
||||||
static get navBackgroundDark => black.withOpacity(0.8);
|
static Color get navBackgroundDark => black.withOpacity(0.8);
|
||||||
|
|
||||||
static const List<Color> uninitializedGradientColors = [
|
static const List<Color> uninitializedGradientColors = [
|
||||||
Color(0xFF555555),
|
Color(0xFF555555),
|
||||||
|
@ -41,14 +43,14 @@ class BrandColors {
|
||||||
Color(0xFFEFD135),
|
Color(0xFFEFD135),
|
||||||
];
|
];
|
||||||
|
|
||||||
static const primary = blue;
|
static const Color primary = blue;
|
||||||
static const headlineColor = black;
|
static const Color headlineColor = black;
|
||||||
static const inactive = gray2;
|
static const Color inactive = gray2;
|
||||||
static const scaffoldBackground = gray3;
|
static const Color scaffoldBackground = gray3;
|
||||||
static const inputInactive = gray4;
|
static const Color inputInactive = gray4;
|
||||||
|
|
||||||
static const textColor1 = black;
|
static const Color textColor1 = black;
|
||||||
static const textColor2 = gray1;
|
static const Color textColor2 = gray1;
|
||||||
static const dividerColor = gray5;
|
static const Color dividerColor = gray5;
|
||||||
static const warning = red1;
|
static const Color warning = red1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/text_themes.dart';
|
import 'package:selfprivacy/config/text_themes.dart';
|
||||||
|
|
||||||
import 'brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
final lightTheme = ThemeData(
|
final ThemeData lightTheme = ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
primaryColor: BrandColors.primary,
|
primaryColor: BrandColors.primary,
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
|
@ -52,7 +52,7 @@ final lightTheme = ThemeData(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var darkTheme = lightTheme.copyWith(
|
ThemeData darkTheme = lightTheme.copyWith(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
scaffoldBackgroundColor: const Color(0xFF202120),
|
scaffoldBackgroundColor: const Color(0xFF202120),
|
||||||
iconTheme: const IconThemeData(color: BrandColors.gray3),
|
iconTheme: const IconThemeData(color: BrandColors.gray3),
|
||||||
|
@ -82,6 +82,6 @@ var darkTheme = lightTheme.copyWith(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const paddingH15V30 = EdgeInsets.symmetric(horizontal: 15, vertical: 30);
|
const EdgeInsets paddingH15V30 = EdgeInsets.symmetric(horizontal: 15, vertical: 30);
|
||||||
|
|
||||||
const paddingH15V0 = EdgeInsets.symmetric(horizontal: 15);
|
const EdgeInsets paddingH15V0 = EdgeInsets.symmetric(horizontal: 15);
|
||||||
|
|
|
@ -9,7 +9,7 @@ export 'package:selfprivacy/logic/get_it/console.dart';
|
||||||
export 'package:selfprivacy/logic/get_it/navigation.dart';
|
export 'package:selfprivacy/logic/get_it/navigation.dart';
|
||||||
export 'package:selfprivacy/logic/get_it/timer.dart';
|
export 'package:selfprivacy/logic/get_it/timer.dart';
|
||||||
|
|
||||||
final getIt = GetIt.instance;
|
final GetIt getIt = GetIt.instance;
|
||||||
|
|
||||||
Future<void> getItSetup() async {
|
Future<void> getItSetup() async {
|
||||||
getIt.registerSingleton<NavigationService>(NavigationService());
|
getIt.registerSingleton<NavigationService>(NavigationService());
|
||||||
|
|
|
@ -24,15 +24,15 @@ class HiveConfig {
|
||||||
|
|
||||||
await Hive.openBox(BNames.appSettingsBox);
|
await Hive.openBox(BNames.appSettingsBox);
|
||||||
|
|
||||||
var cipher = HiveAesCipher(
|
final HiveAesCipher cipher = HiveAesCipher(
|
||||||
await getEncryptedKey(BNames.serverInstallationEncryptionKey));
|
await getEncryptedKey(BNames.serverInstallationEncryptionKey),);
|
||||||
|
|
||||||
await Hive.openBox<User>(BNames.usersDeprecated);
|
await Hive.openBox<User>(BNames.usersDeprecated);
|
||||||
await Hive.openBox<User>(BNames.usersBox, encryptionCipher: cipher);
|
await Hive.openBox<User>(BNames.usersBox, encryptionCipher: cipher);
|
||||||
|
|
||||||
Box<User> deprecatedUsers = Hive.box<User>(BNames.usersDeprecated);
|
final Box<User> deprecatedUsers = Hive.box<User>(BNames.usersDeprecated);
|
||||||
if (deprecatedUsers.isNotEmpty) {
|
if (deprecatedUsers.isNotEmpty) {
|
||||||
Box<User> users = Hive.box<User>(BNames.usersBox);
|
final Box<User> users = Hive.box<User>(BNames.usersBox);
|
||||||
users.addAll(deprecatedUsers.values.toList());
|
users.addAll(deprecatedUsers.values.toList());
|
||||||
deprecatedUsers.clear();
|
deprecatedUsers.clear();
|
||||||
}
|
}
|
||||||
|
@ -40,15 +40,15 @@ class HiveConfig {
|
||||||
await Hive.openBox(BNames.serverInstallationBox, encryptionCipher: cipher);
|
await Hive.openBox(BNames.serverInstallationBox, encryptionCipher: cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Uint8List> getEncryptedKey(String encKey) async {
|
static Future<Uint8List> getEncryptedKey(final String encKey) async {
|
||||||
const secureStorage = FlutterSecureStorage();
|
const FlutterSecureStorage secureStorage = FlutterSecureStorage();
|
||||||
var hasEncryptionKey = await secureStorage.containsKey(key: encKey);
|
final bool hasEncryptionKey = await secureStorage.containsKey(key: encKey);
|
||||||
if (!hasEncryptionKey) {
|
if (!hasEncryptionKey) {
|
||||||
var key = Hive.generateSecureKey();
|
final List<int> key = Hive.generateSecureKey();
|
||||||
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
|
await secureStorage.write(key: encKey, value: base64UrlEncode(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
String? string = await secureStorage.read(key: encKey);
|
final String? string = await secureStorage.read(key: encKey);
|
||||||
return base64Url.decode(string!);
|
return base64Url.decode(string!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Localization extends StatelessWidget {
|
class Localization extends StatelessWidget {
|
||||||
const Localization({
|
const Localization({
|
||||||
Key? key,
|
final super.key,
|
||||||
this.child,
|
this.child,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final Widget? child;
|
final Widget? child;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => EasyLocalization(
|
||||||
return EasyLocalization(
|
|
||||||
supportedLocales: const [Locale('ru'), Locale('en')],
|
supportedLocales: const [Locale('ru'), Locale('en')],
|
||||||
path: 'assets/translations',
|
path: 'assets/translations',
|
||||||
fallbackLocale: const Locale('en'),
|
fallbackLocale: const Locale('en'),
|
||||||
|
@ -18,5 +19,4 @@ class Localization extends StatelessWidget {
|
||||||
useOnlyLangCode: true,
|
useOnlyLangCode: true,
|
||||||
child: child!,
|
child: child!,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +1,78 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/utils/named_font_weight.dart';
|
import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
|
|
||||||
import 'brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
const defaultTextStyle = TextStyle(
|
const TextStyle defaultTextStyle = TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
color: BrandColors.textColor1,
|
color: BrandColors.textColor1,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline1Style = defaultTextStyle.copyWith(
|
final TextStyle headline1Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 40,
|
fontSize: 40,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline2Style = defaultTextStyle.copyWith(
|
final TextStyle headline2Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final onboardingTitle = defaultTextStyle.copyWith(
|
final TextStyle onboardingTitle = defaultTextStyle.copyWith(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline3Style = defaultTextStyle.copyWith(
|
final TextStyle headline3Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: NamedFontWeight.extraBold,
|
fontWeight: NamedFontWeight.extraBold,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline4Style = defaultTextStyle.copyWith(
|
final TextStyle headline4Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: NamedFontWeight.medium,
|
fontWeight: NamedFontWeight.medium,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline4UnderlinedStyle = defaultTextStyle.copyWith(
|
final TextStyle headline4UnderlinedStyle = defaultTextStyle.copyWith(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: NamedFontWeight.medium,
|
fontWeight: NamedFontWeight.medium,
|
||||||
color: BrandColors.headlineColor,
|
color: BrandColors.headlineColor,
|
||||||
decoration: TextDecoration.underline,
|
decoration: TextDecoration.underline,
|
||||||
);
|
);
|
||||||
|
|
||||||
final headline5Style = defaultTextStyle.copyWith(
|
final TextStyle headline5Style = defaultTextStyle.copyWith(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: NamedFontWeight.medium,
|
fontWeight: NamedFontWeight.medium,
|
||||||
color: BrandColors.headlineColor.withOpacity(0.8),
|
color: BrandColors.headlineColor.withOpacity(0.8),
|
||||||
);
|
);
|
||||||
|
|
||||||
const body1Style = defaultTextStyle;
|
const TextStyle body1Style = defaultTextStyle;
|
||||||
final body2Style = defaultTextStyle.copyWith(
|
final TextStyle body2Style = defaultTextStyle.copyWith(
|
||||||
color: BrandColors.textColor2,
|
color: BrandColors.textColor2,
|
||||||
);
|
);
|
||||||
|
|
||||||
final buttonTitleText = defaultTextStyle.copyWith(
|
final TextStyle buttonTitleText = defaultTextStyle.copyWith(
|
||||||
color: BrandColors.white,
|
color: BrandColors.white,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
height: 1,
|
height: 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mediumStyle = defaultTextStyle.copyWith(fontSize: 13, height: 1.53);
|
final TextStyle mediumStyle = defaultTextStyle.copyWith(fontSize: 13, height: 1.53);
|
||||||
|
|
||||||
final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45);
|
final TextStyle smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45);
|
||||||
|
|
||||||
const progressTextStyleLight = TextStyle(
|
const TextStyle progressTextStyleLight = TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
color: BrandColors.textColor1,
|
color: BrandColors.textColor1,
|
||||||
height: 1.7,
|
height: 1.7,
|
||||||
);
|
);
|
||||||
|
|
||||||
final progressTextStyleDark = progressTextStyleLight.copyWith(
|
final TextStyle progressTextStyleDark = progressTextStyleLight.copyWith(
|
||||||
color: BrandColors.white,
|
color: BrandColors.white,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
@ -10,19 +12,19 @@ import 'package:selfprivacy/logic/models/message.dart';
|
||||||
|
|
||||||
abstract class ApiMap {
|
abstract class ApiMap {
|
||||||
Future<Dio> getClient() async {
|
Future<Dio> getClient() async {
|
||||||
var dio = Dio(await options);
|
final Dio dio = Dio(await options);
|
||||||
if (hasLogger) {
|
if (hasLogger) {
|
||||||
dio.interceptors.add(PrettyDioLogger());
|
dio.interceptors.add(PrettyDioLogger());
|
||||||
}
|
}
|
||||||
dio.interceptors.add(ConsoleInterceptor());
|
dio.interceptors.add(ConsoleInterceptor());
|
||||||
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
|
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
|
||||||
(HttpClient client) {
|
(final HttpClient client) {
|
||||||
client.badCertificateCallback =
|
client.badCertificateCallback =
|
||||||
(X509Certificate cert, String host, int port) => true;
|
(final X509Certificate cert, final String host, final int port) => true;
|
||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
|
||||||
dio.interceptors.add(InterceptorsWrapper(onError: (DioError e, handler) {
|
dio.interceptors.add(InterceptorsWrapper(onError: (final DioError e, final ErrorInterceptorHandler handler) {
|
||||||
print(e.requestOptions.path);
|
print(e.requestOptions.path);
|
||||||
print(e.requestOptions.data);
|
print(e.requestOptions.data);
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ abstract class ApiMap {
|
||||||
print(e.response);
|
print(e.response);
|
||||||
|
|
||||||
return handler.next(e);
|
return handler.next(e);
|
||||||
}));
|
},),);
|
||||||
return dio;
|
return dio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,21 +44,21 @@ abstract class ApiMap {
|
||||||
|
|
||||||
ValidateStatus? validateStatus;
|
ValidateStatus? validateStatus;
|
||||||
|
|
||||||
void close(Dio client) {
|
void close(final Dio client) {
|
||||||
client.close();
|
client.close();
|
||||||
validateStatus = null;
|
validateStatus = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConsoleInterceptor extends InterceptorsWrapper {
|
class ConsoleInterceptor extends InterceptorsWrapper {
|
||||||
void addMessage(Message message) {
|
void addMessage(final Message message) {
|
||||||
getIt.get<ConsoleModel>().addMessage(message);
|
getIt.get<ConsoleModel>().addMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future onRequest(
|
Future onRequest(
|
||||||
RequestOptions options,
|
final RequestOptions options,
|
||||||
RequestInterceptorHandler handler,
|
final RequestInterceptorHandler handler,
|
||||||
) async {
|
) async {
|
||||||
addMessage(
|
addMessage(
|
||||||
Message(
|
Message(
|
||||||
|
@ -69,8 +71,8 @@ class ConsoleInterceptor extends InterceptorsWrapper {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future onResponse(
|
Future onResponse(
|
||||||
Response response,
|
final Response response,
|
||||||
ResponseInterceptorHandler handler,
|
final ResponseInterceptorHandler handler,
|
||||||
) async {
|
) async {
|
||||||
addMessage(
|
addMessage(
|
||||||
Message(
|
Message(
|
||||||
|
@ -85,8 +87,8 @@ class ConsoleInterceptor extends InterceptorsWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future onError(DioError err, ErrorInterceptorHandler handler) async {
|
Future onError(final DioError err, final ErrorInterceptorHandler handler) async {
|
||||||
var response = err.response;
|
final Response? response = err.response;
|
||||||
log(err.toString());
|
log(err.toString());
|
||||||
addMessage(
|
addMessage(
|
||||||
Message.warn(
|
Message.warn(
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
@ -14,7 +16,7 @@ class BackblazeApiAuth {
|
||||||
|
|
||||||
class BackblazeApplicationKey {
|
class BackblazeApplicationKey {
|
||||||
BackblazeApplicationKey(
|
BackblazeApplicationKey(
|
||||||
{required this.applicationKeyId, required this.applicationKey});
|
{required this.applicationKeyId, required this.applicationKey,});
|
||||||
|
|
||||||
final String applicationKeyId;
|
final String applicationKeyId;
|
||||||
final String applicationKey;
|
final String applicationKey;
|
||||||
|
@ -25,10 +27,10 @@ class BackblazeApi extends ApiMap {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BaseOptions get options {
|
BaseOptions get options {
|
||||||
var options = BaseOptions(baseUrl: rootAddress);
|
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
|
||||||
if (isWithToken) {
|
if (isWithToken) {
|
||||||
var backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
final BackblazeCredential? backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
||||||
var token = backblazeCredential!.applicationKey;
|
final String token = backblazeCredential!.applicationKey;
|
||||||
options.headers = {'Authorization': 'Basic $token'};
|
options.headers = {'Authorization': 'Basic $token'};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,14 +47,14 @@ class BackblazeApi extends ApiMap {
|
||||||
String apiPrefix = '/b2api/v2';
|
String apiPrefix = '/b2api/v2';
|
||||||
|
|
||||||
Future<BackblazeApiAuth> getAuthorizationToken() async {
|
Future<BackblazeApiAuth> getAuthorizationToken() async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
var backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
final BackblazeCredential? backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
||||||
if (backblazeCredential == null) {
|
if (backblazeCredential == null) {
|
||||||
throw Exception('Backblaze credential is null');
|
throw Exception('Backblaze credential is null');
|
||||||
}
|
}
|
||||||
final String encodedApiKey = encodedBackblazeKey(
|
final String encodedApiKey = encodedBackblazeKey(
|
||||||
backblazeCredential.keyId, backblazeCredential.applicationKey);
|
backblazeCredential.keyId, backblazeCredential.applicationKey,);
|
||||||
var response = await client.get(
|
final Response response = await client.get(
|
||||||
'b2_authorize_account',
|
'b2_authorize_account',
|
||||||
options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}),
|
options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}),
|
||||||
);
|
);
|
||||||
|
@ -65,9 +67,9 @@ class BackblazeApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isValid(String encodedApiKey) async {
|
Future<bool> isValid(final String encodedApiKey) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response response = await client.get(
|
final Response response = await client.get(
|
||||||
'b2_authorize_account',
|
'b2_authorize_account',
|
||||||
options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}),
|
options: Options(headers: {'Authorization': 'Basic $encodedApiKey'}),
|
||||||
);
|
);
|
||||||
|
@ -85,12 +87,12 @@ class BackblazeApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create bucket
|
// Create bucket
|
||||||
Future<String> createBucket(String bucketName) async {
|
Future<String> createBucket(final String bucketName) async {
|
||||||
final auth = await getAuthorizationToken();
|
final BackblazeApiAuth auth = await getAuthorizationToken();
|
||||||
var backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
final BackblazeCredential? backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
client.options.baseUrl = auth.apiUrl;
|
client.options.baseUrl = auth.apiUrl;
|
||||||
var response = await client.post(
|
final Response response = await client.post(
|
||||||
'$apiPrefix/b2_create_bucket',
|
'$apiPrefix/b2_create_bucket',
|
||||||
data: {
|
data: {
|
||||||
'accountId': backblazeCredential!.keyId,
|
'accountId': backblazeCredential!.keyId,
|
||||||
|
@ -117,11 +119,11 @@ class BackblazeApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a limited capability key with access to the given bucket
|
// Create a limited capability key with access to the given bucket
|
||||||
Future<BackblazeApplicationKey> createKey(String bucketId) async {
|
Future<BackblazeApplicationKey> createKey(final String bucketId) async {
|
||||||
final auth = await getAuthorizationToken();
|
final BackblazeApiAuth auth = await getAuthorizationToken();
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
client.options.baseUrl = auth.apiUrl;
|
client.options.baseUrl = auth.apiUrl;
|
||||||
var response = await client.post(
|
final Response response = await client.post(
|
||||||
'$apiPrefix/b2_create_key',
|
'$apiPrefix/b2_create_key',
|
||||||
data: {
|
data: {
|
||||||
'accountId': getIt<ApiConfigModel>().backblazeCredential!.keyId,
|
'accountId': getIt<ApiConfigModel>().backblazeCredential!.keyId,
|
||||||
|
@ -137,7 +139,7 @@ class BackblazeApi extends ApiMap {
|
||||||
if (response.statusCode == HttpStatus.ok) {
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
return BackblazeApplicationKey(
|
return BackblazeApplicationKey(
|
||||||
applicationKeyId: response.data['applicationKeyId'],
|
applicationKeyId: response.data['applicationKeyId'],
|
||||||
applicationKey: response.data['applicationKey']);
|
applicationKey: response.data['applicationKey'],);
|
||||||
} else {
|
} else {
|
||||||
throw Exception('code: ${response.statusCode}');
|
throw Exception('code: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
@ -7,11 +9,17 @@ import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||||
|
|
||||||
class DomainNotFoundException implements Exception {
|
class DomainNotFoundException implements Exception {
|
||||||
final String message;
|
|
||||||
DomainNotFoundException(this.message);
|
DomainNotFoundException(this.message);
|
||||||
|
final String message;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CloudflareApi extends ApiMap {
|
class CloudflareApi extends ApiMap {
|
||||||
|
|
||||||
|
CloudflareApi({
|
||||||
|
this.hasLogger = false,
|
||||||
|
this.isWithToken = true,
|
||||||
|
this.customToken,
|
||||||
|
});
|
||||||
@override
|
@override
|
||||||
final bool hasLogger;
|
final bool hasLogger;
|
||||||
@override
|
@override
|
||||||
|
@ -19,17 +27,11 @@ class CloudflareApi extends ApiMap {
|
||||||
|
|
||||||
final String? customToken;
|
final String? customToken;
|
||||||
|
|
||||||
CloudflareApi({
|
|
||||||
this.hasLogger = false,
|
|
||||||
this.isWithToken = true,
|
|
||||||
this.customToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BaseOptions get options {
|
BaseOptions get options {
|
||||||
var options = BaseOptions(baseUrl: rootAddress);
|
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
|
||||||
if (isWithToken) {
|
if (isWithToken) {
|
||||||
var token = getIt<ApiConfigModel>().cloudFlareKey;
|
final String? token = getIt<ApiConfigModel>().cloudFlareKey;
|
||||||
assert(token != null);
|
assert(token != null);
|
||||||
options.headers = {'Authorization': 'Bearer $token'};
|
options.headers = {'Authorization': 'Bearer $token'};
|
||||||
}
|
}
|
||||||
|
@ -47,14 +49,12 @@ class CloudflareApi extends ApiMap {
|
||||||
@override
|
@override
|
||||||
String rootAddress = 'https://api.cloudflare.com/client/v4';
|
String rootAddress = 'https://api.cloudflare.com/client/v4';
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
Future<bool> isValid(final String token) async {
|
||||||
validateStatus = (status) {
|
validateStatus = (final status) => status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
||||||
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
|
||||||
};
|
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response response = await client.get('/user/tokens/verify',
|
final Response response = await client.get('/user/tokens/verify',
|
||||||
options: Options(headers: {'Authorization': 'Bearer $token'}));
|
options: Options(headers: {'Authorization': 'Bearer $token'}),);
|
||||||
|
|
||||||
close(client);
|
close(client);
|
||||||
|
|
||||||
|
@ -67,12 +67,10 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getZoneId(String domain) async {
|
Future<String> getZoneId(final String domain) async {
|
||||||
validateStatus = (status) {
|
validateStatus = (final status) => status == HttpStatus.ok || status == HttpStatus.forbidden;
|
||||||
return status == HttpStatus.ok || status == HttpStatus.forbidden;
|
final Dio client = await getClient();
|
||||||
};
|
final Response response = await client.get(
|
||||||
var client = await getClient();
|
|
||||||
Response response = await client.get(
|
|
||||||
'/zones',
|
'/zones',
|
||||||
queryParameters: {'name': domain},
|
queryParameters: {'name': domain},
|
||||||
);
|
);
|
||||||
|
@ -87,21 +85,21 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeSimilarRecords({
|
Future<void> removeSimilarRecords({
|
||||||
String? ip4,
|
required final ServerDomain cloudFlareDomain,
|
||||||
required ServerDomain cloudFlareDomain,
|
final String? ip4,
|
||||||
}) async {
|
}) async {
|
||||||
var domainName = cloudFlareDomain.domainName;
|
final String domainName = cloudFlareDomain.domainName;
|
||||||
var domainZoneId = cloudFlareDomain.zoneId;
|
final String domainZoneId = cloudFlareDomain.zoneId;
|
||||||
|
|
||||||
var url = '/zones/$domainZoneId/dns_records';
|
final String url = '/zones/$domainZoneId/dns_records';
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response response = await client.get(url);
|
final Response response = await client.get(url);
|
||||||
|
|
||||||
List records = response.data['result'] ?? [];
|
final List records = response.data['result'] ?? [];
|
||||||
var allDeleteFutures = <Future>[];
|
final List<Future> allDeleteFutures = <Future>[];
|
||||||
|
|
||||||
for (var record in records) {
|
for (final record in records) {
|
||||||
if (record['zone_name'] == domainName) {
|
if (record['zone_name'] == domainName) {
|
||||||
allDeleteFutures.add(
|
allDeleteFutures.add(
|
||||||
client.delete('$url/${record["id"]}'),
|
client.delete('$url/${record["id"]}'),
|
||||||
|
@ -114,20 +112,20 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<DnsRecord>> getDnsRecords({
|
Future<List<DnsRecord>> getDnsRecords({
|
||||||
required ServerDomain cloudFlareDomain,
|
required final ServerDomain cloudFlareDomain,
|
||||||
}) async {
|
}) async {
|
||||||
var domainName = cloudFlareDomain.domainName;
|
final String domainName = cloudFlareDomain.domainName;
|
||||||
var domainZoneId = cloudFlareDomain.zoneId;
|
final String domainZoneId = cloudFlareDomain.zoneId;
|
||||||
|
|
||||||
var url = '/zones/$domainZoneId/dns_records';
|
final String url = '/zones/$domainZoneId/dns_records';
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response response = await client.get(url);
|
final Response response = await client.get(url);
|
||||||
|
|
||||||
List records = response.data['result'] ?? [];
|
final List records = response.data['result'] ?? [];
|
||||||
var allRecords = <DnsRecord>[];
|
final List<DnsRecord> allRecords = <DnsRecord>[];
|
||||||
|
|
||||||
for (var record in records) {
|
for (final record in records) {
|
||||||
if (record['zone_name'] == domainName) {
|
if (record['zone_name'] == domainName) {
|
||||||
allRecords.add(DnsRecord(
|
allRecords.add(DnsRecord(
|
||||||
name: record['name'],
|
name: record['name'],
|
||||||
|
@ -135,7 +133,7 @@ class CloudflareApi extends ApiMap {
|
||||||
content: record['content'],
|
content: record['content'],
|
||||||
ttl: record['ttl'],
|
ttl: record['ttl'],
|
||||||
proxied: record['proxied'],
|
proxied: record['proxied'],
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,17 +142,17 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createMultipleDnsRecords({
|
Future<void> createMultipleDnsRecords({
|
||||||
String? ip4,
|
required final ServerDomain cloudFlareDomain,
|
||||||
required ServerDomain cloudFlareDomain,
|
final String? ip4,
|
||||||
}) async {
|
}) async {
|
||||||
var domainName = cloudFlareDomain.domainName;
|
final String domainName = cloudFlareDomain.domainName;
|
||||||
var domainZoneId = cloudFlareDomain.zoneId;
|
final String domainZoneId = cloudFlareDomain.zoneId;
|
||||||
var listDnsRecords = projectDnsRecords(domainName, ip4);
|
final List<DnsRecord> listDnsRecords = projectDnsRecords(domainName, ip4);
|
||||||
var allCreateFutures = <Future>[];
|
final List<Future> allCreateFutures = <Future>[];
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
for (var record in listDnsRecords) {
|
for (final DnsRecord record in listDnsRecords) {
|
||||||
allCreateFutures.add(
|
allCreateFutures.add(
|
||||||
client.post(
|
client.post(
|
||||||
'/zones/$domainZoneId/dns_records',
|
'/zones/$domainZoneId/dns_records',
|
||||||
|
@ -171,26 +169,26 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DnsRecord> projectDnsRecords(String? domainName, String? ip4) {
|
List<DnsRecord> projectDnsRecords(final String? domainName, final String? ip4) {
|
||||||
var domainA = DnsRecord(type: 'A', name: domainName, content: ip4);
|
final DnsRecord domainA = DnsRecord(type: 'A', name: domainName, content: ip4);
|
||||||
|
|
||||||
var mx = DnsRecord(type: 'MX', name: '@', content: domainName);
|
final DnsRecord mx = DnsRecord(type: 'MX', name: '@', content: domainName);
|
||||||
var apiA = DnsRecord(type: 'A', name: 'api', content: ip4);
|
final DnsRecord apiA = DnsRecord(type: 'A', name: 'api', content: ip4);
|
||||||
var cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4);
|
final DnsRecord cloudA = DnsRecord(type: 'A', name: 'cloud', content: ip4);
|
||||||
var gitA = DnsRecord(type: 'A', name: 'git', content: ip4);
|
final DnsRecord gitA = DnsRecord(type: 'A', name: 'git', content: ip4);
|
||||||
var meetA = DnsRecord(type: 'A', name: 'meet', content: ip4);
|
final DnsRecord meetA = DnsRecord(type: 'A', name: 'meet', content: ip4);
|
||||||
var passwordA = DnsRecord(type: 'A', name: 'password', content: ip4);
|
final DnsRecord passwordA = DnsRecord(type: 'A', name: 'password', content: ip4);
|
||||||
var socialA = DnsRecord(type: 'A', name: 'social', content: ip4);
|
final DnsRecord socialA = DnsRecord(type: 'A', name: 'social', content: ip4);
|
||||||
var vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4);
|
final DnsRecord vpn = DnsRecord(type: 'A', name: 'vpn', content: ip4);
|
||||||
|
|
||||||
var txt1 = DnsRecord(
|
final DnsRecord txt1 = DnsRecord(
|
||||||
type: 'TXT',
|
type: 'TXT',
|
||||||
name: '_dmarc',
|
name: '_dmarc',
|
||||||
content: 'v=DMARC1; p=none',
|
content: 'v=DMARC1; p=none',
|
||||||
ttl: 18000,
|
ttl: 18000,
|
||||||
);
|
);
|
||||||
|
|
||||||
var txt2 = DnsRecord(
|
final DnsRecord txt2 = DnsRecord(
|
||||||
type: 'TXT',
|
type: 'TXT',
|
||||||
name: domainName,
|
name: domainName,
|
||||||
content: 'v=spf1 a mx ip4:$ip4 -all',
|
content: 'v=spf1 a mx ip4:$ip4 -all',
|
||||||
|
@ -213,18 +211,18 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setDkim(
|
Future<void> setDkim(
|
||||||
String dkimRecordString, ServerDomain cloudFlareDomain) async {
|
final String dkimRecordString, final ServerDomain cloudFlareDomain,) async {
|
||||||
final domainZoneId = cloudFlareDomain.zoneId;
|
final String domainZoneId = cloudFlareDomain.zoneId;
|
||||||
final url = '$rootAddress/zones/$domainZoneId/dns_records';
|
final String url = '$rootAddress/zones/$domainZoneId/dns_records';
|
||||||
|
|
||||||
final dkimRecord = DnsRecord(
|
final DnsRecord dkimRecord = DnsRecord(
|
||||||
type: 'TXT',
|
type: 'TXT',
|
||||||
name: 'selector._domainkey',
|
name: 'selector._domainkey',
|
||||||
content: dkimRecordString,
|
content: dkimRecordString,
|
||||||
ttl: 18000,
|
ttl: 18000,
|
||||||
);
|
);
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
await client.post(
|
await client.post(
|
||||||
url,
|
url,
|
||||||
data: dkimRecord.toJson(),
|
data: dkimRecord.toJson(),
|
||||||
|
@ -234,17 +232,17 @@ class CloudflareApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> domainList() async {
|
Future<List<String>> domainList() async {
|
||||||
var url = '$rootAddress/zones';
|
final String url = '$rootAddress/zones';
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
|
||||||
var response = await client.get(
|
final Response response = await client.get(
|
||||||
url,
|
url,
|
||||||
queryParameters: {'per_page': 50},
|
queryParameters: {'per_page': 50},
|
||||||
);
|
);
|
||||||
|
|
||||||
close(client);
|
close(client);
|
||||||
return response.data['result']
|
return response.data['result']
|
||||||
.map<String>((el) => el['name'] as String)
|
.map<String>((final el) => el['name'] as String)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
@ -10,18 +12,18 @@ import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
import 'package:selfprivacy/utils/password_generator.dart';
|
import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
class HetznerApi extends ApiMap {
|
class HetznerApi extends ApiMap {
|
||||||
|
|
||||||
|
HetznerApi({this.hasLogger = false, this.isWithToken = true});
|
||||||
@override
|
@override
|
||||||
bool hasLogger;
|
bool hasLogger;
|
||||||
@override
|
@override
|
||||||
bool isWithToken;
|
bool isWithToken;
|
||||||
|
|
||||||
HetznerApi({this.hasLogger = false, this.isWithToken = true});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BaseOptions get options {
|
BaseOptions get options {
|
||||||
var options = BaseOptions(baseUrl: rootAddress);
|
final BaseOptions options = BaseOptions(baseUrl: rootAddress);
|
||||||
if (isWithToken) {
|
if (isWithToken) {
|
||||||
var token = getIt<ApiConfigModel>().hetznerKey;
|
final String? token = getIt<ApiConfigModel>().hetznerKey;
|
||||||
assert(token != null);
|
assert(token != null);
|
||||||
options.headers = {'Authorization': 'Bearer $token'};
|
options.headers = {'Authorization': 'Bearer $token'};
|
||||||
}
|
}
|
||||||
|
@ -36,12 +38,10 @@ class HetznerApi extends ApiMap {
|
||||||
@override
|
@override
|
||||||
String rootAddress = 'https://api.hetzner.cloud/v1';
|
String rootAddress = 'https://api.hetzner.cloud/v1';
|
||||||
|
|
||||||
Future<bool> isValid(String token) async {
|
Future<bool> isValid(final String token) async {
|
||||||
validateStatus = (status) {
|
validateStatus = (final int? status) => status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
||||||
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
|
final Dio client = await getClient();
|
||||||
};
|
final Response response = await client.get(
|
||||||
var client = await getClient();
|
|
||||||
Response response = await client.get(
|
|
||||||
'/servers',
|
'/servers',
|
||||||
options: Options(
|
options: Options(
|
||||||
headers: {'Authorization': 'Bearer $token'},
|
headers: {'Authorization': 'Bearer $token'},
|
||||||
|
@ -59,8 +59,8 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerVolume> createVolume() async {
|
Future<ServerVolume> createVolume() async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response dbCreateResponse = await client.post(
|
final Response dbCreateResponse = await client.post(
|
||||||
'/volumes',
|
'/volumes',
|
||||||
data: {
|
data: {
|
||||||
'size': 10,
|
'size': 10,
|
||||||
|
@ -71,7 +71,7 @@ class HetznerApi extends ApiMap {
|
||||||
'format': 'ext4'
|
'format': 'ext4'
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
var dbId = dbCreateResponse.data['volume']['id'];
|
final dbId = dbCreateResponse.data['volume']['id'];
|
||||||
return ServerVolume(
|
return ServerVolume(
|
||||||
id: dbId,
|
id: dbId,
|
||||||
name: dbCreateResponse.data['volume']['name'],
|
name: dbCreateResponse.data['volume']['name'],
|
||||||
|
@ -79,21 +79,21 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> createServer({
|
Future<ServerHostingDetails> createServer({
|
||||||
required String cloudFlareKey,
|
required final String cloudFlareKey,
|
||||||
required User rootUser,
|
required final User rootUser,
|
||||||
required String domainName,
|
required final String domainName,
|
||||||
required ServerVolume dataBase,
|
required final ServerVolume dataBase,
|
||||||
}) async {
|
}) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
|
||||||
var dbPassword = StringGenerators.dbPassword();
|
final String dbPassword = StringGenerators.dbPassword();
|
||||||
var dbId = dataBase.id;
|
final int dbId = dataBase.id;
|
||||||
|
|
||||||
final apiToken = StringGenerators.apiToken();
|
final String apiToken = StringGenerators.apiToken();
|
||||||
|
|
||||||
final hostname = getHostnameFromDomain(domainName);
|
final String hostname = getHostnameFromDomain(domainName);
|
||||||
|
|
||||||
final base64Password =
|
final String base64Password =
|
||||||
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
|
base64.encode(utf8.encode(rootUser.password ?? 'PASS'));
|
||||||
|
|
||||||
print('hostname: $hostname');
|
print('hostname: $hostname');
|
||||||
|
@ -101,11 +101,11 @@ class HetznerApi extends ApiMap {
|
||||||
/// 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".
|
||||||
///
|
///
|
||||||
final userdataString =
|
final String userdataString =
|
||||||
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
|
"#cloud-config\nruncmd:\n- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword API_TOKEN=$apiToken HOSTNAME=$hostname bash 2>&1 | tee /tmp/infect.log";
|
||||||
print(userdataString);
|
print(userdataString);
|
||||||
|
|
||||||
final data = {
|
final Map<String, Object> data = {
|
||||||
'name': hostname,
|
'name': hostname,
|
||||||
'server_type': 'cx11',
|
'server_type': 'cx11',
|
||||||
'start_after_create': false,
|
'start_after_create': false,
|
||||||
|
@ -119,7 +119,7 @@ class HetznerApi extends ApiMap {
|
||||||
};
|
};
|
||||||
print('Decoded data: $data');
|
print('Decoded data: $data');
|
||||||
|
|
||||||
Response serverCreateResponse = await client.post(
|
final Response serverCreateResponse = await client.post(
|
||||||
'/servers',
|
'/servers',
|
||||||
data: data,
|
data: data,
|
||||||
);
|
);
|
||||||
|
@ -136,9 +136,9 @@ class HetznerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getHostnameFromDomain(String domain) {
|
static String getHostnameFromDomain(final String domain) {
|
||||||
// Replace all non-alphanumeric characters with an underscore
|
// Replace all non-alphanumeric characters with an underscore
|
||||||
var hostname =
|
String hostname =
|
||||||
domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
domain.split('.')[0].replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
||||||
if (hostname.endsWith('-')) {
|
if (hostname.endsWith('-')) {
|
||||||
hostname = hostname.substring(0, hostname.length - 1);
|
hostname = hostname.substring(0, hostname.length - 1);
|
||||||
|
@ -154,24 +154,24 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteSelfprivacyServerAndAllVolumes({
|
Future<void> deleteSelfprivacyServerAndAllVolumes({
|
||||||
required String domainName,
|
required final String domainName,
|
||||||
}) async {
|
}) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
|
||||||
final hostname = getHostnameFromDomain(domainName);
|
final String hostname = getHostnameFromDomain(domainName);
|
||||||
|
|
||||||
Response serversReponse = await client.get('/servers');
|
final Response serversReponse = await client.get('/servers');
|
||||||
List servers = serversReponse.data['servers'];
|
final List servers = serversReponse.data['servers'];
|
||||||
Map server = servers.firstWhere((el) => el['name'] == hostname);
|
final Map server = servers.firstWhere((final el) => el['name'] == hostname);
|
||||||
List volumes = server['volumes'];
|
final List volumes = server['volumes'];
|
||||||
var laterFutures = <Future>[];
|
final List<Future> laterFutures = <Future>[];
|
||||||
|
|
||||||
for (var volumeId in volumes) {
|
for (final volumeId in volumes) {
|
||||||
await client.post('/volumes/$volumeId/actions/detach');
|
await client.post('/volumes/$volumeId/actions/detach');
|
||||||
}
|
}
|
||||||
await Future.delayed(const Duration(seconds: 10));
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
|
||||||
for (var volumeId in volumes) {
|
for (final volumeId in volumes) {
|
||||||
laterFutures.add(client.delete('/volumes/$volumeId'));
|
laterFutures.add(client.delete('/volumes/$volumeId'));
|
||||||
}
|
}
|
||||||
laterFutures.add(client.delete('/servers/${server['id']}'));
|
laterFutures.add(client.delete('/servers/${server['id']}'));
|
||||||
|
@ -181,9 +181,9 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> reset() async {
|
Future<ServerHostingDetails> reset() async {
|
||||||
var server = getIt<ApiConfigModel>().serverDetails!;
|
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
await client.post('/servers/${server.id}/actions/reset');
|
await client.post('/servers/${server.id}/actions/reset');
|
||||||
close(client);
|
close(client);
|
||||||
|
|
||||||
|
@ -191,9 +191,9 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> powerOn() async {
|
Future<ServerHostingDetails> powerOn() async {
|
||||||
var server = getIt<ApiConfigModel>().serverDetails!;
|
final ServerHostingDetails server = getIt<ApiConfigModel>().serverDetails!;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
await client.post('/servers/${server.id}/actions/poweron');
|
await client.post('/servers/${server.id}/actions/poweron');
|
||||||
close(client);
|
close(client);
|
||||||
|
|
||||||
|
@ -201,16 +201,16 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> getMetrics(
|
Future<Map<String, dynamic>> getMetrics(
|
||||||
DateTime start, DateTime end, String type) async {
|
final DateTime start, final DateTime end, final String type,) async {
|
||||||
var hetznerServer = getIt<ApiConfigModel>().serverDetails;
|
final ServerHostingDetails? hetznerServer = getIt<ApiConfigModel>().serverDetails;
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
|
|
||||||
Map<String, dynamic> queryParameters = {
|
final Map<String, dynamic> queryParameters = {
|
||||||
'start': start.toUtc().toIso8601String(),
|
'start': start.toUtc().toIso8601String(),
|
||||||
'end': end.toUtc().toIso8601String(),
|
'end': end.toUtc().toIso8601String(),
|
||||||
'type': type
|
'type': type
|
||||||
};
|
};
|
||||||
var res = await client.get(
|
final Response res = await client.get(
|
||||||
'/servers/${hetznerServer!.id}/metrics',
|
'/servers/${hetznerServer!.id}/metrics',
|
||||||
queryParameters: queryParameters,
|
queryParameters: queryParameters,
|
||||||
);
|
);
|
||||||
|
@ -219,30 +219,30 @@ class HetznerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<HetznerServerInfo> getInfo() async {
|
Future<HetznerServerInfo> getInfo() async {
|
||||||
var hetznerServer = getIt<ApiConfigModel>().serverDetails;
|
final ServerHostingDetails? hetznerServer = getIt<ApiConfigModel>().serverDetails;
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response response = await client.get('/servers/${hetznerServer!.id}');
|
final Response response = await client.get('/servers/${hetznerServer!.id}');
|
||||||
close(client);
|
close(client);
|
||||||
|
|
||||||
return HetznerServerInfo.fromJson(response.data!['server']);
|
return HetznerServerInfo.fromJson(response.data!['server']);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<HetznerServerInfo>> getServers() async {
|
Future<List<HetznerServerInfo>> getServers() async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response response = await client.get('/servers');
|
final Response response = await client.get('/servers');
|
||||||
close(client);
|
close(client);
|
||||||
|
|
||||||
return (response.data!['servers'] as List)
|
return (response.data!['servers'] as List)
|
||||||
.map((e) => HetznerServerInfo.fromJson(e))
|
.map((final e) => HetznerServerInfo.fromJson(e))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createReverseDns({
|
Future<void> createReverseDns({
|
||||||
required String ip4,
|
required final String ip4,
|
||||||
required String domainName,
|
required final String domainName,
|
||||||
}) async {
|
}) async {
|
||||||
var hetznerServer = getIt<ApiConfigModel>().serverDetails;
|
final ServerHostingDetails? hetznerServer = getIt<ApiConfigModel>().serverDetails;
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
await client.post(
|
await client.post(
|
||||||
'/servers/${hetznerServer!.id}/actions/change_dns_ptr',
|
'/servers/${hetznerServer!.id}/actions/change_dns_ptr',
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
@ -6,6 +8,7 @@ import 'package:dio/dio.dart';
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
import 'package:selfprivacy/logic/models/json/api_token.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart';
|
import 'package:selfprivacy/logic/models/json/auto_upgrade_settings.dart';
|
||||||
|
@ -14,23 +17,29 @@ import 'package:selfprivacy/logic/models/json/device_token.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
|
import 'package:selfprivacy/logic/models/json/recovery_token_status.dart';
|
||||||
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
||||||
|
|
||||||
import 'api_map.dart';
|
import 'package:selfprivacy/logic/api_maps/api_map.dart';
|
||||||
|
|
||||||
class ApiResponse<D> {
|
class ApiResponse<D> {
|
||||||
|
|
||||||
|
ApiResponse({
|
||||||
|
required this.statusCode,
|
||||||
|
required this.data,
|
||||||
|
this.errorMessage,
|
||||||
|
});
|
||||||
final int statusCode;
|
final int statusCode;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
final D data;
|
final D data;
|
||||||
|
|
||||||
bool get isSuccess => statusCode >= 200 && statusCode < 300;
|
bool get isSuccess => statusCode >= 200 && statusCode < 300;
|
||||||
|
|
||||||
ApiResponse({
|
|
||||||
required this.statusCode,
|
|
||||||
this.errorMessage,
|
|
||||||
required this.data,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerApi extends ApiMap {
|
class ServerApi extends ApiMap {
|
||||||
|
|
||||||
|
ServerApi(
|
||||||
|
{this.hasLogger = false,
|
||||||
|
this.isWithToken = true,
|
||||||
|
this.overrideDomain,
|
||||||
|
this.customToken,});
|
||||||
@override
|
@override
|
||||||
bool hasLogger;
|
bool hasLogger;
|
||||||
@override
|
@override
|
||||||
|
@ -38,24 +47,18 @@ class ServerApi extends ApiMap {
|
||||||
String? overrideDomain;
|
String? overrideDomain;
|
||||||
String? customToken;
|
String? customToken;
|
||||||
|
|
||||||
ServerApi(
|
|
||||||
{this.hasLogger = false,
|
|
||||||
this.isWithToken = true,
|
|
||||||
this.overrideDomain,
|
|
||||||
this.customToken});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
BaseOptions get options {
|
BaseOptions get options {
|
||||||
var options = BaseOptions();
|
BaseOptions options = BaseOptions();
|
||||||
|
|
||||||
if (isWithToken) {
|
if (isWithToken) {
|
||||||
var cloudFlareDomain = getIt<ApiConfigModel>().serverDomain;
|
final ServerDomain? cloudFlareDomain = getIt<ApiConfigModel>().serverDomain;
|
||||||
var domainName = cloudFlareDomain!.domainName;
|
final String domainName = cloudFlareDomain!.domainName;
|
||||||
var apiToken = getIt<ApiConfigModel>().serverDetails?.apiToken;
|
final String? apiToken = getIt<ApiConfigModel>().serverDetails?.apiToken;
|
||||||
|
|
||||||
options = BaseOptions(baseUrl: 'https://api.$domainName', headers: {
|
options = BaseOptions(baseUrl: 'https://api.$domainName', headers: {
|
||||||
'Authorization': 'Bearer $apiToken',
|
'Authorization': 'Bearer $apiToken',
|
||||||
});
|
},);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overrideDomain != null) {
|
if (overrideDomain != null) {
|
||||||
|
@ -73,7 +76,7 @@ class ServerApi extends ApiMap {
|
||||||
Future<String?> getApiVersion() async {
|
Future<String?> getApiVersion() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
String? apiVersion;
|
String? apiVersion;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -91,7 +94,7 @@ class ServerApi extends ApiMap {
|
||||||
bool res = false;
|
bool res = false;
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/services/status');
|
response = await client.get('/services/status');
|
||||||
res = response.statusCode == HttpStatus.ok;
|
res = response.statusCode == HttpStatus.ok;
|
||||||
|
@ -103,10 +106,10 @@ class ServerApi extends ApiMap {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<User>> createUser(User user) async {
|
Future<ApiResponse<User>> createUser(final User user) async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
'/users',
|
'/users',
|
||||||
|
@ -154,15 +157,15 @@ class ServerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<List<String>>> getUsersList({withMainUser = false}) async {
|
Future<ApiResponse<List<String>>> getUsersList({final withMainUser = false}) async {
|
||||||
List<String> res = [];
|
final List<String> res = [];
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/users',
|
response = await client.get('/users',
|
||||||
queryParameters: withMainUser ? {'withMainUser': 'true'} : null);
|
queryParameters: withMainUser ? {'withMainUser': 'true'} : null,);
|
||||||
for (var user in response.data) {
|
for (final user in response.data) {
|
||||||
res.add(user.toString());
|
res.add(user.toString());
|
||||||
}
|
}
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -191,10 +194,10 @@ class ServerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<void>> addUserSshKey(User user, String sshKey) async {
|
Future<ApiResponse<void>> addUserSshKey(final User user, final String sshKey) async {
|
||||||
late Response response;
|
late Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
'/services/ssh/keys/${user.login}',
|
'/services/ssh/keys/${user.login}',
|
||||||
|
@ -221,10 +224,10 @@ class ServerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<void>> addRootSshKey(String ssh) async {
|
Future<ApiResponse<void>> addRootSshKey(final String ssh) async {
|
||||||
late Response response;
|
late Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.put(
|
response = await client.put(
|
||||||
'/services/ssh/key/send',
|
'/services/ssh/key/send',
|
||||||
|
@ -249,14 +252,14 @@ class ServerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<List<String>>> getUserSshKeys(User user) async {
|
Future<ApiResponse<List<String>>> getUserSshKeys(final User user) async {
|
||||||
List<String> res;
|
List<String> res;
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/services/ssh/keys/${user.login}');
|
response = await client.get('/services/ssh/keys/${user.login}');
|
||||||
res = (response.data as List<dynamic>).map((e) => e as String).toList();
|
res = (response.data as List<dynamic>).map((final e) => e as String).toList();
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
print(e.message);
|
print(e.message);
|
||||||
return ApiResponse<List<String>>(
|
return ApiResponse<List<String>>(
|
||||||
|
@ -287,10 +290,10 @@ class ServerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<void>> deleteUserSshKey(User user, String sshKey) async {
|
Future<ApiResponse<void>> deleteUserSshKey(final User user, final String sshKey) async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.delete(
|
response = await client.delete(
|
||||||
'/services/ssh/keys/${user.login}',
|
'/services/ssh/keys/${user.login}',
|
||||||
|
@ -318,11 +321,11 @@ class ServerApi extends ApiMap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteUser(User user) async {
|
Future<bool> deleteUser(final User user) async {
|
||||||
bool res = false;
|
bool res = false;
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.delete('/users/${user.login}');
|
response = await client.delete('/users/${user.login}');
|
||||||
res = response.statusCode == HttpStatus.ok ||
|
res = response.statusCode == HttpStatus.ok ||
|
||||||
|
@ -344,7 +347,7 @@ class ServerApi extends ApiMap {
|
||||||
bool res = false;
|
bool res = false;
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/system/configuration/apply');
|
response = await client.get('/system/configuration/apply');
|
||||||
res = response.statusCode == HttpStatus.ok;
|
res = response.statusCode == HttpStatus.ok;
|
||||||
|
@ -357,8 +360,8 @@ class ServerApi extends ApiMap {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> switchService(ServiceTypes type, bool needToTurnOn) async {
|
Future<void> switchService(final ServiceTypes type, final bool needToTurnOn) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
client.post(
|
client.post(
|
||||||
'/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}',
|
'/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}',
|
||||||
|
@ -373,7 +376,7 @@ class ServerApi extends ApiMap {
|
||||||
Future<Map<ServiceTypes, bool>> servicesPowerCheck() async {
|
Future<Map<ServiceTypes, bool>> servicesPowerCheck() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/services/status');
|
response = await client.get('/services/status');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -392,8 +395,8 @@ class ServerApi extends ApiMap {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> uploadBackblazeConfig(BackblazeBucket bucket) async {
|
Future<void> uploadBackblazeConfig(final BackblazeBucket bucket) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
client.put(
|
client.put(
|
||||||
'/services/restic/backblaze/config',
|
'/services/restic/backblaze/config',
|
||||||
|
@ -411,7 +414,7 @@ class ServerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startBackup() async {
|
Future<void> startBackup() async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
client.put('/services/restic/backup/create');
|
client.put('/services/restic/backup/create');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -425,10 +428,10 @@ class ServerApi extends ApiMap {
|
||||||
Response response;
|
Response response;
|
||||||
List<Backup> backups = [];
|
List<Backup> backups = [];
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/services/restic/backup/list');
|
response = await client.get('/services/restic/backup/list');
|
||||||
backups = response.data.map<Backup>((e) => Backup.fromJson(e)).toList();
|
backups = response.data.map<Backup>((final e) => Backup.fromJson(e)).toList();
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
print(e.message);
|
print(e.message);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -447,7 +450,7 @@ class ServerApi extends ApiMap {
|
||||||
progress: 0,
|
progress: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/services/restic/backup/status');
|
response = await client.get('/services/restic/backup/status');
|
||||||
status = BackupStatus.fromJson(response.data);
|
status = BackupStatus.fromJson(response.data);
|
||||||
|
@ -460,7 +463,7 @@ class ServerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> forceBackupListReload() async {
|
Future<void> forceBackupListReload() async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
client.get('/services/restic/backup/reload');
|
client.get('/services/restic/backup/reload');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -470,8 +473,8 @@ class ServerApi extends ApiMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> restoreBackup(String backupId) async {
|
Future<void> restoreBackup(final String backupId) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
client.put(
|
client.put(
|
||||||
'/services/restic/backup/restore',
|
'/services/restic/backup/restore',
|
||||||
|
@ -488,7 +491,7 @@ class ServerApi extends ApiMap {
|
||||||
Response response;
|
Response response;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/system/configuration/pull');
|
response = await client.get('/system/configuration/pull');
|
||||||
result = (response.statusCode != null)
|
result = (response.statusCode != null)
|
||||||
|
@ -506,7 +509,7 @@ class ServerApi extends ApiMap {
|
||||||
Response response;
|
Response response;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/system/reboot');
|
response = await client.get('/system/reboot');
|
||||||
result = (response.statusCode != null)
|
result = (response.statusCode != null)
|
||||||
|
@ -524,7 +527,7 @@ class ServerApi extends ApiMap {
|
||||||
Response response;
|
Response response;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/system/configuration/upgrade');
|
response = await client.get('/system/configuration/upgrade');
|
||||||
result = (response.statusCode != null)
|
result = (response.statusCode != null)
|
||||||
|
@ -545,7 +548,7 @@ class ServerApi extends ApiMap {
|
||||||
allowReboot: false,
|
allowReboot: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/system/configuration/autoUpgrade');
|
response = await client.get('/system/configuration/autoUpgrade');
|
||||||
if (response.data != null) {
|
if (response.data != null) {
|
||||||
|
@ -559,8 +562,8 @@ class ServerApi extends ApiMap {
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateAutoUpgradeSettings(AutoUpgradeSettings settings) async {
|
Future<void> updateAutoUpgradeSettings(final AutoUpgradeSettings settings) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
await client.put(
|
await client.put(
|
||||||
'/system/configuration/autoUpgrade',
|
'/system/configuration/autoUpgrade',
|
||||||
|
@ -575,15 +578,15 @@ class ServerApi extends ApiMap {
|
||||||
|
|
||||||
Future<TimeZoneSettings> getServerTimezone() async {
|
Future<TimeZoneSettings> getServerTimezone() async {
|
||||||
// I am not sure how to initialize TimeZoneSettings with default value...
|
// I am not sure how to initialize TimeZoneSettings with default value...
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
Response response = await client.get('/system/configuration/timezone');
|
final Response response = await client.get('/system/configuration/timezone');
|
||||||
close(client);
|
close(client);
|
||||||
|
|
||||||
return TimeZoneSettings.fromString(response.data);
|
return TimeZoneSettings.fromString(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateServerTimezone(TimeZoneSettings settings) async {
|
Future<void> updateServerTimezone(final TimeZoneSettings settings) async {
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
await client.put(
|
await client.put(
|
||||||
'/system/configuration/timezone',
|
'/system/configuration/timezone',
|
||||||
|
@ -599,7 +602,7 @@ class ServerApi extends ApiMap {
|
||||||
Future<String?> getDkim() async {
|
Future<String?> getDkim() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/services/mailserver/dkim');
|
response = await client.get('/services/mailserver/dkim');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -621,7 +624,7 @@ class ServerApi extends ApiMap {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
final base64toString = utf8.fuse(base64);
|
final Codec<String, String> base64toString = utf8.fuse(base64);
|
||||||
|
|
||||||
return base64toString
|
return base64toString
|
||||||
.decode(response.data)
|
.decode(response.data)
|
||||||
|
@ -633,7 +636,7 @@ class ServerApi extends ApiMap {
|
||||||
Future<ApiResponse<RecoveryKeyStatus?>> getRecoveryTokenStatus() async {
|
Future<ApiResponse<RecoveryKeyStatus?>> getRecoveryTokenStatus() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/auth/recovery_token');
|
response = await client.get('/auth/recovery_token');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -641,7 +644,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: const RecoveryKeyStatus(exists: false, valid: false));
|
data: const RecoveryKeyStatus(exists: false, valid: false),);
|
||||||
} finally {
|
} finally {
|
||||||
close(client);
|
close(client);
|
||||||
}
|
}
|
||||||
|
@ -652,17 +655,17 @@ class ServerApi extends ApiMap {
|
||||||
statusCode: code,
|
statusCode: code,
|
||||||
data: response.data != null
|
data: response.data != null
|
||||||
? RecoveryKeyStatus.fromJson(response.data)
|
? RecoveryKeyStatus.fromJson(response.data)
|
||||||
: null);
|
: null,);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<String>> generateRecoveryToken(
|
Future<ApiResponse<String>> generateRecoveryToken(
|
||||||
DateTime? expiration,
|
final DateTime? expiration,
|
||||||
int? uses,
|
final int? uses,
|
||||||
) async {
|
) async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
var data = {};
|
final Map data = {};
|
||||||
if (expiration != null) {
|
if (expiration != null) {
|
||||||
data['expiration'] = '${expiration.toIso8601String()}Z';
|
data['expiration'] = '${expiration.toIso8601String()}Z';
|
||||||
print(data['expiration']);
|
print(data['expiration']);
|
||||||
|
@ -680,7 +683,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: '');
|
data: '',);
|
||||||
} finally {
|
} finally {
|
||||||
close(client);
|
close(client);
|
||||||
}
|
}
|
||||||
|
@ -689,13 +692,13 @@ class ServerApi extends ApiMap {
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
statusCode: code,
|
statusCode: code,
|
||||||
data: response.data != null ? response.data['token'] : '');
|
data: response.data != null ? response.data['token'] : '',);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<String>> useRecoveryToken(DeviceToken token) async {
|
Future<ApiResponse<String>> useRecoveryToken(final DeviceToken token) async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
'/auth/recovery_token/use',
|
'/auth/recovery_token/use',
|
||||||
|
@ -709,7 +712,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: '');
|
data: '',);
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
@ -718,13 +721,13 @@ class ServerApi extends ApiMap {
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
statusCode: code,
|
statusCode: code,
|
||||||
data: response.data != null ? response.data['token'] : '');
|
data: response.data != null ? response.data['token'] : '',);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<String>> authorizeDevice(DeviceToken token) async {
|
Future<ApiResponse<String>> authorizeDevice(final DeviceToken token) async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
'/auth/new_device/authorize',
|
'/auth/new_device/authorize',
|
||||||
|
@ -738,7 +741,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: '');
|
data: '',);
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
@ -751,7 +754,7 @@ class ServerApi extends ApiMap {
|
||||||
Future<ApiResponse<String>> createDeviceToken() async {
|
Future<ApiResponse<String>> createDeviceToken() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.post('/auth/new_device');
|
response = await client.post('/auth/new_device');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -759,7 +762,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: '');
|
data: '',);
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
@ -768,13 +771,13 @@ class ServerApi extends ApiMap {
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
statusCode: code,
|
statusCode: code,
|
||||||
data: response.data != null ? response.data['token'] : '');
|
data: response.data != null ? response.data['token'] : '',);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<String>> deleteDeviceToken() async {
|
Future<ApiResponse<String>> deleteDeviceToken() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.delete('/auth/new_device');
|
response = await client.delete('/auth/new_device');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -782,7 +785,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: '');
|
data: '',);
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
@ -795,7 +798,7 @@ class ServerApi extends ApiMap {
|
||||||
Future<ApiResponse<List<ApiToken>>> getApiTokens() async {
|
Future<ApiResponse<List<ApiToken>>> getApiTokens() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.get('/auth/tokens');
|
response = await client.get('/auth/tokens');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -803,7 +806,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: []);
|
data: [],);
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
@ -813,14 +816,14 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
statusCode: code,
|
statusCode: code,
|
||||||
data: (response.data != null)
|
data: (response.data != null)
|
||||||
? response.data.map<ApiToken>((e) => ApiToken.fromJson(e)).toList()
|
? response.data.map<ApiToken>((final e) => ApiToken.fromJson(e)).toList()
|
||||||
: []);
|
: [],);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<String>> refreshCurrentApiToken() async {
|
Future<ApiResponse<String>> refreshCurrentApiToken() async {
|
||||||
Response response;
|
Response response;
|
||||||
|
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.post('/auth/tokens');
|
response = await client.post('/auth/tokens');
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
|
@ -828,7 +831,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: '');
|
data: '',);
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
@ -837,12 +840,12 @@ class ServerApi extends ApiMap {
|
||||||
|
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
statusCode: code,
|
statusCode: code,
|
||||||
data: response.data != null ? response.data['token'] : '');
|
data: response.data != null ? response.data['token'] : '',);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ApiResponse<void>> deleteApiToken(String device) async {
|
Future<ApiResponse<void>> deleteApiToken(final String device) async {
|
||||||
Response response;
|
Response response;
|
||||||
var client = await getClient();
|
final Dio client = await getClient();
|
||||||
try {
|
try {
|
||||||
response = await client.delete(
|
response = await client.delete(
|
||||||
'/auth/tokens',
|
'/auth/tokens',
|
||||||
|
@ -855,7 +858,7 @@ class ServerApi extends ApiMap {
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
statusCode: e.response?.statusCode ?? HttpStatus.internalServerError,
|
||||||
data: null);
|
data: null,);
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
@ -11,14 +13,14 @@ abstract class ServerInstallationDependendCubit<
|
||||||
T extends ServerInstallationDependendState> extends Cubit<T> {
|
T extends ServerInstallationDependendState> extends Cubit<T> {
|
||||||
ServerInstallationDependendCubit(
|
ServerInstallationDependendCubit(
|
||||||
this.serverInstallationCubit,
|
this.serverInstallationCubit,
|
||||||
T initState,
|
final T initState,
|
||||||
) : super(initState) {
|
) : super(initState) {
|
||||||
authCubitSubscription =
|
authCubitSubscription =
|
||||||
serverInstallationCubit.stream.listen(checkAuthStatus);
|
serverInstallationCubit.stream.listen(checkAuthStatus);
|
||||||
checkAuthStatus(serverInstallationCubit.state);
|
checkAuthStatus(serverInstallationCubit.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkAuthStatus(ServerInstallationState state) {
|
void checkAuthStatus(final ServerInstallationState state) {
|
||||||
if (state is ServerInstallationFinished) {
|
if (state is ServerInstallationFinished) {
|
||||||
load();
|
load();
|
||||||
} else if (state is ServerInstallationEmpty) {
|
} else if (state is ServerInstallationEmpty) {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
@ -9,8 +11,8 @@ part 'app_settings_state.dart';
|
||||||
|
|
||||||
class AppSettingsCubit extends Cubit<AppSettingsState> {
|
class AppSettingsCubit extends Cubit<AppSettingsState> {
|
||||||
AppSettingsCubit({
|
AppSettingsCubit({
|
||||||
required bool isDarkModeOn,
|
required final bool isDarkModeOn,
|
||||||
required bool isOnboardingShowing,
|
required final bool isOnboardingShowing,
|
||||||
}) : super(
|
}) : super(
|
||||||
AppSettingsState(
|
AppSettingsState(
|
||||||
isDarkModeOn: isDarkModeOn,
|
isDarkModeOn: isDarkModeOn,
|
||||||
|
@ -21,15 +23,15 @@ class AppSettingsCubit extends Cubit<AppSettingsState> {
|
||||||
Box box = Hive.box(BNames.appSettingsBox);
|
Box box = Hive.box(BNames.appSettingsBox);
|
||||||
|
|
||||||
void load() {
|
void load() {
|
||||||
bool? isDarkModeOn = box.get(BNames.isDarkModeOn);
|
final bool? isDarkModeOn = box.get(BNames.isDarkModeOn);
|
||||||
bool? isOnboardingShowing = box.get(BNames.isOnboardingShowing);
|
final bool? isOnboardingShowing = box.get(BNames.isOnboardingShowing);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isDarkModeOn: isDarkModeOn,
|
isDarkModeOn: isDarkModeOn,
|
||||||
isOnboardingShowing: isOnboardingShowing,
|
isOnboardingShowing: isOnboardingShowing,
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateDarkMode({required bool isDarkModeOn}) {
|
void updateDarkMode({required final bool isDarkModeOn}) {
|
||||||
box.put(BNames.isDarkModeOn, isDarkModeOn);
|
box.put(BNames.isDarkModeOn, isDarkModeOn);
|
||||||
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
|
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'app_settings_cubit.dart';
|
part of 'app_settings_cubit.dart';
|
||||||
|
|
||||||
class AppSettingsState extends Equatable {
|
class AppSettingsState extends Equatable {
|
||||||
|
@ -9,7 +11,7 @@ class AppSettingsState extends Equatable {
|
||||||
final bool isDarkModeOn;
|
final bool isDarkModeOn;
|
||||||
final bool isOnboardingShowing;
|
final bool isOnboardingShowing;
|
||||||
|
|
||||||
AppSettingsState copyWith({isDarkModeOn, isOnboardingShowing}) =>
|
AppSettingsState copyWith({final isDarkModeOn, final isOnboardingShowing}) =>
|
||||||
AppSettingsState(
|
AppSettingsState(
|
||||||
isDarkModeOn: isDarkModeOn ?? this.isDarkModeOn,
|
isDarkModeOn: isDarkModeOn ?? this.isDarkModeOn,
|
||||||
isOnboardingShowing: isOnboardingShowing ?? this.isOnboardingShowing,
|
isOnboardingShowing: isOnboardingShowing ?? this.isOnboardingShowing,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
@ -11,22 +13,22 @@ import 'package:selfprivacy/logic/models/json/backup.dart';
|
||||||
part 'backups_state.dart';
|
part 'backups_state.dart';
|
||||||
|
|
||||||
class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
BackupsCubit(ServerInstallationCubit serverInstallationCubit)
|
BackupsCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(
|
: super(
|
||||||
serverInstallationCubit, const BackupsState(preventActions: true));
|
serverInstallationCubit, const BackupsState(preventActions: true),);
|
||||||
|
|
||||||
final api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
final backblaze = BackblazeApi();
|
final BackblazeApi backblaze = BackblazeApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
final bucket = getIt<ApiConfigModel>().backblazeBucket;
|
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
|
||||||
if (bucket == null) {
|
if (bucket == null) {
|
||||||
emit(const BackupsState(
|
emit(const BackupsState(
|
||||||
isInitialized: false, preventActions: false, refreshing: false));
|
isInitialized: false, preventActions: false, refreshing: false,),);
|
||||||
} else {
|
} else {
|
||||||
final status = await api.getBackupStatus();
|
final BackupStatus status = await api.getBackupStatus();
|
||||||
switch (status.status) {
|
switch (status.status) {
|
||||||
case BackupStatusEnum.noKey:
|
case BackupStatusEnum.noKey:
|
||||||
case BackupStatusEnum.notInitialized:
|
case BackupStatusEnum.notInitialized:
|
||||||
|
@ -37,7 +39,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
progress: 0,
|
progress: 0,
|
||||||
status: status.status,
|
status: status.status,
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
));
|
),);
|
||||||
break;
|
break;
|
||||||
case BackupStatusEnum.initializing:
|
case BackupStatusEnum.initializing:
|
||||||
emit(BackupsState(
|
emit(BackupsState(
|
||||||
|
@ -48,11 +50,11 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
status: status.status,
|
status: status.status,
|
||||||
refreshTimer: const Duration(seconds: 10),
|
refreshTimer: const Duration(seconds: 10),
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
));
|
),);
|
||||||
break;
|
break;
|
||||||
case BackupStatusEnum.initialized:
|
case BackupStatusEnum.initialized:
|
||||||
case BackupStatusEnum.error:
|
case BackupStatusEnum.error:
|
||||||
final backups = await api.getBackups();
|
final List<Backup> backups = await api.getBackups();
|
||||||
emit(BackupsState(
|
emit(BackupsState(
|
||||||
backups: backups,
|
backups: backups,
|
||||||
isInitialized: true,
|
isInitialized: true,
|
||||||
|
@ -61,11 +63,11 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
status: status.status,
|
status: status.status,
|
||||||
error: status.errorMessage ?? '',
|
error: status.errorMessage ?? '',
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
));
|
),);
|
||||||
break;
|
break;
|
||||||
case BackupStatusEnum.backingUp:
|
case BackupStatusEnum.backingUp:
|
||||||
case BackupStatusEnum.restoring:
|
case BackupStatusEnum.restoring:
|
||||||
final backups = await api.getBackups();
|
final List<Backup> backups = await api.getBackups();
|
||||||
emit(BackupsState(
|
emit(BackupsState(
|
||||||
backups: backups,
|
backups: backups,
|
||||||
isInitialized: true,
|
isInitialized: true,
|
||||||
|
@ -75,7 +77,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
error: status.errorMessage ?? '',
|
error: status.errorMessage ?? '',
|
||||||
refreshTimer: const Duration(seconds: 5),
|
refreshTimer: const Duration(seconds: 5),
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
));
|
),);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
emit(const BackupsState());
|
emit(const BackupsState());
|
||||||
|
@ -87,22 +89,22 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
|
|
||||||
Future<void> createBucket() async {
|
Future<void> createBucket() async {
|
||||||
emit(state.copyWith(preventActions: true));
|
emit(state.copyWith(preventActions: true));
|
||||||
final domain = serverInstallationCubit.state.serverDomain!.domainName
|
final String domain = serverInstallationCubit.state.serverDomain!.domainName
|
||||||
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
.replaceAll(RegExp(r'[^a-zA-Z0-9]'), '-');
|
||||||
final serverId = serverInstallationCubit.state.serverDetails!.id;
|
final int serverId = serverInstallationCubit.state.serverDetails!.id;
|
||||||
var bucketName = 'selfprivacy-$domain-$serverId';
|
String bucketName = 'selfprivacy-$domain-$serverId';
|
||||||
// If bucket name is too long, shorten it
|
// If bucket name is too long, shorten it
|
||||||
if (bucketName.length > 49) {
|
if (bucketName.length > 49) {
|
||||||
bucketName = bucketName.substring(0, 49);
|
bucketName = bucketName.substring(0, 49);
|
||||||
}
|
}
|
||||||
final bucketId = await backblaze.createBucket(bucketName);
|
final String bucketId = await backblaze.createBucket(bucketName);
|
||||||
|
|
||||||
final key = await backblaze.createKey(bucketId);
|
final BackblazeApplicationKey key = await backblaze.createKey(bucketId);
|
||||||
final bucket = BackblazeBucket(
|
final BackblazeBucket bucket = BackblazeBucket(
|
||||||
bucketId: bucketId,
|
bucketId: bucketId,
|
||||||
bucketName: bucketName,
|
bucketName: bucketName,
|
||||||
applicationKey: key.applicationKey,
|
applicationKey: key.applicationKey,
|
||||||
applicationKeyId: key.applicationKeyId);
|
applicationKeyId: key.applicationKeyId,);
|
||||||
|
|
||||||
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
|
await getIt<ApiConfigModel>().storeBackblazeBucket(bucket);
|
||||||
await api.uploadBackblazeConfig(bucket);
|
await api.uploadBackblazeConfig(bucket);
|
||||||
|
@ -113,7 +115,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
|
|
||||||
Future<void> reuploadKey() async {
|
Future<void> reuploadKey() async {
|
||||||
emit(state.copyWith(preventActions: true));
|
emit(state.copyWith(preventActions: true));
|
||||||
final bucket = getIt<ApiConfigModel>().backblazeBucket;
|
final BackblazeBucket? bucket = getIt<ApiConfigModel>().backblazeBucket;
|
||||||
if (bucket == null) {
|
if (bucket == null) {
|
||||||
emit(state.copyWith(isInitialized: false));
|
emit(state.copyWith(isInitialized: false));
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,7 +125,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Duration refreshTimeFromState(BackupStatusEnum status) {
|
Duration refreshTimeFromState(final BackupStatusEnum status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case BackupStatusEnum.backingUp:
|
case BackupStatusEnum.backingUp:
|
||||||
case BackupStatusEnum.restoring:
|
case BackupStatusEnum.restoring:
|
||||||
|
@ -135,10 +137,10 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateBackups({bool useTimer = false}) async {
|
Future<void> updateBackups({final bool useTimer = false}) async {
|
||||||
emit(state.copyWith(refreshing: true));
|
emit(state.copyWith(refreshing: true));
|
||||||
final backups = await api.getBackups();
|
final List<Backup> backups = await api.getBackups();
|
||||||
final status = await api.getBackupStatus();
|
final BackupStatus status = await api.getBackupStatus();
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
backups: backups,
|
backups: backups,
|
||||||
progress: status.progress,
|
progress: status.progress,
|
||||||
|
@ -146,7 +148,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
error: status.errorMessage,
|
error: status.errorMessage,
|
||||||
refreshTimer: refreshTimeFromState(status.status),
|
refreshTimer: refreshTimeFromState(status.status),
|
||||||
refreshing: false,
|
refreshing: false,
|
||||||
));
|
),);
|
||||||
if (useTimer) {
|
if (useTimer) {
|
||||||
Timer(state.refreshTimer, () => updateBackups(useTimer: true));
|
Timer(state.refreshTimer, () => updateBackups(useTimer: true));
|
||||||
}
|
}
|
||||||
|
@ -167,7 +169,7 @@ class BackupsCubit extends ServerInstallationDependendCubit<BackupsState> {
|
||||||
emit(state.copyWith(preventActions: false));
|
emit(state.copyWith(preventActions: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> restoreBackup(String backupId) async {
|
Future<void> restoreBackup(final String backupId) async {
|
||||||
emit(state.copyWith(preventActions: true));
|
emit(state.copyWith(preventActions: true));
|
||||||
await api.restoreBackup(backupId);
|
await api.restoreBackup(backupId);
|
||||||
emit(state.copyWith(preventActions: false));
|
emit(state.copyWith(preventActions: false));
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'backups_cubit.dart';
|
part of 'backups_cubit.dart';
|
||||||
|
|
||||||
class BackupsState extends ServerInstallationDependendState {
|
class BackupsState extends ServerInstallationDependendState {
|
||||||
|
@ -34,14 +36,14 @@ class BackupsState extends ServerInstallationDependendState {
|
||||||
];
|
];
|
||||||
|
|
||||||
BackupsState copyWith({
|
BackupsState copyWith({
|
||||||
bool? isInitialized,
|
final bool? isInitialized,
|
||||||
List<Backup>? backups,
|
final List<Backup>? backups,
|
||||||
double? progress,
|
final double? progress,
|
||||||
BackupStatusEnum? status,
|
final BackupStatusEnum? status,
|
||||||
bool? preventActions,
|
final bool? preventActions,
|
||||||
String? error,
|
final String? error,
|
||||||
Duration? refreshTimer,
|
final Duration? refreshTimer,
|
||||||
bool? refreshing,
|
final bool? refreshing,
|
||||||
}) =>
|
}) =>
|
||||||
BackupsState(
|
BackupsState(
|
||||||
isInitialized: isInitialized ?? this.isInitialized,
|
isInitialized: isInitialized ?? this.isInitialized,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:selfprivacy/config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/server.dart';
|
import 'package:selfprivacy/logic/api_maps/server.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
|
@ -8,16 +10,16 @@ part 'devices_state.dart';
|
||||||
|
|
||||||
class ApiDevicesCubit
|
class ApiDevicesCubit
|
||||||
extends ServerInstallationDependendCubit<ApiDevicesState> {
|
extends ServerInstallationDependendCubit<ApiDevicesState> {
|
||||||
ApiDevicesCubit(ServerInstallationCubit serverInstallationCubit)
|
ApiDevicesCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(serverInstallationCubit, const ApiDevicesState.initial());
|
: super(serverInstallationCubit, const ApiDevicesState.initial());
|
||||||
|
|
||||||
final api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void load() async {
|
void load() async {
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
emit(const ApiDevicesState([], LoadingStatus.refreshing));
|
emit(const ApiDevicesState([], LoadingStatus.refreshing));
|
||||||
final devices = await _getApiTokens();
|
final List<ApiToken>? devices = await _getApiTokens();
|
||||||
if (devices != null) {
|
if (devices != null) {
|
||||||
emit(ApiDevicesState(devices, LoadingStatus.success));
|
emit(ApiDevicesState(devices, LoadingStatus.success));
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,7 +30,7 @@ class ApiDevicesCubit
|
||||||
|
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
emit(const ApiDevicesState([], LoadingStatus.refreshing));
|
emit(const ApiDevicesState([], LoadingStatus.refreshing));
|
||||||
final devices = await _getApiTokens();
|
final List<ApiToken>? devices = await _getApiTokens();
|
||||||
if (devices != null) {
|
if (devices != null) {
|
||||||
emit(ApiDevicesState(devices, LoadingStatus.success));
|
emit(ApiDevicesState(devices, LoadingStatus.success));
|
||||||
} else {
|
} else {
|
||||||
|
@ -37,7 +39,7 @@ class ApiDevicesCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ApiToken>?> _getApiTokens() async {
|
Future<List<ApiToken>?> _getApiTokens() async {
|
||||||
final response = await api.getApiTokens();
|
final ApiResponse<List<ApiToken>> response = await api.getApiTokens();
|
||||||
if (response.isSuccess) {
|
if (response.isSuccess) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
|
@ -45,12 +47,12 @@ class ApiDevicesCubit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteDevice(ApiToken device) async {
|
Future<void> deleteDevice(final ApiToken device) async {
|
||||||
final response = await api.deleteApiToken(device.name);
|
final ApiResponse<void> response = await api.deleteApiToken(device.name);
|
||||||
if (response.isSuccess) {
|
if (response.isSuccess) {
|
||||||
emit(ApiDevicesState(
|
emit(ApiDevicesState(
|
||||||
state.devices.where((d) => d.name != device.name).toList(),
|
state.devices.where((final d) => d.name != device.name).toList(),
|
||||||
LoadingStatus.success));
|
LoadingStatus.success,),);
|
||||||
} else {
|
} else {
|
||||||
getIt<NavigationService>()
|
getIt<NavigationService>()
|
||||||
.showSnackBar(response.errorMessage ?? 'Error deleting device');
|
.showSnackBar(response.errorMessage ?? 'Error deleting device');
|
||||||
|
@ -58,12 +60,12 @@ class ApiDevicesCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getNewDeviceKey() async {
|
Future<String?> getNewDeviceKey() async {
|
||||||
final response = await api.createDeviceToken();
|
final ApiResponse<String> response = await api.createDeviceToken();
|
||||||
if (response.isSuccess) {
|
if (response.isSuccess) {
|
||||||
return response.data;
|
return response.data;
|
||||||
} else {
|
} else {
|
||||||
getIt<NavigationService>().showSnackBar(
|
getIt<NavigationService>().showSnackBar(
|
||||||
response.errorMessage ?? 'Error getting new device key');
|
response.errorMessage ?? 'Error getting new device key',);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'devices_cubit.dart';
|
part of 'devices_cubit.dart';
|
||||||
|
|
||||||
class ApiDevicesState extends ServerInstallationDependendState {
|
class ApiDevicesState extends ServerInstallationDependendState {
|
||||||
|
@ -8,25 +10,23 @@ class ApiDevicesState extends ServerInstallationDependendState {
|
||||||
final LoadingStatus status;
|
final LoadingStatus status;
|
||||||
|
|
||||||
List<ApiToken> get devices => _devices;
|
List<ApiToken> get devices => _devices;
|
||||||
ApiToken get thisDevice => _devices.firstWhere((device) => device.isCaller,
|
ApiToken get thisDevice => _devices.firstWhere((final device) => device.isCaller,
|
||||||
orElse: () => ApiToken(
|
orElse: () => ApiToken(
|
||||||
name: 'Error fetching device',
|
name: 'Error fetching device',
|
||||||
isCaller: true,
|
isCaller: true,
|
||||||
date: DateTime.now(),
|
date: DateTime.now(),
|
||||||
));
|
),);
|
||||||
|
|
||||||
List<ApiToken> get otherDevices =>
|
List<ApiToken> get otherDevices =>
|
||||||
_devices.where((device) => !device.isCaller).toList();
|
_devices.where((final device) => !device.isCaller).toList();
|
||||||
|
|
||||||
ApiDevicesState copyWith({
|
ApiDevicesState copyWith({
|
||||||
List<ApiToken>? devices,
|
final List<ApiToken>? devices,
|
||||||
LoadingStatus? status,
|
final LoadingStatus? status,
|
||||||
}) {
|
}) => ApiDevicesState(
|
||||||
return ApiDevicesState(
|
|
||||||
devices ?? _devices,
|
devices ?? _devices,
|
||||||
status ?? this.status,
|
status ?? this.status,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [_devices];
|
List<Object?> get props => [_devices];
|
||||||
|
|
|
@ -1,28 +1,30 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
import 'package:selfprivacy/logic/models/json/dns_records.dart';
|
||||||
|
|
||||||
import '../../api_maps/cloudflare.dart';
|
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
||||||
import '../../api_maps/server.dart';
|
import 'package:selfprivacy/logic/api_maps/server.dart';
|
||||||
|
|
||||||
part 'dns_records_state.dart';
|
part 'dns_records_state.dart';
|
||||||
|
|
||||||
class DnsRecordsCubit
|
class DnsRecordsCubit
|
||||||
extends ServerInstallationDependendCubit<DnsRecordsState> {
|
extends ServerInstallationDependendCubit<DnsRecordsState> {
|
||||||
DnsRecordsCubit(ServerInstallationCubit serverInstallationCubit)
|
DnsRecordsCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(serverInstallationCubit,
|
: super(serverInstallationCubit,
|
||||||
const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing));
|
const DnsRecordsState(dnsState: DnsRecordsStatus.refreshing),);
|
||||||
|
|
||||||
final api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
final cloudflare = CloudflareApi();
|
final CloudflareApi cloudflare = CloudflareApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
emit(DnsRecordsState(
|
emit(DnsRecordsState(
|
||||||
dnsState: DnsRecordsStatus.refreshing,
|
dnsState: DnsRecordsStatus.refreshing,
|
||||||
dnsRecords: _getDesiredDnsRecords(
|
dnsRecords: _getDesiredDnsRecords(
|
||||||
serverInstallationCubit.state.serverDomain?.domainName, '', '')));
|
serverInstallationCubit.state.serverDomain?.domainName, '', '',),),);
|
||||||
print('Loading DNS status');
|
print('Loading DNS status');
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
|
final ServerDomain? domain = serverInstallationCubit.state.serverDomain;
|
||||||
|
@ -31,37 +33,37 @@ class DnsRecordsCubit
|
||||||
if (domain != null && ipAddress != null) {
|
if (domain != null && ipAddress != null) {
|
||||||
final List<DnsRecord> records =
|
final List<DnsRecord> records =
|
||||||
await cloudflare.getDnsRecords(cloudFlareDomain: domain);
|
await cloudflare.getDnsRecords(cloudFlareDomain: domain);
|
||||||
final dkimPublicKey = await api.getDkim();
|
final String? dkimPublicKey = await api.getDkim();
|
||||||
final desiredRecords =
|
final List<DesiredDnsRecord> desiredRecords =
|
||||||
_getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey);
|
_getDesiredDnsRecords(domain.domainName, ipAddress, dkimPublicKey);
|
||||||
List<DesiredDnsRecord> foundRecords = [];
|
final List<DesiredDnsRecord> foundRecords = [];
|
||||||
for (final record in desiredRecords) {
|
for (final DesiredDnsRecord record in desiredRecords) {
|
||||||
if (record.description ==
|
if (record.description ==
|
||||||
'providers.domain.record_description.dkim') {
|
'providers.domain.record_description.dkim') {
|
||||||
final foundRecord = records.firstWhere(
|
final DnsRecord foundRecord = records.firstWhere(
|
||||||
(r) => r.name == record.name && r.type == record.type,
|
(final r) => r.name == record.name && r.type == record.type,
|
||||||
orElse: () => DnsRecord(
|
orElse: () => DnsRecord(
|
||||||
name: record.name,
|
name: record.name,
|
||||||
type: record.type,
|
type: record.type,
|
||||||
content: '',
|
content: '',
|
||||||
ttl: 800,
|
ttl: 800,
|
||||||
proxied: false));
|
proxied: false,),);
|
||||||
// remove all spaces and tabulators from
|
// remove all spaces and tabulators from
|
||||||
// the foundRecord.content and the record.content
|
// the foundRecord.content and the record.content
|
||||||
// to compare them
|
// to compare them
|
||||||
final foundContent =
|
final String? foundContent =
|
||||||
foundRecord.content?.replaceAll(RegExp(r'\s+'), '');
|
foundRecord.content?.replaceAll(RegExp(r'\s+'), '');
|
||||||
final content = record.content.replaceAll(RegExp(r'\s+'), '');
|
final String content = record.content.replaceAll(RegExp(r'\s+'), '');
|
||||||
if (foundContent == content) {
|
if (foundContent == content) {
|
||||||
foundRecords.add(record.copyWith(isSatisfied: true));
|
foundRecords.add(record.copyWith(isSatisfied: true));
|
||||||
} else {
|
} else {
|
||||||
foundRecords.add(record.copyWith(isSatisfied: false));
|
foundRecords.add(record.copyWith(isSatisfied: false));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (records.any((r) =>
|
if (records.any((final r) =>
|
||||||
r.name == record.name &&
|
r.name == record.name &&
|
||||||
r.type == record.type &&
|
r.type == record.type &&
|
||||||
r.content == record.content)) {
|
r.content == record.content,)) {
|
||||||
foundRecords.add(record.copyWith(isSatisfied: true));
|
foundRecords.add(record.copyWith(isSatisfied: true));
|
||||||
} else {
|
} else {
|
||||||
foundRecords.add(record.copyWith(isSatisfied: false));
|
foundRecords.add(record.copyWith(isSatisfied: false));
|
||||||
|
@ -70,10 +72,10 @@ class DnsRecordsCubit
|
||||||
}
|
}
|
||||||
emit(DnsRecordsState(
|
emit(DnsRecordsState(
|
||||||
dnsRecords: foundRecords,
|
dnsRecords: foundRecords,
|
||||||
dnsState: foundRecords.any((r) => r.isSatisfied == false)
|
dnsState: foundRecords.any((final r) => r.isSatisfied == false)
|
||||||
? DnsRecordsStatus.error
|
? DnsRecordsStatus.error
|
||||||
: DnsRecordsStatus.good,
|
: DnsRecordsStatus.good,
|
||||||
));
|
),);
|
||||||
} else {
|
} else {
|
||||||
emit(const DnsRecordsState());
|
emit(const DnsRecordsState());
|
||||||
}
|
}
|
||||||
|
@ -81,7 +83,7 @@ class DnsRecordsCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onChange(Change<DnsRecordsState> change) {
|
void onChange(final Change<DnsRecordsState> change) {
|
||||||
// print(change);
|
// print(change);
|
||||||
super.onChange(change);
|
super.onChange(change);
|
||||||
}
|
}
|
||||||
|
@ -103,13 +105,13 @@ class DnsRecordsCubit
|
||||||
final String? dkimPublicKey = await api.getDkim();
|
final String? dkimPublicKey = await api.getDkim();
|
||||||
await cloudflare.removeSimilarRecords(cloudFlareDomain: domain!);
|
await cloudflare.removeSimilarRecords(cloudFlareDomain: domain!);
|
||||||
await cloudflare.createMultipleDnsRecords(
|
await cloudflare.createMultipleDnsRecords(
|
||||||
cloudFlareDomain: domain, ip4: ipAddress);
|
cloudFlareDomain: domain, ip4: ipAddress,);
|
||||||
await cloudflare.setDkim(dkimPublicKey ?? '', domain);
|
await cloudflare.setDkim(dkimPublicKey ?? '', domain);
|
||||||
await load();
|
await load();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DesiredDnsRecord> _getDesiredDnsRecords(
|
List<DesiredDnsRecord> _getDesiredDnsRecords(
|
||||||
String? domainName, String? ipAddress, String? dkimPublicKey) {
|
final String? domainName, final String? ipAddress, final String? dkimPublicKey,) {
|
||||||
if (domainName == null || ipAddress == null || dkimPublicKey == null) {
|
if (domainName == null || ipAddress == null || dkimPublicKey == null) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'dns_records_cubit.dart';
|
part of 'dns_records_cubit.dart';
|
||||||
|
|
||||||
enum DnsRecordsStatus {
|
enum DnsRecordsStatus {
|
||||||
|
@ -29,21 +31,19 @@ class DnsRecordsState extends ServerInstallationDependendState {
|
||||||
];
|
];
|
||||||
|
|
||||||
DnsRecordsState copyWith({
|
DnsRecordsState copyWith({
|
||||||
DnsRecordsStatus? dnsState,
|
final DnsRecordsStatus? dnsState,
|
||||||
List<DesiredDnsRecord>? dnsRecords,
|
final List<DesiredDnsRecord>? dnsRecords,
|
||||||
}) {
|
}) => DnsRecordsState(
|
||||||
return DnsRecordsState(
|
|
||||||
dnsState: dnsState ?? this.dnsState,
|
dnsState: dnsState ?? this.dnsState,
|
||||||
dnsRecords: dnsRecords ?? this.dnsRecords,
|
dnsRecords: dnsRecords ?? this.dnsRecords,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DesiredDnsRecord {
|
class DesiredDnsRecord {
|
||||||
const DesiredDnsRecord({
|
const DesiredDnsRecord({
|
||||||
required this.name,
|
required this.name,
|
||||||
this.type = 'A',
|
|
||||||
required this.content,
|
required this.content,
|
||||||
|
this.type = 'A',
|
||||||
this.description = '',
|
this.description = '',
|
||||||
this.category = DnsRecordsCategory.services,
|
this.category = DnsRecordsCategory.services,
|
||||||
this.isSatisfied = false,
|
this.isSatisfied = false,
|
||||||
|
@ -57,14 +57,13 @@ class DesiredDnsRecord {
|
||||||
final bool isSatisfied;
|
final bool isSatisfied;
|
||||||
|
|
||||||
DesiredDnsRecord copyWith({
|
DesiredDnsRecord copyWith({
|
||||||
String? name,
|
final String? name,
|
||||||
String? type,
|
final String? type,
|
||||||
String? content,
|
final String? content,
|
||||||
String? description,
|
final String? description,
|
||||||
DnsRecordsCategory? category,
|
final DnsRecordsCategory? category,
|
||||||
bool? isSatisfied,
|
final bool? isSatisfied,
|
||||||
}) {
|
}) => DesiredDnsRecord(
|
||||||
return DesiredDnsRecord(
|
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
type: type ?? this.type,
|
type: type ?? this.type,
|
||||||
content: content ?? this.content,
|
content: content ?? this.content,
|
||||||
|
@ -72,5 +71,4 @@ class DesiredDnsRecord {
|
||||||
category: category ?? this.category,
|
category: category ?? this.category,
|
||||||
isSatisfied: isSatisfied ?? this.isSatisfied,
|
isSatisfied: isSatisfied ?? this.isSatisfied,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
@ -16,21 +18,21 @@ class FieldCubitFactory {
|
||||||
/// - Must not be a reserved root login
|
/// - Must not be a reserved root login
|
||||||
/// - Must be unique
|
/// - Must be unique
|
||||||
FieldCubit<String> createUserLoginField() {
|
FieldCubit<String> createUserLoginField() {
|
||||||
final userAllowedRegExp = RegExp(r'^[a-z_][a-z0-9_]+$');
|
final RegExp userAllowedRegExp = RegExp(r'^[a-z_][a-z0-9_]+$');
|
||||||
const userMaxLength = 31;
|
const int userMaxLength = 31;
|
||||||
return FieldCubit(
|
return FieldCubit(
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
(s) => s.toLowerCase() == 'root', 'validations.root_name'.tr()),
|
(final String s) => s.toLowerCase() == 'root', 'validations.root_name'.tr(),),
|
||||||
ValidationModel(
|
ValidationModel(
|
||||||
(login) => context.read<UsersCubit>().state.isLoginRegistered(login),
|
(final String login) => context.read<UsersCubit>().state.isLoginRegistered(login),
|
||||||
'validations.user_already_exist'.tr(),
|
'validations.user_already_exist'.tr(),
|
||||||
),
|
),
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
LengthStringLongerValidation(userMaxLength),
|
LengthStringLongerValidation(userMaxLength),
|
||||||
ValidationModel<String>((s) => !userAllowedRegExp.hasMatch(s),
|
ValidationModel<String>((final String s) => !userAllowedRegExp.hasMatch(s),
|
||||||
'validations.invalid_format'.tr()),
|
'validations.invalid_format'.tr(),),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -40,26 +42,24 @@ class FieldCubitFactory {
|
||||||
/// - Must fail on the regural expression of invalid matches: [\n\r\s]+
|
/// - Must fail on the regural expression of invalid matches: [\n\r\s]+
|
||||||
/// - Must not be empty
|
/// - Must not be empty
|
||||||
FieldCubit<String> createUserPasswordField() {
|
FieldCubit<String> createUserPasswordField() {
|
||||||
var passwordForbiddenRegExp = RegExp(r'[\n\r\s]+');
|
final RegExp passwordForbiddenRegExp = RegExp(r'[\n\r\s]+');
|
||||||
return FieldCubit(
|
return FieldCubit(
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
(password) => passwordForbiddenRegExp.hasMatch(password),
|
passwordForbiddenRegExp.hasMatch,
|
||||||
'validations.invalid_format'.tr()),
|
'validations.invalid_format'.tr(),),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldCubit<String> createRequiredStringField() {
|
FieldCubit<String> createRequiredStringField() => FieldCubit(
|
||||||
return FieldCubit(
|
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/backblaze.dart';
|
import 'package:selfprivacy/logic/api_maps/backblaze.dart';
|
||||||
|
@ -41,10 +43,10 @@ class BackblazeFormCubit extends FormCubit {
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
late bool isKeyValid;
|
late bool isKeyValid;
|
||||||
BackblazeApi apiClient = BackblazeApi(isWithToken: false);
|
final BackblazeApi apiClient = BackblazeApi(isWithToken: false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String encodedApiKey = encodedBackblazeKey(
|
final String encodedApiKey = encodedBackblazeKey(
|
||||||
keyId.state.value,
|
keyId.state.value,
|
||||||
applicationKey.state.value,
|
applicationKey.state.value,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
@ -8,13 +10,13 @@ import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
|
|
||||||
class CloudFlareFormCubit extends FormCubit {
|
class CloudFlareFormCubit extends FormCubit {
|
||||||
CloudFlareFormCubit(this.initializingCubit) {
|
CloudFlareFormCubit(this.initializingCubit) {
|
||||||
var regExp = RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
|
final RegExp regExp = RegExp(r'\s+|[!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
|
||||||
apiKey = FieldCubit(
|
apiKey = FieldCubit(
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
regExp.hasMatch, 'validations.key_format'.tr(),),
|
||||||
LengthStringNotEqualValidation(40)
|
LengthStringNotEqualValidation(40)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -34,7 +36,7 @@ class CloudFlareFormCubit extends FormCubit {
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
late bool isKeyValid;
|
late bool isKeyValid;
|
||||||
CloudflareApi apiClient = CloudflareApi(isWithToken: false);
|
final CloudflareApi apiClient = CloudflareApi(isWithToken: false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
||||||
|
|
|
@ -10,9 +10,9 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
emit(Loading(LoadingTypes.loadingDomain));
|
emit(Loading(LoadingTypes.loadingDomain));
|
||||||
var api = CloudflareApi();
|
final CloudflareApi api = CloudflareApi();
|
||||||
|
|
||||||
var list = await api.domainList();
|
final List<String> list = await api.domainList();
|
||||||
if (list.isEmpty) {
|
if (list.isEmpty) {
|
||||||
emit(Empty());
|
emit(Empty());
|
||||||
} else if (list.length == 1) {
|
} else if (list.length == 1) {
|
||||||
|
@ -23,20 +23,18 @@ class DomainSetupCubit extends Cubit<DomainSetupState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() {
|
Future<void> close() => super.close();
|
||||||
return super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> saveDomain() async {
|
Future<void> saveDomain() async {
|
||||||
assert(state is Loaded, 'wrong state');
|
assert(state is Loaded, 'wrong state');
|
||||||
var domainName = (state as Loaded).domain;
|
final String domainName = (state as Loaded).domain;
|
||||||
var api = CloudflareApi();
|
final CloudflareApi api = CloudflareApi();
|
||||||
|
|
||||||
emit(Loading(LoadingTypes.saving));
|
emit(Loading(LoadingTypes.saving));
|
||||||
|
|
||||||
var zoneId = await api.getZoneId(domainName);
|
final String zoneId = await api.getZoneId(domainName);
|
||||||
|
|
||||||
var domain = ServerDomain(
|
final ServerDomain domain = ServerDomain(
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
zoneId: zoneId,
|
zoneId: zoneId,
|
||||||
provider: DnsProvider.cloudflare,
|
provider: DnsProvider.cloudflare,
|
||||||
|
@ -63,9 +61,9 @@ class Loading extends DomainSetupState {
|
||||||
enum LoadingTypes { loadingDomain, saving }
|
enum LoadingTypes { loadingDomain, saving }
|
||||||
|
|
||||||
class Loaded extends DomainSetupState {
|
class Loaded extends DomainSetupState {
|
||||||
final String domain;
|
|
||||||
|
|
||||||
Loaded(this.domain);
|
Loaded(this.domain);
|
||||||
|
final String domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DomainSet extends DomainSetupState {}
|
class DomainSet extends DomainSetupState {}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
@ -8,13 +10,13 @@ import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
|
||||||
|
|
||||||
class HetznerFormCubit extends FormCubit {
|
class HetznerFormCubit extends FormCubit {
|
||||||
HetznerFormCubit(this.serverInstallationCubit) {
|
HetznerFormCubit(this.serverInstallationCubit) {
|
||||||
var regExp = RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
|
final RegExp regExp = RegExp(r'\s+|[-!$%^&*()@+|~=`{}\[\]:<>?,.\/]');
|
||||||
apiKey = FieldCubit(
|
apiKey = FieldCubit(
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>(
|
ValidationModel<String>(
|
||||||
(s) => regExp.hasMatch(s), 'validations.key_format'.tr()),
|
regExp.hasMatch, 'validations.key_format'.tr(),),
|
||||||
LengthStringNotEqualValidation(64)
|
LengthStringNotEqualValidation(64)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -34,7 +36,7 @@ class HetznerFormCubit extends FormCubit {
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
late bool isKeyValid;
|
late bool isKeyValid;
|
||||||
HetznerApi apiClient = HetznerApi(isWithToken: false);
|
final HetznerApi apiClient = HetznerApi(isWithToken: false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
isKeyValid = await apiClient.isValid(apiKey.state.value);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
@ -7,7 +9,7 @@ import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
|
||||||
class RootUserFormCubit extends FormCubit {
|
class RootUserFormCubit extends FormCubit {
|
||||||
RootUserFormCubit(
|
RootUserFormCubit(
|
||||||
this.serverInstallationCubit, final FieldCubitFactory fieldFactory) {
|
this.serverInstallationCubit, final FieldCubitFactory fieldFactory,) {
|
||||||
userName = fieldFactory.createUserLoginField();
|
userName = fieldFactory.createUserLoginField();
|
||||||
password = fieldFactory.createUserPasswordField();
|
password = fieldFactory.createUserPasswordField();
|
||||||
|
|
||||||
|
@ -18,7 +20,7 @@ class RootUserFormCubit extends FormCubit {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<void> onSubmit() async {
|
FutureOr<void> onSubmit() async {
|
||||||
var user = User(
|
final User user = User(
|
||||||
login: userName.state.value,
|
login: userName.state.value,
|
||||||
password: password.state.value,
|
password: password.state.value,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
@ -6,7 +8,7 @@ import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart
|
||||||
|
|
||||||
class RecoveryDeviceFormCubit extends FormCubit {
|
class RecoveryDeviceFormCubit extends FormCubit {
|
||||||
RecoveryDeviceFormCubit(this.installationCubit,
|
RecoveryDeviceFormCubit(this.installationCubit,
|
||||||
final FieldCubitFactory fieldFactory, this.recoveryMethod) {
|
final FieldCubitFactory fieldFactory, this.recoveryMethod,) {
|
||||||
tokenField = fieldFactory.createRequiredStringField();
|
tokenField = fieldFactory.createRequiredStringField();
|
||||||
|
|
||||||
super.addFields([tokenField]);
|
super.addFields([tokenField]);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
@ -8,7 +10,7 @@ import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart
|
||||||
|
|
||||||
class RecoveryDomainFormCubit extends FormCubit {
|
class RecoveryDomainFormCubit extends FormCubit {
|
||||||
RecoveryDomainFormCubit(
|
RecoveryDomainFormCubit(
|
||||||
this.initializingCubit, final FieldCubitFactory fieldFactory) {
|
this.initializingCubit, final FieldCubitFactory fieldFactory,) {
|
||||||
serverDomainField = fieldFactory.createRequiredStringField();
|
serverDomainField = fieldFactory.createRequiredStringField();
|
||||||
|
|
||||||
super.addFields([serverDomainField]);
|
super.addFields([serverDomainField]);
|
||||||
|
@ -22,10 +24,10 @@ class RecoveryDomainFormCubit extends FormCubit {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<bool> asyncValidation() async {
|
FutureOr<bool> asyncValidation() async {
|
||||||
var api = ServerApi(
|
final ServerApi api = ServerApi(
|
||||||
hasLogger: false,
|
hasLogger: false,
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
overrideDomain: serverDomainField.state.value);
|
overrideDomain: serverDomainField.state.value,);
|
||||||
|
|
||||||
// API version doesn't require access token,
|
// API version doesn't require access token,
|
||||||
// so if the entered domain is indeed valid
|
// so if the entered domain is indeed valid
|
||||||
|
@ -40,7 +42,7 @@ class RecoveryDomainFormCubit extends FormCubit {
|
||||||
return domainValid;
|
return domainValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> setCustomError(String error) {
|
FutureOr<void> setCustomError(final String error) {
|
||||||
serverDomainField.setError(error);
|
serverDomainField.setError(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
@ -11,22 +13,22 @@ class SshFormCubit extends FormCubit {
|
||||||
required this.jobsCubit,
|
required this.jobsCubit,
|
||||||
required this.user,
|
required this.user,
|
||||||
}) {
|
}) {
|
||||||
var keyRegExp = RegExp(
|
final RegExp keyRegExp = RegExp(
|
||||||
r'^(ssh-rsa AAAAB3NzaC1yc2|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5)[0-9A-Za-z+/]+[=]{0,3}( .*)?$');
|
r'^(ssh-rsa AAAAB3NzaC1yc2|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5)[0-9A-Za-z+/]+[=]{0,3}( .*)?$',);
|
||||||
|
|
||||||
key = FieldCubit(
|
key = FieldCubit(
|
||||||
initalValue: '',
|
initalValue: '',
|
||||||
validations: [
|
validations: [
|
||||||
ValidationModel(
|
ValidationModel(
|
||||||
(newKey) => user.sshKeys.any((key) => key == newKey),
|
(final String newKey) => user.sshKeys.any((final String key) => key == newKey),
|
||||||
'validations.key_already_exists'.tr(),
|
'validations.key_already_exists'.tr(),
|
||||||
),
|
),
|
||||||
RequiredStringValidation('validations.required'.tr()),
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
ValidationModel<String>((s) {
|
ValidationModel<String>((final String s) {
|
||||||
print(s);
|
print(s);
|
||||||
print(keyRegExp.hasMatch(s));
|
print(keyRegExp.hasMatch(s));
|
||||||
return !keyRegExp.hasMatch(s);
|
return !keyRegExp.hasMatch(s);
|
||||||
}, 'validations.invalid_format'.tr()),
|
}, 'validations.invalid_format'.tr(),),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
|
@ -10,23 +12,23 @@ import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
class UserFormCubit extends FormCubit {
|
class UserFormCubit extends FormCubit {
|
||||||
UserFormCubit({
|
UserFormCubit({
|
||||||
required this.jobsCubit,
|
required this.jobsCubit,
|
||||||
required FieldCubitFactory fieldFactory,
|
required final FieldCubitFactory fieldFactory,
|
||||||
User? user,
|
final User? user,
|
||||||
}) {
|
}) {
|
||||||
var isEdit = user != null;
|
final bool isEdit = user != null;
|
||||||
|
|
||||||
login = fieldFactory.createUserLoginField();
|
login = fieldFactory.createUserLoginField();
|
||||||
login.setValue(isEdit ? user.login : '');
|
login.setValue(isEdit ? user.login : '');
|
||||||
password = fieldFactory.createUserPasswordField();
|
password = fieldFactory.createUserPasswordField();
|
||||||
password.setValue(
|
password.setValue(
|
||||||
isEdit ? (user.password ?? '') : StringGenerators.userPassword());
|
isEdit ? (user.password ?? '') : StringGenerators.userPassword(),);
|
||||||
|
|
||||||
super.addFields([login, password]);
|
super.addFields([login, password]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<void> onSubmit() {
|
FutureOr<void> onSubmit() {
|
||||||
var user = User(
|
final User user = User(
|
||||||
login: login.state.value,
|
login: login.state.value,
|
||||||
password: password.state.value,
|
password: password.state.value,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,28 +1,29 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
abstract class LengthStringValidation extends ValidationModel<String> {
|
abstract class LengthStringValidation extends ValidationModel<String> {
|
||||||
LengthStringValidation(bool Function(String) predicate, String errorMessage)
|
LengthStringValidation(super.predicate, super.errorMessage);
|
||||||
: super(predicate, errorMessage);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String? check(String val) {
|
String? check(final String val) {
|
||||||
var length = val.length;
|
final int length = val.length;
|
||||||
var errorMessage = errorMassage.replaceAll('[]', length.toString());
|
final String errorMessage = errorMassage.replaceAll('[]', length.toString());
|
||||||
return test(val) ? errorMessage : null;
|
return test(val) ? errorMessage : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LengthStringNotEqualValidation extends LengthStringValidation {
|
class LengthStringNotEqualValidation extends LengthStringValidation {
|
||||||
/// String must be equal to [length]
|
/// String must be equal to [length]
|
||||||
LengthStringNotEqualValidation(int length)
|
LengthStringNotEqualValidation(final int length)
|
||||||
: super((n) => n.length != length,
|
: super((final n) => n.length != length,
|
||||||
'validations.length_not_equal'.tr(args: [length.toString()]));
|
'validations.length_not_equal'.tr(args: [length.toString()]),);
|
||||||
}
|
}
|
||||||
|
|
||||||
class LengthStringLongerValidation extends LengthStringValidation {
|
class LengthStringLongerValidation extends LengthStringValidation {
|
||||||
/// String must be shorter than or equal to [length]
|
/// String must be shorter than or equal to [length]
|
||||||
LengthStringLongerValidation(int length)
|
LengthStringLongerValidation(final int length)
|
||||||
: super((n) => n.length > length,
|
: super((final n) => n.length > length,
|
||||||
'validations.length_longer'.tr(args: [length.toString()]));
|
'validations.length_longer'.tr(args: [length.toString()]),);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,19 @@ import 'package:equatable/equatable.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
||||||
|
|
||||||
import 'hetzner_metrics_repository.dart';
|
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_repository.dart';
|
||||||
|
|
||||||
part 'hetzner_metrics_state.dart';
|
part 'hetzner_metrics_state.dart';
|
||||||
|
|
||||||
class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
||||||
HetznerMetricsCubit() : super(const HetznerMetricsLoading(Period.day));
|
HetznerMetricsCubit() : super(const HetznerMetricsLoading(Period.day));
|
||||||
|
|
||||||
final repository = HetznerMetricsRepository();
|
final HetznerMetricsRepository repository = HetznerMetricsRepository();
|
||||||
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
close() {
|
Future<void> close() {
|
||||||
closeTimer();
|
closeTimer();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void changePeriod(Period period) async {
|
void changePeriod(final Period period) async {
|
||||||
closeTimer();
|
closeTimer();
|
||||||
emit(HetznerMetricsLoading(period));
|
emit(HetznerMetricsLoading(period));
|
||||||
load(period);
|
load(period);
|
||||||
|
@ -38,8 +38,8 @@ class HetznerMetricsCubit extends Cubit<HetznerMetricsState> {
|
||||||
load(state.period);
|
load(state.period);
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(Period period) async {
|
void load(final Period period) async {
|
||||||
var newState = await repository.getMetrics(period);
|
final HetznerMetricsLoaded newState = await repository.getMetrics(period);
|
||||||
timer = Timer(
|
timer = Timer(
|
||||||
Duration(seconds: newState.stepInSeconds.toInt()),
|
Duration(seconds: newState.stepInSeconds.toInt()),
|
||||||
() => load(newState.period),
|
() => load(newState.period),
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
import 'package:selfprivacy/logic/models/hetzner_metrics.dart';
|
||||||
|
|
||||||
import 'hetzner_metrics_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/hetzner_metrics/hetzner_metrics_cubit.dart';
|
||||||
|
|
||||||
class HetznerMetricsRepository {
|
class HetznerMetricsRepository {
|
||||||
Future<HetznerMetricsLoaded> getMetrics(Period period) async {
|
Future<HetznerMetricsLoaded> getMetrics(final Period period) async {
|
||||||
var end = DateTime.now();
|
final DateTime end = DateTime.now();
|
||||||
DateTime start;
|
DateTime start;
|
||||||
|
|
||||||
switch (period) {
|
switch (period) {
|
||||||
|
@ -21,15 +23,15 @@ class HetznerMetricsRepository {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var api = HetznerApi(hasLogger: true);
|
final HetznerApi api = HetznerApi(hasLogger: true);
|
||||||
|
|
||||||
var results = await Future.wait([
|
final List<Map<String, dynamic>> results = await Future.wait([
|
||||||
api.getMetrics(start, end, 'cpu'),
|
api.getMetrics(start, end, 'cpu'),
|
||||||
api.getMetrics(start, end, 'network'),
|
api.getMetrics(start, end, 'network'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var cpuMetricsData = results[0]['metrics'];
|
final cpuMetricsData = results[0]['metrics'];
|
||||||
var networkMetricsData = results[1]['metrics'];
|
final networkMetricsData = results[1]['metrics'];
|
||||||
|
|
||||||
return HetznerMetricsLoaded(
|
return HetznerMetricsLoaded(
|
||||||
period: period,
|
period: period,
|
||||||
|
@ -50,7 +52,7 @@ class HetznerMetricsRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<TimeSeriesData> timeSeriesSerializer(
|
List<TimeSeriesData> timeSeriesSerializer(
|
||||||
Map<String, dynamic> json, String type) {
|
final Map<String, dynamic> json, final String type,) {
|
||||||
List list = json['time_series'][type]['values'];
|
final List list = json['time_series'][type]['values'];
|
||||||
return list.map((el) => TimeSeriesData(el[0], double.parse(el[1]))).toList();
|
return list.map((final el) => TimeSeriesData(el[0], double.parse(el[1]))).toList();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'hetzner_metrics_cubit.dart';
|
part of 'hetzner_metrics_cubit.dart';
|
||||||
|
|
||||||
abstract class HetznerMetricsState extends Equatable {
|
abstract class HetznerMetricsState extends Equatable {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -17,12 +19,12 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
required this.servicesCubit,
|
required this.servicesCubit,
|
||||||
}) : super(JobsStateEmpty());
|
}) : super(JobsStateEmpty());
|
||||||
|
|
||||||
final api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
final UsersCubit usersCubit;
|
final UsersCubit usersCubit;
|
||||||
final ServicesCubit servicesCubit;
|
final ServicesCubit servicesCubit;
|
||||||
|
|
||||||
void addJob(Job job) {
|
void addJob(final Job job) {
|
||||||
var newJobsList = <Job>[];
|
final List<Job> newJobsList = <Job>[];
|
||||||
if (state is JobsStateWithJobs) {
|
if (state is JobsStateWithJobs) {
|
||||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||||
}
|
}
|
||||||
|
@ -31,21 +33,21 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
emit(JobsStateWithJobs(newJobsList));
|
emit(JobsStateWithJobs(newJobsList));
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeJob(String id) {
|
void removeJob(final String id) {
|
||||||
final newState = (state as JobsStateWithJobs).removeById(id);
|
final JobsState newState = (state as JobsStateWithJobs).removeById(id);
|
||||||
emit(newState);
|
emit(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
void createOrRemoveServiceToggleJob(ToggleJob job) {
|
void createOrRemoveServiceToggleJob(final ToggleJob job) {
|
||||||
var newJobsList = <Job>[];
|
final List<Job> newJobsList = <Job>[];
|
||||||
if (state is JobsStateWithJobs) {
|
if (state is JobsStateWithJobs) {
|
||||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||||
}
|
}
|
||||||
var needToRemoveJob =
|
final bool needToRemoveJob =
|
||||||
newJobsList.any((el) => el is ServiceToggleJob && el.type == job.type);
|
newJobsList.any((final el) => el is ServiceToggleJob && el.type == job.type);
|
||||||
if (needToRemoveJob) {
|
if (needToRemoveJob) {
|
||||||
var removingJob = newJobsList
|
final Job removingJob = newJobsList
|
||||||
.firstWhere(((el) => el is ServiceToggleJob && el.type == job.type));
|
.firstWhere((final el) => el is ServiceToggleJob && el.type == job.type);
|
||||||
removeJob(removingJob.id);
|
removeJob(removingJob.id);
|
||||||
} else {
|
} else {
|
||||||
newJobsList.add(job);
|
newJobsList.add(job);
|
||||||
|
@ -54,12 +56,12 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void createShhJobIfNotExist(CreateSSHKeyJob job) {
|
void createShhJobIfNotExist(final CreateSSHKeyJob job) {
|
||||||
var newJobsList = <Job>[];
|
final List<Job> newJobsList = <Job>[];
|
||||||
if (state is JobsStateWithJobs) {
|
if (state is JobsStateWithJobs) {
|
||||||
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
newJobsList.addAll((state as JobsStateWithJobs).jobList);
|
||||||
}
|
}
|
||||||
var isExistInJobList = newJobsList.any((el) => el is CreateSSHKeyJob);
|
final bool isExistInJobList = newJobsList.any((final el) => el is CreateSSHKeyJob);
|
||||||
if (!isExistInJobList) {
|
if (!isExistInJobList) {
|
||||||
newJobsList.add(job);
|
newJobsList.add(job);
|
||||||
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
|
getIt<NavigationService>().showSnackBar('jobs.jobAdded'.tr());
|
||||||
|
@ -69,7 +71,7 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
|
|
||||||
Future<void> rebootServer() async {
|
Future<void> rebootServer() async {
|
||||||
emit(JobsStateLoading());
|
emit(JobsStateLoading());
|
||||||
final isSuccessful = await api.reboot();
|
final bool isSuccessful = await api.reboot();
|
||||||
if (isSuccessful) {
|
if (isSuccessful) {
|
||||||
getIt<NavigationService>().showSnackBar('jobs.rebootSuccess'.tr());
|
getIt<NavigationService>().showSnackBar('jobs.rebootSuccess'.tr());
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,8 +82,8 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
|
|
||||||
Future<void> upgradeServer() async {
|
Future<void> upgradeServer() async {
|
||||||
emit(JobsStateLoading());
|
emit(JobsStateLoading());
|
||||||
final isPullSuccessful = await api.pullConfigurationUpdate();
|
final bool isPullSuccessful = await api.pullConfigurationUpdate();
|
||||||
final isSuccessful = await api.upgrade();
|
final bool isSuccessful = await api.upgrade();
|
||||||
if (isSuccessful) {
|
if (isSuccessful) {
|
||||||
if (!isPullSuccessful) {
|
if (!isPullSuccessful) {
|
||||||
getIt<NavigationService>().showSnackBar('jobs.configPullFailed'.tr());
|
getIt<NavigationService>().showSnackBar('jobs.configPullFailed'.tr());
|
||||||
|
@ -96,10 +98,10 @@ class JobsCubit extends Cubit<JobsState> {
|
||||||
|
|
||||||
Future<void> applyAll() async {
|
Future<void> applyAll() async {
|
||||||
if (state is JobsStateWithJobs) {
|
if (state is JobsStateWithJobs) {
|
||||||
var jobs = (state as JobsStateWithJobs).jobList;
|
final List<Job> jobs = (state as JobsStateWithJobs).jobList;
|
||||||
emit(JobsStateLoading());
|
emit(JobsStateLoading());
|
||||||
var hasServiceJobs = false;
|
bool hasServiceJobs = false;
|
||||||
for (var job in jobs) {
|
for (final Job job in jobs) {
|
||||||
if (job is CreateUserJob) {
|
if (job is CreateUserJob) {
|
||||||
await usersCubit.createUser(job.user);
|
await usersCubit.createUser(job.user);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'jobs_cubit.dart';
|
part of 'jobs_cubit.dart';
|
||||||
|
|
||||||
abstract class JobsState extends Equatable {
|
abstract class JobsState extends Equatable {
|
||||||
|
@ -13,8 +15,8 @@ class JobsStateWithJobs extends JobsState {
|
||||||
JobsStateWithJobs(this.jobList);
|
JobsStateWithJobs(this.jobList);
|
||||||
final List<Job> jobList;
|
final List<Job> jobList;
|
||||||
|
|
||||||
JobsState removeById(String id) {
|
JobsState removeById(final String id) {
|
||||||
var newJobsList = jobList.where((element) => element.id != id).toList();
|
final List<Job> newJobsList = jobList.where((final element) => element.id != id).toList();
|
||||||
|
|
||||||
if (newJobsList.isEmpty) {
|
if (newJobsList.isEmpty) {
|
||||||
return JobsStateEmpty();
|
return JobsStateEmpty();
|
||||||
|
|
|
@ -11,8 +11,8 @@ part 'providers_state.dart';
|
||||||
class ProvidersCubit extends Cubit<ProvidersState> {
|
class ProvidersCubit extends Cubit<ProvidersState> {
|
||||||
ProvidersCubit() : super(InitialProviderState());
|
ProvidersCubit() : super(InitialProviderState());
|
||||||
|
|
||||||
void connect(ProviderModel provider) {
|
void connect(final ProviderModel provider) {
|
||||||
var newState = state.updateElement(provider, StateType.stable);
|
final ProvidersState newState = state.updateElement(provider, StateType.stable);
|
||||||
emit(newState);
|
emit(newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'providers_cubit.dart';
|
part of 'providers_cubit.dart';
|
||||||
|
|
||||||
class ProvidersState extends Equatable {
|
class ProvidersState extends Equatable {
|
||||||
|
@ -5,18 +7,18 @@ class ProvidersState extends Equatable {
|
||||||
|
|
||||||
final List<ProviderModel> all;
|
final List<ProviderModel> all;
|
||||||
|
|
||||||
ProvidersState updateElement(ProviderModel provider, StateType newState) {
|
ProvidersState updateElement(final ProviderModel provider, final StateType newState) {
|
||||||
var newList = [...all];
|
final List<ProviderModel> newList = [...all];
|
||||||
var index = newList.indexOf(provider);
|
final int index = newList.indexOf(provider);
|
||||||
newList[index] = provider.updateState(newState);
|
newList[index] = provider.updateState(newState);
|
||||||
return ProvidersState(newList);
|
return ProvidersState(newList);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<ProviderModel> get connected =>
|
List<ProviderModel> get connected =>
|
||||||
all.where((service) => service.state != StateType.uninitialized).toList();
|
all.where((final service) => service.state != StateType.uninitialized).toList();
|
||||||
|
|
||||||
List<ProviderModel> get uninitialized =>
|
List<ProviderModel> get uninitialized =>
|
||||||
all.where((service) => service.state == StateType.uninitialized).toList();
|
all.where((final service) => service.state == StateType.uninitialized).toList();
|
||||||
|
|
||||||
bool get isFullyInitialized => uninitialized.isEmpty;
|
bool get isFullyInitialized => uninitialized.isEmpty;
|
||||||
|
|
||||||
|
@ -29,7 +31,7 @@ class InitialProviderState extends ProvidersState {
|
||||||
: super(
|
: super(
|
||||||
ProviderType.values
|
ProviderType.values
|
||||||
.map(
|
.map(
|
||||||
(type) => ProviderModel(
|
(final type) => ProviderModel(
|
||||||
state: StateType.uninitialized,
|
state: StateType.uninitialized,
|
||||||
type: type,
|
type: type,
|
||||||
),
|
),
|
||||||
|
|
|
@ -7,20 +7,20 @@ part 'recovery_key_state.dart';
|
||||||
|
|
||||||
class RecoveryKeyCubit
|
class RecoveryKeyCubit
|
||||||
extends ServerInstallationDependendCubit<RecoveryKeyState> {
|
extends ServerInstallationDependendCubit<RecoveryKeyState> {
|
||||||
RecoveryKeyCubit(ServerInstallationCubit serverInstallationCubit)
|
RecoveryKeyCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(serverInstallationCubit, const RecoveryKeyState.initial());
|
: super(serverInstallationCubit, const RecoveryKeyState.initial());
|
||||||
|
|
||||||
final api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void load() async {
|
void load() async {
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
final status = await _getRecoveryKeyStatus();
|
final RecoveryKeyStatus? status = await _getRecoveryKeyStatus();
|
||||||
if (status == null) {
|
if (status == null) {
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.error));
|
emit(state.copyWith(loadingStatus: LoadingStatus.error));
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
status: status, loadingStatus: LoadingStatus.success));
|
status: status, loadingStatus: LoadingStatus.success,),);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized));
|
emit(state.copyWith(loadingStatus: LoadingStatus.uninitialized));
|
||||||
|
@ -39,18 +39,18 @@ class RecoveryKeyCubit
|
||||||
|
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.refreshing));
|
emit(state.copyWith(loadingStatus: LoadingStatus.refreshing));
|
||||||
final status = await _getRecoveryKeyStatus();
|
final RecoveryKeyStatus? status = await _getRecoveryKeyStatus();
|
||||||
if (status == null) {
|
if (status == null) {
|
||||||
emit(state.copyWith(loadingStatus: LoadingStatus.error));
|
emit(state.copyWith(loadingStatus: LoadingStatus.error));
|
||||||
} else {
|
} else {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(status: status, loadingStatus: LoadingStatus.success));
|
state.copyWith(status: status, loadingStatus: LoadingStatus.success),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> generateRecoveryKey({
|
Future<String> generateRecoveryKey({
|
||||||
DateTime? expirationDate,
|
final DateTime? expirationDate,
|
||||||
int? numberOfUses,
|
final int? numberOfUses,
|
||||||
}) async {
|
}) async {
|
||||||
final ApiResponse<String> response =
|
final ApiResponse<String> response =
|
||||||
await api.generateRecoveryToken(expirationDate, numberOfUses);
|
await api.generateRecoveryToken(expirationDate, numberOfUses);
|
||||||
|
@ -69,7 +69,7 @@ class RecoveryKeyCubit
|
||||||
}
|
}
|
||||||
|
|
||||||
class GenerationError extends Error {
|
class GenerationError extends Error {
|
||||||
final String message;
|
|
||||||
|
|
||||||
GenerationError(this.message);
|
GenerationError(this.message);
|
||||||
|
final String message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'recovery_key_cubit.dart';
|
part of 'recovery_key_cubit.dart';
|
||||||
|
|
||||||
class RecoveryKeyState extends ServerInstallationDependendState {
|
class RecoveryKeyState extends ServerInstallationDependendState {
|
||||||
|
@ -5,7 +7,7 @@ class RecoveryKeyState extends ServerInstallationDependendState {
|
||||||
|
|
||||||
const RecoveryKeyState.initial()
|
const RecoveryKeyState.initial()
|
||||||
: this(const RecoveryKeyStatus(exists: false, valid: false),
|
: this(const RecoveryKeyStatus(exists: false, valid: false),
|
||||||
LoadingStatus.refreshing);
|
LoadingStatus.refreshing,);
|
||||||
|
|
||||||
final RecoveryKeyStatus _status;
|
final RecoveryKeyStatus _status;
|
||||||
final LoadingStatus loadingStatus;
|
final LoadingStatus loadingStatus;
|
||||||
|
@ -19,12 +21,10 @@ class RecoveryKeyState extends ServerInstallationDependendState {
|
||||||
List<Object> get props => [_status, loadingStatus];
|
List<Object> get props => [_status, loadingStatus];
|
||||||
|
|
||||||
RecoveryKeyState copyWith({
|
RecoveryKeyState copyWith({
|
||||||
RecoveryKeyStatus? status,
|
final RecoveryKeyStatus? status,
|
||||||
LoadingStatus? loadingStatus,
|
final LoadingStatus? loadingStatus,
|
||||||
}) {
|
}) => RecoveryKeyState(
|
||||||
return RecoveryKeyState(
|
|
||||||
status ?? _status,
|
status ?? _status,
|
||||||
loadingStatus ?? this.loadingStatus,
|
loadingStatus ?? this.loadingStatus,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,16 @@ class ServerDetailsCubit extends Cubit<ServerDetailsState> {
|
||||||
ServerDetailsRepository repository = ServerDetailsRepository();
|
ServerDetailsRepository repository = ServerDetailsRepository();
|
||||||
|
|
||||||
void check() async {
|
void check() async {
|
||||||
var isReadyToCheck = getIt<ApiConfigModel>().serverDetails != null;
|
final bool isReadyToCheck = getIt<ApiConfigModel>().serverDetails != null;
|
||||||
if (isReadyToCheck) {
|
if (isReadyToCheck) {
|
||||||
emit(ServerDetailsLoading());
|
emit(ServerDetailsLoading());
|
||||||
var data = await repository.load();
|
final ServerDetailsRepositoryDto data = await repository.load();
|
||||||
emit(Loaded(
|
emit(Loaded(
|
||||||
serverInfo: data.hetznerServerInfo,
|
serverInfo: data.hetznerServerInfo,
|
||||||
autoUpgradeSettings: data.autoUpgradeSettings,
|
autoUpgradeSettings: data.autoUpgradeSettings,
|
||||||
serverTimezone: data.serverTimezone,
|
serverTimezone: data.serverTimezone,
|
||||||
checkTime: DateTime.now(),
|
checkTime: DateTime.now(),
|
||||||
));
|
),);
|
||||||
} else {
|
} else {
|
||||||
emit(ServerDetailsNotReady());
|
emit(ServerDetailsNotReady());
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
import 'package:selfprivacy/logic/models/timezone_settings.dart';
|
||||||
|
|
||||||
class ServerDetailsRepository {
|
class ServerDetailsRepository {
|
||||||
var hetznerAPi = HetznerApi();
|
HetznerApi hetznerAPi = HetznerApi();
|
||||||
var selfprivacyServer = ServerApi();
|
ServerApi selfprivacyServer = ServerApi();
|
||||||
|
|
||||||
Future<ServerDetailsRepositoryDto> load() async {
|
Future<ServerDetailsRepositoryDto> load() async {
|
||||||
print('load');
|
print('load');
|
||||||
|
@ -19,15 +19,15 @@ class ServerDetailsRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerDetailsRepositoryDto {
|
class ServerDetailsRepositoryDto {
|
||||||
final HetznerServerInfo hetznerServerInfo;
|
|
||||||
|
|
||||||
final TimeZoneSettings serverTimezone;
|
|
||||||
|
|
||||||
final AutoUpgradeSettings autoUpgradeSettings;
|
|
||||||
|
|
||||||
ServerDetailsRepositoryDto({
|
ServerDetailsRepositoryDto({
|
||||||
required this.hetznerServerInfo,
|
required this.hetznerServerInfo,
|
||||||
required this.serverTimezone,
|
required this.serverTimezone,
|
||||||
required this.autoUpgradeSettings,
|
required this.autoUpgradeSettings,
|
||||||
});
|
});
|
||||||
|
final HetznerServerInfo hetznerServerInfo;
|
||||||
|
|
||||||
|
final TimeZoneSettings serverTimezone;
|
||||||
|
|
||||||
|
final AutoUpgradeSettings autoUpgradeSettings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'server_detailed_info_cubit.dart';
|
part of 'server_detailed_info_cubit.dart';
|
||||||
|
|
||||||
abstract class ServerDetailsState extends Equatable {
|
abstract class ServerDetailsState extends Equatable {
|
||||||
|
@ -16,12 +18,6 @@ class ServerDetailsNotReady extends ServerDetailsState {}
|
||||||
class Loading extends ServerDetailsState {}
|
class Loading extends ServerDetailsState {}
|
||||||
|
|
||||||
class Loaded extends ServerDetailsState {
|
class Loaded extends ServerDetailsState {
|
||||||
final HetznerServerInfo serverInfo;
|
|
||||||
|
|
||||||
final TimeZoneSettings serverTimezone;
|
|
||||||
|
|
||||||
final AutoUpgradeSettings autoUpgradeSettings;
|
|
||||||
final DateTime checkTime;
|
|
||||||
|
|
||||||
const Loaded({
|
const Loaded({
|
||||||
required this.serverInfo,
|
required this.serverInfo,
|
||||||
|
@ -29,6 +25,12 @@ class Loaded extends ServerDetailsState {
|
||||||
required this.autoUpgradeSettings,
|
required this.autoUpgradeSettings,
|
||||||
required this.checkTime,
|
required this.checkTime,
|
||||||
});
|
});
|
||||||
|
final HetznerServerInfo serverInfo;
|
||||||
|
|
||||||
|
final TimeZoneSettings serverTimezone;
|
||||||
|
|
||||||
|
final AutoUpgradeSettings autoUpgradeSettings;
|
||||||
|
final DateTime checkTime;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [
|
List<Object> get props => [
|
||||||
|
|
|
@ -10,7 +10,7 @@ import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
|
|
||||||
import '../server_installation/server_installation_repository.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_repository.dart';
|
||||||
|
|
||||||
export 'package:provider/provider.dart';
|
export 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
@ -19,12 +19,13 @@ part '../server_installation/server_installation_state.dart';
|
||||||
class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
ServerInstallationCubit() : super(const ServerInstallationEmpty());
|
ServerInstallationCubit() : super(const ServerInstallationEmpty());
|
||||||
|
|
||||||
final repository = ServerInstallationRepository();
|
final ServerInstallationRepository repository =
|
||||||
|
ServerInstallationRepository();
|
||||||
|
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
var state = await repository.load();
|
final ServerInstallationState state = await repository.load();
|
||||||
|
|
||||||
if (state is ServerInstallationFinished) {
|
if (state is ServerInstallationFinished) {
|
||||||
emit(state);
|
emit(state);
|
||||||
|
@ -48,33 +49,38 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setHetznerKey(String hetznerKey) async {
|
void setHetznerKey(final String hetznerKey) async {
|
||||||
await repository.saveHetznerKey(hetznerKey);
|
await repository.saveHetznerKey(hetznerKey);
|
||||||
|
|
||||||
if (state is ServerInstallationRecovery) {
|
if (state is ServerInstallationRecovery) {
|
||||||
emit((state as ServerInstallationRecovery).copyWith(
|
emit(
|
||||||
hetznerKey: hetznerKey,
|
(state as ServerInstallationRecovery).copyWith(
|
||||||
currentStep: RecoveryStep.serverSelection,
|
hetznerKey: hetznerKey,
|
||||||
));
|
currentStep: RecoveryStep.serverSelection,
|
||||||
|
),
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit((state as ServerInstallationNotFinished)
|
emit(
|
||||||
.copyWith(hetznerKey: hetznerKey));
|
(state as ServerInstallationNotFinished).copyWith(hetznerKey: hetznerKey),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCloudflareKey(String cloudFlareKey) async {
|
void setCloudflareKey(final String cloudFlareKey) async {
|
||||||
if (state is ServerInstallationRecovery) {
|
if (state is ServerInstallationRecovery) {
|
||||||
setAndValidateCloudflareToken(cloudFlareKey);
|
setAndValidateCloudflareToken(cloudFlareKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await repository.saveCloudFlareKey(cloudFlareKey);
|
await repository.saveCloudFlareKey(cloudFlareKey);
|
||||||
emit((state as ServerInstallationNotFinished)
|
emit(
|
||||||
.copyWith(cloudFlareKey: cloudFlareKey));
|
(state as ServerInstallationNotFinished)
|
||||||
|
.copyWith(cloudFlareKey: cloudFlareKey),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBackblazeKey(String keyId, String applicationKey) async {
|
void setBackblazeKey(final String keyId, final String applicationKey) async {
|
||||||
var backblazeCredential = BackblazeCredential(
|
final BackblazeCredential backblazeCredential = BackblazeCredential(
|
||||||
keyId: keyId,
|
keyId: keyId,
|
||||||
applicationKey: applicationKey,
|
applicationKey: applicationKey,
|
||||||
);
|
);
|
||||||
|
@ -83,38 +89,45 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
finishRecoveryProcess(backblazeCredential);
|
finishRecoveryProcess(backblazeCredential);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emit((state as ServerInstallationNotFinished)
|
emit(
|
||||||
.copyWith(backblazeCredential: backblazeCredential));
|
(state as ServerInstallationNotFinished)
|
||||||
|
.copyWith(backblazeCredential: backblazeCredential),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDomain(ServerDomain serverDomain) async {
|
void setDomain(final ServerDomain serverDomain) async {
|
||||||
await repository.saveDomain(serverDomain);
|
await repository.saveDomain(serverDomain);
|
||||||
emit((state as ServerInstallationNotFinished)
|
emit(
|
||||||
.copyWith(serverDomain: serverDomain));
|
(state as ServerInstallationNotFinished)
|
||||||
|
.copyWith(serverDomain: serverDomain),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRootUser(User rootUser) async {
|
void setRootUser(final User rootUser) async {
|
||||||
await repository.saveRootUser(rootUser);
|
await repository.saveRootUser(rootUser);
|
||||||
emit((state as ServerInstallationNotFinished).copyWith(rootUser: rootUser));
|
emit((state as ServerInstallationNotFinished).copyWith(rootUser: rootUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
void createServerAndSetDnsRecords() async {
|
void createServerAndSetDnsRecords() async {
|
||||||
ServerInstallationNotFinished stateCopy =
|
final ServerInstallationNotFinished stateCopy =
|
||||||
state as ServerInstallationNotFinished;
|
state as ServerInstallationNotFinished;
|
||||||
onCancel() => emit(
|
void onCancel() => emit(
|
||||||
(state as ServerInstallationNotFinished).copyWith(isLoading: false));
|
(state as ServerInstallationNotFinished).copyWith(isLoading: false),
|
||||||
|
);
|
||||||
|
|
||||||
onSuccess(ServerHostingDetails serverDetails) async {
|
Future<void> onSuccess(final ServerHostingDetails serverDetails) async {
|
||||||
await repository.createDnsRecords(
|
await repository.createDnsRecords(
|
||||||
serverDetails.ip4,
|
serverDetails.ip4,
|
||||||
state.serverDomain!,
|
state.serverDomain!,
|
||||||
onCancel: onCancel,
|
onCancel: onCancel,
|
||||||
);
|
);
|
||||||
|
|
||||||
emit((state as ServerInstallationNotFinished).copyWith(
|
emit(
|
||||||
isLoading: false,
|
(state as ServerInstallationNotFinished).copyWith(
|
||||||
serverDetails: serverDetails,
|
isLoading: false,
|
||||||
));
|
serverDetails: serverDetails,
|
||||||
|
),
|
||||||
|
);
|
||||||
runDelayed(startServerIfDnsIsOkay, const Duration(seconds: 30), null);
|
runDelayed(startServerIfDnsIsOkay, const Duration(seconds: 30), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,125 +146,149 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void startServerIfDnsIsOkay({ServerInstallationNotFinished? state}) async {
|
void startServerIfDnsIsOkay(
|
||||||
final dataState = state ?? this.state as ServerInstallationNotFinished;
|
{final ServerInstallationNotFinished? state,}) async {
|
||||||
|
final ServerInstallationNotFinished dataState =
|
||||||
|
state ?? this.state as ServerInstallationNotFinished;
|
||||||
|
|
||||||
emit(TimerState(dataState: dataState, isLoading: true));
|
emit(TimerState(dataState: dataState, isLoading: true));
|
||||||
|
|
||||||
var ip4 = dataState.serverDetails!.ip4;
|
final String ip4 = dataState.serverDetails!.ip4;
|
||||||
var domainName = dataState.serverDomain!.domainName;
|
final String domainName = dataState.serverDomain!.domainName;
|
||||||
|
|
||||||
var matches = await repository.isDnsAddressesMatch(
|
final Map<String, bool> matches = await repository.isDnsAddressesMatch(
|
||||||
domainName, ip4, dataState.dnsMatches);
|
domainName,
|
||||||
|
ip4,
|
||||||
|
dataState.dnsMatches,
|
||||||
|
);
|
||||||
|
|
||||||
if (matches.values.every((value) => value)) {
|
if (matches.values.every((final bool value) => value)) {
|
||||||
var server = await repository.startServer(
|
final ServerHostingDetails server = await repository.startServer(
|
||||||
dataState.serverDetails!,
|
dataState.serverDetails!,
|
||||||
);
|
);
|
||||||
await repository.saveServerDetails(server);
|
await repository.saveServerDetails(server);
|
||||||
await repository.saveIsServerStarted(true);
|
await repository.saveIsServerStarted(true);
|
||||||
|
|
||||||
emit(
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
dataState.copyWith(
|
isServerStarted: true,
|
||||||
isServerStarted: true,
|
isLoading: false,
|
||||||
isLoading: false,
|
serverDetails: server,
|
||||||
serverDetails: server,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
emit(newState);
|
||||||
runDelayed(
|
runDelayed(
|
||||||
resetServerIfServerIsOkay, const Duration(seconds: 60), dataState);
|
resetServerIfServerIsOkay,
|
||||||
|
const Duration(seconds: 60),
|
||||||
|
newState,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
emit(
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
dataState.copyWith(
|
isLoading: false,
|
||||||
isLoading: false,
|
dnsMatches: matches,
|
||||||
dnsMatches: matches,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
emit(newState);
|
||||||
runDelayed(
|
runDelayed(
|
||||||
startServerIfDnsIsOkay, const Duration(seconds: 30), dataState);
|
startServerIfDnsIsOkay,
|
||||||
|
const Duration(seconds: 30),
|
||||||
|
newState,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void oneMoreReset({ServerInstallationNotFinished? state}) async {
|
void resetServerIfServerIsOkay({
|
||||||
final dataState = state ?? this.state as ServerInstallationNotFinished;
|
final ServerInstallationNotFinished? state,
|
||||||
|
}) async {
|
||||||
|
final ServerInstallationNotFinished dataState =
|
||||||
|
state ?? this.state as ServerInstallationNotFinished;
|
||||||
|
|
||||||
emit(TimerState(dataState: dataState, isLoading: true));
|
emit(TimerState(dataState: dataState, isLoading: true));
|
||||||
|
|
||||||
var isServerWorking = await repository.isHttpServerWorking();
|
final bool isServerWorking = await repository.isHttpServerWorking();
|
||||||
|
|
||||||
if (isServerWorking) {
|
if (isServerWorking) {
|
||||||
var pauseDuration = const Duration(seconds: 30);
|
const Duration pauseDuration = Duration(seconds: 30);
|
||||||
emit(TimerState(
|
emit(
|
||||||
dataState: dataState,
|
TimerState(
|
||||||
timerStart: DateTime.now(),
|
dataState: dataState,
|
||||||
isLoading: false,
|
timerStart: DateTime.now(),
|
||||||
duration: pauseDuration,
|
isLoading: false,
|
||||||
));
|
duration: pauseDuration,
|
||||||
|
),
|
||||||
|
);
|
||||||
timer = Timer(pauseDuration, () async {
|
timer = Timer(pauseDuration, () async {
|
||||||
var hetznerServerDetails = await repository.restart();
|
final ServerHostingDetails hetznerServerDetails =
|
||||||
|
await repository.restart();
|
||||||
|
await repository.saveIsServerResetedFirstTime(true);
|
||||||
|
await repository.saveServerDetails(hetznerServerDetails);
|
||||||
|
|
||||||
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
|
isServerResetedFirstTime: true,
|
||||||
|
serverDetails: hetznerServerDetails,
|
||||||
|
isLoading: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
emit(newState);
|
||||||
|
runDelayed(oneMoreReset, const Duration(seconds: 60), newState);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
runDelayed(
|
||||||
|
resetServerIfServerIsOkay,
|
||||||
|
const Duration(seconds: 60),
|
||||||
|
dataState,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oneMoreReset({final ServerInstallationNotFinished? state}) async {
|
||||||
|
final ServerInstallationNotFinished dataState =
|
||||||
|
state ?? this.state as ServerInstallationNotFinished;
|
||||||
|
|
||||||
|
emit(TimerState(dataState: dataState, isLoading: true));
|
||||||
|
|
||||||
|
final bool isServerWorking = await repository.isHttpServerWorking();
|
||||||
|
|
||||||
|
if (isServerWorking) {
|
||||||
|
const Duration pauseDuration = Duration(seconds: 30);
|
||||||
|
emit(
|
||||||
|
TimerState(
|
||||||
|
dataState: dataState,
|
||||||
|
timerStart: DateTime.now(),
|
||||||
|
isLoading: false,
|
||||||
|
duration: pauseDuration,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
timer = Timer(pauseDuration, () async {
|
||||||
|
final ServerHostingDetails hetznerServerDetails =
|
||||||
|
await repository.restart();
|
||||||
await repository.saveIsServerResetedSecondTime(true);
|
await repository.saveIsServerResetedSecondTime(true);
|
||||||
await repository.saveServerDetails(hetznerServerDetails);
|
await repository.saveServerDetails(hetznerServerDetails);
|
||||||
|
|
||||||
emit(
|
final ServerInstallationNotFinished newState = dataState.copyWith(
|
||||||
dataState.copyWith(
|
isServerResetedSecondTime: true,
|
||||||
isServerResetedSecondTime: true,
|
serverDetails: hetznerServerDetails,
|
||||||
serverDetails: hetznerServerDetails,
|
isLoading: false,
|
||||||
isLoading: false,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
emit(newState);
|
||||||
runDelayed(
|
runDelayed(
|
||||||
finishCheckIfServerIsOkay, const Duration(seconds: 60), dataState);
|
finishCheckIfServerIsOkay,
|
||||||
|
const Duration(seconds: 60),
|
||||||
|
newState,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
runDelayed(oneMoreReset, const Duration(seconds: 60), dataState);
|
runDelayed(oneMoreReset, const Duration(seconds: 60), dataState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void resetServerIfServerIsOkay({
|
|
||||||
ServerInstallationNotFinished? state,
|
|
||||||
}) async {
|
|
||||||
final dataState = state ?? this.state as ServerInstallationNotFinished;
|
|
||||||
|
|
||||||
emit(TimerState(dataState: dataState, isLoading: true));
|
|
||||||
|
|
||||||
var isServerWorking = await repository.isHttpServerWorking();
|
|
||||||
|
|
||||||
if (isServerWorking) {
|
|
||||||
var pauseDuration = const Duration(seconds: 30);
|
|
||||||
emit(TimerState(
|
|
||||||
dataState: dataState,
|
|
||||||
timerStart: DateTime.now(),
|
|
||||||
isLoading: false,
|
|
||||||
duration: pauseDuration,
|
|
||||||
));
|
|
||||||
timer = Timer(pauseDuration, () async {
|
|
||||||
var hetznerServerDetails = await repository.restart();
|
|
||||||
await repository.saveIsServerResetedFirstTime(true);
|
|
||||||
await repository.saveServerDetails(hetznerServerDetails);
|
|
||||||
|
|
||||||
emit(
|
|
||||||
dataState.copyWith(
|
|
||||||
isServerResetedFirstTime: true,
|
|
||||||
serverDetails: hetznerServerDetails,
|
|
||||||
isLoading: false,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
runDelayed(oneMoreReset, const Duration(seconds: 60), dataState);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
runDelayed(
|
|
||||||
resetServerIfServerIsOkay, const Duration(seconds: 60), dataState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void finishCheckIfServerIsOkay({
|
void finishCheckIfServerIsOkay({
|
||||||
ServerInstallationNotFinished? state,
|
final ServerInstallationNotFinished? state,
|
||||||
}) async {
|
}) async {
|
||||||
final dataState = state ?? this.state as ServerInstallationNotFinished;
|
final ServerInstallationNotFinished dataState =
|
||||||
|
state ?? this.state as ServerInstallationNotFinished;
|
||||||
|
|
||||||
emit(TimerState(dataState: dataState, isLoading: true));
|
emit(TimerState(dataState: dataState, isLoading: true));
|
||||||
|
|
||||||
var isServerWorking = await repository.isHttpServerWorking();
|
final bool isServerWorking = await repository.isHttpServerWorking();
|
||||||
|
|
||||||
if (isServerWorking) {
|
if (isServerWorking) {
|
||||||
await repository.createDkimRecord(dataState.serverDomain!);
|
await repository.createDkimRecord(dataState.serverDomain!);
|
||||||
|
@ -260,51 +297,67 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
emit(dataState.finish());
|
emit(dataState.finish());
|
||||||
} else {
|
} else {
|
||||||
runDelayed(
|
runDelayed(
|
||||||
finishCheckIfServerIsOkay, const Duration(seconds: 60), dataState);
|
finishCheckIfServerIsOkay,
|
||||||
|
const Duration(seconds: 60),
|
||||||
|
dataState,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void runDelayed(void Function() work, Duration delay,
|
void runDelayed(
|
||||||
ServerInstallationNotFinished? state) async {
|
final void Function() work,
|
||||||
final dataState = state ?? this.state as ServerInstallationNotFinished;
|
final Duration delay,
|
||||||
|
final ServerInstallationNotFinished? state,
|
||||||
|
) async {
|
||||||
|
final ServerInstallationNotFinished dataState =
|
||||||
|
state ?? this.state as ServerInstallationNotFinished;
|
||||||
|
|
||||||
emit(TimerState(
|
emit(
|
||||||
dataState: dataState,
|
TimerState(
|
||||||
timerStart: DateTime.now(),
|
dataState: dataState,
|
||||||
duration: delay,
|
timerStart: DateTime.now(),
|
||||||
isLoading: false,
|
duration: delay,
|
||||||
));
|
isLoading: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
timer = Timer(delay, work);
|
timer = Timer(delay, work);
|
||||||
}
|
}
|
||||||
|
|
||||||
void submitDomainForAccessRecovery(String domain) async {
|
void submitDomainForAccessRecovery(final String domain) async {
|
||||||
var serverDomain = ServerDomain(
|
final ServerDomain serverDomain = ServerDomain(
|
||||||
domainName: domain,
|
domainName: domain,
|
||||||
provider: DnsProvider.unknown,
|
provider: DnsProvider.unknown,
|
||||||
zoneId: '',
|
zoneId: '',
|
||||||
);
|
);
|
||||||
final recoveryCapabilities =
|
final ServerRecoveryCapabilities recoveryCapabilities =
|
||||||
await repository.getRecoveryCapabilities(serverDomain);
|
await repository.getRecoveryCapabilities(serverDomain);
|
||||||
|
|
||||||
await repository.saveDomain(serverDomain);
|
await repository.saveDomain(serverDomain);
|
||||||
await repository.saveIsRecoveringServer(true);
|
await repository.saveIsRecoveringServer(true);
|
||||||
|
|
||||||
emit(ServerInstallationRecovery(
|
emit(
|
||||||
serverDomain: serverDomain,
|
ServerInstallationRecovery(
|
||||||
recoveryCapabilities: recoveryCapabilities,
|
serverDomain: serverDomain,
|
||||||
currentStep: RecoveryStep.selecting,
|
recoveryCapabilities: recoveryCapabilities,
|
||||||
));
|
currentStep: RecoveryStep.selecting,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tryToRecover(String token, ServerRecoveryMethods method) async {
|
void tryToRecover(
|
||||||
final dataState = state as ServerInstallationRecovery;
|
final String token, final ServerRecoveryMethods method,) async {
|
||||||
final serverDomain = dataState.serverDomain;
|
final ServerInstallationRecovery dataState =
|
||||||
|
state as ServerInstallationRecovery;
|
||||||
|
final ServerDomain? serverDomain = dataState.serverDomain;
|
||||||
if (serverDomain == null) {
|
if (serverDomain == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Future<ServerHostingDetails> Function(
|
Future<ServerHostingDetails> Function(
|
||||||
ServerDomain, String, ServerRecoveryCapabilities) recoveryFunction;
|
ServerDomain,
|
||||||
|
String,
|
||||||
|
ServerRecoveryCapabilities,
|
||||||
|
) recoveryFunction;
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case ServerRecoveryMethods.newDeviceKey:
|
case ServerRecoveryMethods.newDeviceKey:
|
||||||
recoveryFunction = repository.authorizeByNewDeviceKey;
|
recoveryFunction = repository.authorizeByNewDeviceKey;
|
||||||
|
@ -318,16 +371,18 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
default:
|
default:
|
||||||
throw Exception('Unknown recovery method');
|
throw Exception('Unknown recovery method');
|
||||||
}
|
}
|
||||||
final serverDetails = await recoveryFunction(
|
final ServerHostingDetails serverDetails = await recoveryFunction(
|
||||||
serverDomain,
|
serverDomain,
|
||||||
token,
|
token,
|
||||||
dataState.recoveryCapabilities,
|
dataState.recoveryCapabilities,
|
||||||
);
|
);
|
||||||
await repository.saveServerDetails(serverDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
emit(dataState.copyWith(
|
emit(
|
||||||
serverDetails: serverDetails,
|
dataState.copyWith(
|
||||||
currentStep: RecoveryStep.hetznerToken,
|
serverDetails: serverDetails,
|
||||||
));
|
currentStep: RecoveryStep.hetznerToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
} on ServerAuthorizationException {
|
} on ServerAuthorizationException {
|
||||||
getIt<NavigationService>()
|
getIt<NavigationService>()
|
||||||
.showSnackBar('recovering.authorization_failed'.tr());
|
.showSnackBar('recovering.authorization_failed'.tr());
|
||||||
|
@ -340,7 +395,8 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void revertRecoveryStep() {
|
void revertRecoveryStep() {
|
||||||
final dataState = state as ServerInstallationRecovery;
|
final ServerInstallationRecovery dataState =
|
||||||
|
state as ServerInstallationRecovery;
|
||||||
switch (dataState.currentStep) {
|
switch (dataState.currentStep) {
|
||||||
case RecoveryStep.selecting:
|
case RecoveryStep.selecting:
|
||||||
repository.deleteDomain();
|
repository.deleteDomain();
|
||||||
|
@ -349,15 +405,19 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
case RecoveryStep.recoveryKey:
|
case RecoveryStep.recoveryKey:
|
||||||
case RecoveryStep.newDeviceKey:
|
case RecoveryStep.newDeviceKey:
|
||||||
case RecoveryStep.oldToken:
|
case RecoveryStep.oldToken:
|
||||||
emit(dataState.copyWith(
|
emit(
|
||||||
currentStep: RecoveryStep.selecting,
|
dataState.copyWith(
|
||||||
));
|
currentStep: RecoveryStep.selecting,
|
||||||
|
),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case RecoveryStep.serverSelection:
|
case RecoveryStep.serverSelection:
|
||||||
repository.deleteHetznerKey();
|
repository.deleteHetznerKey();
|
||||||
emit(dataState.copyWith(
|
emit(
|
||||||
currentStep: RecoveryStep.hetznerToken,
|
dataState.copyWith(
|
||||||
));
|
currentStep: RecoveryStep.hetznerToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
// We won't revert steps after client is authorized
|
// We won't revert steps after client is authorized
|
||||||
default:
|
default:
|
||||||
|
@ -365,48 +425,60 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectRecoveryMethod(ServerRecoveryMethods method) {
|
void selectRecoveryMethod(final ServerRecoveryMethods method) {
|
||||||
final dataState = state as ServerInstallationRecovery;
|
final ServerInstallationRecovery dataState =
|
||||||
|
state as ServerInstallationRecovery;
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case ServerRecoveryMethods.newDeviceKey:
|
case ServerRecoveryMethods.newDeviceKey:
|
||||||
emit(dataState.copyWith(
|
emit(
|
||||||
currentStep: RecoveryStep.newDeviceKey,
|
dataState.copyWith(
|
||||||
));
|
currentStep: RecoveryStep.newDeviceKey,
|
||||||
|
),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case ServerRecoveryMethods.recoveryKey:
|
case ServerRecoveryMethods.recoveryKey:
|
||||||
emit(dataState.copyWith(
|
emit(
|
||||||
currentStep: RecoveryStep.recoveryKey,
|
dataState.copyWith(
|
||||||
));
|
currentStep: RecoveryStep.recoveryKey,
|
||||||
|
),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case ServerRecoveryMethods.oldToken:
|
case ServerRecoveryMethods.oldToken:
|
||||||
emit(dataState.copyWith(
|
emit(
|
||||||
currentStep: RecoveryStep.oldToken,
|
dataState.copyWith(
|
||||||
));
|
currentStep: RecoveryStep.oldToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ServerBasicInfoWithValidators>>
|
Future<List<ServerBasicInfoWithValidators>>
|
||||||
getServersOnHetznerAccount() async {
|
getServersOnHetznerAccount() async {
|
||||||
final dataState = state as ServerInstallationRecovery;
|
final ServerInstallationRecovery dataState =
|
||||||
final servers = await repository.getServersOnHetznerAccount();
|
state as ServerInstallationRecovery;
|
||||||
final validated = servers
|
final List<ServerBasicInfo> servers =
|
||||||
.map((server) => ServerBasicInfoWithValidators.fromServerBasicInfo(
|
await repository.getServersOnHetznerAccount();
|
||||||
serverBasicInfo: server,
|
final Iterable<ServerBasicInfoWithValidators> validated = servers.map(
|
||||||
isIpValid: server.ip == dataState.serverDetails?.ip4,
|
(final ServerBasicInfo server) =>
|
||||||
isReverseDnsValid:
|
ServerBasicInfoWithValidators.fromServerBasicInfo(
|
||||||
server.reverseDns == dataState.serverDomain?.domainName,
|
serverBasicInfo: server,
|
||||||
));
|
isIpValid: server.ip == dataState.serverDetails?.ip4,
|
||||||
|
isReverseDnsValid:
|
||||||
|
server.reverseDns == dataState.serverDomain?.domainName,
|
||||||
|
),
|
||||||
|
);
|
||||||
return validated.toList();
|
return validated.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setServerId(ServerBasicInfo server) async {
|
Future<void> setServerId(final ServerBasicInfo server) async {
|
||||||
final dataState = state as ServerInstallationRecovery;
|
final ServerInstallationRecovery dataState =
|
||||||
final serverDomain = dataState.serverDomain;
|
state as ServerInstallationRecovery;
|
||||||
|
final ServerDomain? serverDomain = dataState.serverDomain;
|
||||||
if (serverDomain == null) {
|
if (serverDomain == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final serverDetails = ServerHostingDetails(
|
final ServerHostingDetails serverDetails = ServerHostingDetails(
|
||||||
ip4: server.ip,
|
ip4: server.ip,
|
||||||
id: server.id,
|
id: server.id,
|
||||||
createTime: server.created,
|
createTime: server.created,
|
||||||
|
@ -419,50 +491,60 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
);
|
);
|
||||||
await repository.saveDomain(serverDomain);
|
await repository.saveDomain(serverDomain);
|
||||||
await repository.saveServerDetails(serverDetails);
|
await repository.saveServerDetails(serverDetails);
|
||||||
emit(dataState.copyWith(
|
emit(
|
||||||
serverDetails: serverDetails,
|
dataState.copyWith(
|
||||||
currentStep: RecoveryStep.cloudflareToken,
|
serverDetails: serverDetails,
|
||||||
));
|
currentStep: RecoveryStep.cloudflareToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setAndValidateCloudflareToken(String token) async {
|
Future<void> setAndValidateCloudflareToken(final String token) async {
|
||||||
final dataState = state as ServerInstallationRecovery;
|
final ServerInstallationRecovery dataState =
|
||||||
final serverDomain = dataState.serverDomain;
|
state as ServerInstallationRecovery;
|
||||||
|
final ServerDomain? serverDomain = dataState.serverDomain;
|
||||||
if (serverDomain == null) {
|
if (serverDomain == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final zoneId = await repository.getDomainId(token, serverDomain.domainName);
|
final String? zoneId =
|
||||||
|
await repository.getDomainId(token, serverDomain.domainName);
|
||||||
if (zoneId == null) {
|
if (zoneId == null) {
|
||||||
getIt<NavigationService>()
|
getIt<NavigationService>()
|
||||||
.showSnackBar('recovering.domain_not_available_on_token'.tr());
|
.showSnackBar('recovering.domain_not_available_on_token'.tr());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await repository.saveDomain(ServerDomain(
|
await repository.saveDomain(
|
||||||
domainName: serverDomain.domainName,
|
ServerDomain(
|
||||||
zoneId: zoneId,
|
|
||||||
provider: DnsProvider.cloudflare,
|
|
||||||
));
|
|
||||||
await repository.saveCloudFlareKey(token);
|
|
||||||
emit(dataState.copyWith(
|
|
||||||
serverDomain: ServerDomain(
|
|
||||||
domainName: serverDomain.domainName,
|
domainName: serverDomain.domainName,
|
||||||
zoneId: zoneId,
|
zoneId: zoneId,
|
||||||
provider: DnsProvider.cloudflare,
|
provider: DnsProvider.cloudflare,
|
||||||
),
|
),
|
||||||
cloudFlareKey: token,
|
);
|
||||||
currentStep: RecoveryStep.backblazeToken,
|
await repository.saveCloudFlareKey(token);
|
||||||
));
|
emit(
|
||||||
|
dataState.copyWith(
|
||||||
|
serverDomain: ServerDomain(
|
||||||
|
domainName: serverDomain.domainName,
|
||||||
|
zoneId: zoneId,
|
||||||
|
provider: DnsProvider.cloudflare,
|
||||||
|
),
|
||||||
|
cloudFlareKey: token,
|
||||||
|
currentStep: RecoveryStep.backblazeToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void finishRecoveryProcess(BackblazeCredential backblazeCredential) async {
|
void finishRecoveryProcess(
|
||||||
|
final BackblazeCredential backblazeCredential,) async {
|
||||||
await repository.saveIsServerStarted(true);
|
await repository.saveIsServerStarted(true);
|
||||||
await repository.saveIsServerResetedFirstTime(true);
|
await repository.saveIsServerResetedFirstTime(true);
|
||||||
await repository.saveIsServerResetedSecondTime(true);
|
await repository.saveIsServerResetedSecondTime(true);
|
||||||
await repository.saveHasFinalChecked(true);
|
await repository.saveHasFinalChecked(true);
|
||||||
await repository.saveIsRecoveringServer(false);
|
await repository.saveIsRecoveringServer(false);
|
||||||
final mainUser = await repository.getMainUser();
|
final User mainUser = await repository.getMainUser();
|
||||||
await repository.saveRootUser(mainUser);
|
await repository.saveRootUser(mainUser);
|
||||||
final updatedState = (state as ServerInstallationRecovery).copyWith(
|
final ServerInstallationRecovery updatedState =
|
||||||
|
(state as ServerInstallationRecovery).copyWith(
|
||||||
backblazeCredential: backblazeCredential,
|
backblazeCredential: backblazeCredential,
|
||||||
rootUser: mainUser,
|
rootUser: mainUser,
|
||||||
);
|
);
|
||||||
|
@ -470,7 +552,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onChange(Change<ServerInstallationState> change) {
|
void onChange(final Change<ServerInstallationState> change) {
|
||||||
super.onChange(change);
|
super.onChange(change);
|
||||||
print('================================');
|
print('================================');
|
||||||
print('ServerInstallationState changed!');
|
print('ServerInstallationState changed!');
|
||||||
|
@ -481,9 +563,11 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
print('BackblazeCredential: ${change.nextState.backblazeCredential}');
|
print('BackblazeCredential: ${change.nextState.backblazeCredential}');
|
||||||
if (change.nextState is ServerInstallationRecovery) {
|
if (change.nextState is ServerInstallationRecovery) {
|
||||||
print(
|
print(
|
||||||
'Recovery Step: ${(change.nextState as ServerInstallationRecovery).currentStep}');
|
'Recovery Step: ${(change.nextState as ServerInstallationRecovery).currentStep}',
|
||||||
|
);
|
||||||
print(
|
print(
|
||||||
'Recovery Capabilities: ${(change.nextState as ServerInstallationRecovery).recoveryCapabilities}');
|
'Recovery Capabilities: ${(change.nextState as ServerInstallationRecovery).recoveryCapabilities}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (change.nextState is TimerState) {
|
if (change.nextState is TimerState) {
|
||||||
print('Timer: ${(change.nextState as TimerState).duration}');
|
print('Timer: ${(change.nextState as TimerState).duration}');
|
||||||
|
@ -504,23 +588,25 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
await repository.deleteServer(state.serverDomain!);
|
await repository.deleteServer(state.serverDomain!);
|
||||||
}
|
}
|
||||||
await repository.deleteServerRelatedRecords();
|
await repository.deleteServerRelatedRecords();
|
||||||
emit(ServerInstallationNotFinished(
|
emit(
|
||||||
hetznerKey: state.hetznerKey,
|
ServerInstallationNotFinished(
|
||||||
serverDomain: state.serverDomain,
|
hetznerKey: state.hetznerKey,
|
||||||
cloudFlareKey: state.cloudFlareKey,
|
serverDomain: state.serverDomain,
|
||||||
backblazeCredential: state.backblazeCredential,
|
cloudFlareKey: state.cloudFlareKey,
|
||||||
rootUser: state.rootUser,
|
backblazeCredential: state.backblazeCredential,
|
||||||
serverDetails: null,
|
rootUser: state.rootUser,
|
||||||
isServerStarted: false,
|
serverDetails: null,
|
||||||
isServerResetedFirstTime: false,
|
isServerStarted: false,
|
||||||
isServerResetedSecondTime: false,
|
isServerResetedFirstTime: false,
|
||||||
isLoading: false,
|
isServerResetedSecondTime: false,
|
||||||
dnsMatches: null,
|
isLoading: false,
|
||||||
));
|
dnsMatches: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
close() {
|
Future<void> close() {
|
||||||
closeTimer();
|
closeTimer();
|
||||||
return super.close();
|
return super.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:basic_utils/basic_utils.dart';
|
import 'package:basic_utils/basic_utils.dart';
|
||||||
|
@ -12,39 +14,41 @@ import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
import 'package:selfprivacy/logic/api_maps/cloudflare.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
|
||||||
import 'package:selfprivacy/logic/api_maps/server.dart';
|
import 'package:selfprivacy/logic/api_maps/server.dart';
|
||||||
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_credential.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
import 'package:selfprivacy/logic/models/hive/server_details.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
import 'package:selfprivacy/logic/models/hive/server_domain.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
import 'package:selfprivacy/logic/models/json/device_token.dart';
|
import 'package:selfprivacy/logic/models/json/device_token.dart';
|
||||||
|
import 'package:selfprivacy/logic/models/json/hetzner_server_info.dart';
|
||||||
import 'package:selfprivacy/logic/models/message.dart';
|
import 'package:selfprivacy/logic/models/message.dart';
|
||||||
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
import 'package:selfprivacy/logic/models/server_basic_info.dart';
|
||||||
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
import 'package:selfprivacy/ui/components/action_button/action_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
import 'package:selfprivacy/ui/components/brand_alert/brand_alert.dart';
|
||||||
|
|
||||||
import '../server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
|
|
||||||
class IpNotFoundException implements Exception {
|
class IpNotFoundException implements Exception {
|
||||||
final String message;
|
|
||||||
|
|
||||||
IpNotFoundException(this.message);
|
IpNotFoundException(this.message);
|
||||||
|
final String message;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerAuthorizationException implements Exception {
|
class ServerAuthorizationException implements Exception {
|
||||||
final String message;
|
|
||||||
|
|
||||||
ServerAuthorizationException(this.message);
|
ServerAuthorizationException(this.message);
|
||||||
|
final String message;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerInstallationRepository {
|
class ServerInstallationRepository {
|
||||||
Box box = Hive.box(BNames.serverInstallationBox);
|
Box box = Hive.box(BNames.serverInstallationBox);
|
||||||
|
|
||||||
Future<ServerInstallationState> load() async {
|
Future<ServerInstallationState> load() async {
|
||||||
final hetznerToken = getIt<ApiConfigModel>().hetznerKey;
|
final String? hetznerToken = getIt<ApiConfigModel>().hetznerKey;
|
||||||
final cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey;
|
final String? cloudflareToken = getIt<ApiConfigModel>().cloudFlareKey;
|
||||||
final serverDomain = getIt<ApiConfigModel>().serverDomain;
|
final ServerDomain? serverDomain = getIt<ApiConfigModel>().serverDomain;
|
||||||
final backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
final BackblazeCredential? backblazeCredential = getIt<ApiConfigModel>().backblazeCredential;
|
||||||
final serverDetails = getIt<ApiConfigModel>().serverDetails;
|
final ServerHostingDetails? serverDetails = getIt<ApiConfigModel>().serverDetails;
|
||||||
|
|
||||||
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
|
if (box.get(BNames.hasFinalChecked, defaultValue: false)) {
|
||||||
return ServerInstallationFinished(
|
return ServerInstallationFinished(
|
||||||
|
@ -72,7 +76,7 @@ class ServerInstallationRepository {
|
||||||
serverDetails: serverDetails,
|
serverDetails: serverDetails,
|
||||||
rootUser: box.get(BNames.rootUser),
|
rootUser: box.get(BNames.rootUser),
|
||||||
currentStep: _getCurrentRecoveryStep(
|
currentStep: _getCurrentRecoveryStep(
|
||||||
hetznerToken, cloudflareToken, serverDomain, serverDetails),
|
hetznerToken, cloudflareToken, serverDomain, serverDetails,),
|
||||||
recoveryCapabilities: await getRecoveryCapabilities(serverDomain),
|
recoveryCapabilities: await getRecoveryCapabilities(serverDomain),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -95,10 +99,10 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
RecoveryStep _getCurrentRecoveryStep(
|
RecoveryStep _getCurrentRecoveryStep(
|
||||||
String? hetznerToken,
|
final String? hetznerToken,
|
||||||
String? cloudflareToken,
|
final String? cloudflareToken,
|
||||||
ServerDomain serverDomain,
|
final ServerDomain serverDomain,
|
||||||
ServerHostingDetails? serverDetails,
|
final ServerHostingDetails? serverDetails,
|
||||||
) {
|
) {
|
||||||
if (serverDetails != null) {
|
if (serverDetails != null) {
|
||||||
if (hetznerToken != null) {
|
if (hetznerToken != null) {
|
||||||
|
@ -120,31 +124,31 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> startServer(
|
Future<ServerHostingDetails> startServer(
|
||||||
ServerHostingDetails hetznerServer,
|
final ServerHostingDetails hetznerServer,
|
||||||
) async {
|
) async {
|
||||||
var hetznerApi = HetznerApi();
|
final HetznerApi hetznerApi = HetznerApi();
|
||||||
var serverDetails = await hetznerApi.powerOn();
|
final ServerHostingDetails serverDetails = await hetznerApi.powerOn();
|
||||||
|
|
||||||
return serverDetails;
|
return serverDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getDomainId(String token, String domain) async {
|
Future<String?> getDomainId(final String token, final String domain) async {
|
||||||
var cloudflareApi = CloudflareApi(
|
final CloudflareApi cloudflareApi = CloudflareApi(
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
customToken: token,
|
customToken: token,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final domainId = await cloudflareApi.getZoneId(domain);
|
final String domainId = await cloudflareApi.getZoneId(domain);
|
||||||
return domainId;
|
return domainId;
|
||||||
} on DomainNotFoundException {
|
} on DomainNotFoundException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, bool>> isDnsAddressesMatch(String? domainName, String? ip4,
|
Future<Map<String, bool>> isDnsAddressesMatch(final String? domainName, final String? ip4,
|
||||||
Map<String, bool>? skippedMatches) async {
|
final Map<String, bool>? skippedMatches,) async {
|
||||||
var addresses = <String>[
|
final List<String> addresses = <String>[
|
||||||
'$domainName',
|
'$domainName',
|
||||||
'api.$domainName',
|
'api.$domainName',
|
||||||
'cloud.$domainName',
|
'cloud.$domainName',
|
||||||
|
@ -152,14 +156,14 @@ class ServerInstallationRepository {
|
||||||
'password.$domainName'
|
'password.$domainName'
|
||||||
];
|
];
|
||||||
|
|
||||||
var matches = <String, bool>{};
|
final Map<String, bool> matches = <String, bool>{};
|
||||||
|
|
||||||
for (var address in addresses) {
|
for (final String address in addresses) {
|
||||||
if (skippedMatches != null && skippedMatches[address] == true) {
|
if (skippedMatches![address] ?? false) {
|
||||||
matches[address] = true;
|
matches[address] = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var lookupRecordRes = await DnsUtils.lookupRecord(
|
final List<RRecord>? lookupRecordRes = await DnsUtils.lookupRecord(
|
||||||
address,
|
address,
|
||||||
RRecordType.A,
|
RRecordType.A,
|
||||||
provider: DnsApiProvider.CLOUDFLARE,
|
provider: DnsApiProvider.CLOUDFLARE,
|
||||||
|
@ -189,21 +193,21 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createServer(
|
Future<void> createServer(
|
||||||
User rootUser,
|
final User rootUser,
|
||||||
String domainName,
|
final String domainName,
|
||||||
String cloudFlareKey,
|
final String cloudFlareKey,
|
||||||
BackblazeCredential backblazeCredential, {
|
final BackblazeCredential backblazeCredential, {
|
||||||
required void Function() onCancel,
|
required final void Function() onCancel,
|
||||||
required Future<void> Function(ServerHostingDetails serverDetails)
|
required final Future<void> Function(ServerHostingDetails serverDetails)
|
||||||
onSuccess,
|
onSuccess,
|
||||||
}) async {
|
}) async {
|
||||||
var hetznerApi = HetznerApi();
|
final HetznerApi hetznerApi = HetznerApi();
|
||||||
late ServerVolume dataBase;
|
late ServerVolume dataBase;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dataBase = await hetznerApi.createVolume();
|
dataBase = await hetznerApi.createVolume();
|
||||||
|
|
||||||
var serverDetails = await hetznerApi.createServer(
|
final ServerHostingDetails serverDetails = await hetznerApi.createServer(
|
||||||
cloudFlareKey: cloudFlareKey,
|
cloudFlareKey: cloudFlareKey,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
|
@ -213,7 +217,7 @@ class ServerInstallationRepository {
|
||||||
onSuccess(serverDetails);
|
onSuccess(serverDetails);
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
if (e.response!.data['error']['code'] == 'uniqueness_error') {
|
if (e.response!.data['error']['code'] == 'uniqueness_error') {
|
||||||
var nav = getIt.get<NavigationService>();
|
final NavigationService nav = getIt.get<NavigationService>();
|
||||||
nav.showPopUpDialog(
|
nav.showPopUpDialog(
|
||||||
BrandAlert(
|
BrandAlert(
|
||||||
title: 'modals.1'.tr(),
|
title: 'modals.1'.tr(),
|
||||||
|
@ -224,9 +228,9 @@ class ServerInstallationRepository {
|
||||||
isRed: true,
|
isRed: true,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
|
await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
|
||||||
domainName: domainName);
|
domainName: domainName,);
|
||||||
|
|
||||||
var serverDetails = await hetznerApi.createServer(
|
final ServerHostingDetails serverDetails = await hetznerApi.createServer(
|
||||||
cloudFlareKey: cloudFlareKey,
|
cloudFlareKey: cloudFlareKey,
|
||||||
rootUser: rootUser,
|
rootUser: rootUser,
|
||||||
domainName: domainName,
|
domainName: domainName,
|
||||||
|
@ -239,9 +243,7 @@ class ServerInstallationRepository {
|
||||||
),
|
),
|
||||||
ActionButton(
|
ActionButton(
|
||||||
text: 'basis.cancel'.tr(),
|
text: 'basis.cancel'.tr(),
|
||||||
onPressed: () {
|
onPressed: onCancel,
|
||||||
onCancel();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -251,11 +253,11 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createDnsRecords(
|
Future<void> createDnsRecords(
|
||||||
String ip4,
|
final String ip4,
|
||||||
ServerDomain cloudFlareDomain, {
|
final ServerDomain cloudFlareDomain, {
|
||||||
required void Function() onCancel,
|
required final void Function() onCancel,
|
||||||
}) async {
|
}) async {
|
||||||
var cloudflareApi = CloudflareApi();
|
final CloudflareApi cloudflareApi = CloudflareApi();
|
||||||
|
|
||||||
await cloudflareApi.removeSimilarRecords(
|
await cloudflareApi.removeSimilarRecords(
|
||||||
ip4: ip4,
|
ip4: ip4,
|
||||||
|
@ -268,8 +270,8 @@ class ServerInstallationRepository {
|
||||||
cloudFlareDomain: cloudFlareDomain,
|
cloudFlareDomain: cloudFlareDomain,
|
||||||
);
|
);
|
||||||
} on DioError catch (e) {
|
} on DioError catch (e) {
|
||||||
var hetznerApi = HetznerApi();
|
final HetznerApi hetznerApi = HetznerApi();
|
||||||
var nav = getIt.get<NavigationService>();
|
final NavigationService nav = getIt.get<NavigationService>();
|
||||||
nav.showPopUpDialog(
|
nav.showPopUpDialog(
|
||||||
BrandAlert(
|
BrandAlert(
|
||||||
title: e.response!.data['errors'][0]['code'] == 1038
|
title: e.response!.data['errors'][0]['code'] == 1038
|
||||||
|
@ -282,16 +284,14 @@ class ServerInstallationRepository {
|
||||||
isRed: true,
|
isRed: true,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
|
await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
|
||||||
domainName: cloudFlareDomain.domainName);
|
domainName: cloudFlareDomain.domainName,);
|
||||||
|
|
||||||
onCancel();
|
onCancel();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ActionButton(
|
ActionButton(
|
||||||
text: 'basis.cancel'.tr(),
|
text: 'basis.cancel'.tr(),
|
||||||
onPressed: () {
|
onPressed: onCancel,
|
||||||
onCancel();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -304,18 +304,18 @@ class ServerInstallationRepository {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createDkimRecord(ServerDomain cloudFlareDomain) async {
|
Future<void> createDkimRecord(final ServerDomain cloudFlareDomain) async {
|
||||||
var cloudflareApi = CloudflareApi();
|
final CloudflareApi cloudflareApi = CloudflareApi();
|
||||||
var api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
|
|
||||||
var dkimRecordString = await api.getDkim();
|
final String? dkimRecordString = await api.getDkim();
|
||||||
|
|
||||||
await cloudflareApi.setDkim(dkimRecordString ?? '', cloudFlareDomain);
|
await cloudflareApi.setDkim(dkimRecordString ?? '', cloudFlareDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isHttpServerWorking() async {
|
Future<bool> isHttpServerWorking() async {
|
||||||
var api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
var isHttpServerWorking = await api.isHttpServerWorking();
|
final bool isHttpServerWorking = await api.isHttpServerWorking();
|
||||||
try {
|
try {
|
||||||
await api.getDkim();
|
await api.getDkim();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -325,28 +325,28 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> restart() async {
|
Future<ServerHostingDetails> restart() async {
|
||||||
var hetznerApi = HetznerApi();
|
final HetznerApi hetznerApi = HetznerApi();
|
||||||
return await hetznerApi.reset();
|
return hetznerApi.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> powerOn() async {
|
Future<ServerHostingDetails> powerOn() async {
|
||||||
var hetznerApi = HetznerApi();
|
final HetznerApi hetznerApi = HetznerApi();
|
||||||
return await hetznerApi.powerOn();
|
return hetznerApi.powerOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerRecoveryCapabilities> getRecoveryCapabilities(
|
Future<ServerRecoveryCapabilities> getRecoveryCapabilities(
|
||||||
ServerDomain serverDomain,
|
final ServerDomain serverDomain,
|
||||||
) async {
|
) async {
|
||||||
var serverApi = ServerApi(
|
final ServerApi serverApi = ServerApi(
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
overrideDomain: serverDomain.domainName,
|
overrideDomain: serverDomain.domainName,
|
||||||
);
|
);
|
||||||
final serverApiVersion = await serverApi.getApiVersion();
|
final String? serverApiVersion = await serverApi.getApiVersion();
|
||||||
if (serverApiVersion == null) {
|
if (serverApiVersion == null) {
|
||||||
return ServerRecoveryCapabilities.none;
|
return ServerRecoveryCapabilities.none;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final parsedVersion = Version.parse(serverApiVersion);
|
final Version parsedVersion = Version.parse(serverApiVersion);
|
||||||
if (!VersionConstraint.parse('>=1.2.0').allows(parsedVersion)) {
|
if (!VersionConstraint.parse('>=1.2.0').allows(parsedVersion)) {
|
||||||
return ServerRecoveryCapabilities.legacy;
|
return ServerRecoveryCapabilities.legacy;
|
||||||
}
|
}
|
||||||
|
@ -356,10 +356,10 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getServerIpFromDomain(ServerDomain serverDomain) async {
|
Future<String> getServerIpFromDomain(final ServerDomain serverDomain) async {
|
||||||
final lookup = await DnsUtils.lookupRecord(
|
final List<RRecord>? lookup = await DnsUtils.lookupRecord(
|
||||||
serverDomain.domainName, RRecordType.A,
|
serverDomain.domainName, RRecordType.A,
|
||||||
provider: DnsApiProvider.CLOUDFLARE);
|
provider: DnsApiProvider.CLOUDFLARE,);
|
||||||
if (lookup == null || lookup.isEmpty) {
|
if (lookup == null || lookup.isEmpty) {
|
||||||
throw IpNotFoundException('No IP found for domain $serverDomain');
|
throw IpNotFoundException('No IP found for domain $serverDomain');
|
||||||
}
|
}
|
||||||
|
@ -369,39 +369,39 @@ class ServerInstallationRepository {
|
||||||
Future<String> getDeviceName() async {
|
Future<String> getDeviceName() async {
|
||||||
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
return await deviceInfo.webBrowserInfo
|
return deviceInfo.webBrowserInfo
|
||||||
.then((value) => '${value.browserName} ${value.platform}');
|
.then((final WebBrowserInfo value) => '${value.browserName} ${value.platform}');
|
||||||
} else {
|
} else {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
return await deviceInfo.androidInfo
|
return deviceInfo.androidInfo
|
||||||
.then((value) => '${value.model} ${value.version.release}');
|
.then((final AndroidDeviceInfo value) => '${value.model} ${value.version.release}');
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
return await deviceInfo.iosInfo.then((value) =>
|
return deviceInfo.iosInfo.then((final IosDeviceInfo value) =>
|
||||||
'${value.utsname.machine} ${value.systemName} ${value.systemVersion}');
|
'${value.utsname.machine} ${value.systemName} ${value.systemVersion}',);
|
||||||
} else if (Platform.isLinux) {
|
} else if (Platform.isLinux) {
|
||||||
return await deviceInfo.linuxInfo.then((value) => value.prettyName);
|
return deviceInfo.linuxInfo.then((final LinuxDeviceInfo value) => value.prettyName);
|
||||||
} else if (Platform.isMacOS) {
|
} else if (Platform.isMacOS) {
|
||||||
return await deviceInfo.macOsInfo
|
return deviceInfo.macOsInfo
|
||||||
.then((value) => '${value.hostName} ${value.computerName}');
|
.then((final MacOsDeviceInfo value) => '${value.hostName} ${value.computerName}');
|
||||||
} else if (Platform.isWindows) {
|
} else if (Platform.isWindows) {
|
||||||
return await deviceInfo.windowsInfo.then((value) => value.computerName);
|
return deviceInfo.windowsInfo.then((final WindowsDeviceInfo value) => value.computerName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 'Unidentified';
|
return 'Unidentified';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> authorizeByNewDeviceKey(
|
Future<ServerHostingDetails> authorizeByNewDeviceKey(
|
||||||
ServerDomain serverDomain,
|
final ServerDomain serverDomain,
|
||||||
String newDeviceKey,
|
final String newDeviceKey,
|
||||||
ServerRecoveryCapabilities recoveryCapabilities,
|
final ServerRecoveryCapabilities recoveryCapabilities,
|
||||||
) async {
|
) async {
|
||||||
var serverApi = ServerApi(
|
final ServerApi serverApi = ServerApi(
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
overrideDomain: serverDomain.domainName,
|
overrideDomain: serverDomain.domainName,
|
||||||
);
|
);
|
||||||
final serverIp = await getServerIpFromDomain(serverDomain);
|
final String serverIp = await getServerIpFromDomain(serverDomain);
|
||||||
final apiResponse = await serverApi.authorizeDevice(
|
final ApiResponse<String> apiResponse = await serverApi.authorizeDevice(
|
||||||
DeviceToken(device: await getDeviceName(), token: newDeviceKey));
|
DeviceToken(device: await getDeviceName(), token: newDeviceKey),);
|
||||||
|
|
||||||
if (apiResponse.isSuccess) {
|
if (apiResponse.isSuccess) {
|
||||||
return ServerHostingDetails(
|
return ServerHostingDetails(
|
||||||
|
@ -424,17 +424,17 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> authorizeByRecoveryKey(
|
Future<ServerHostingDetails> authorizeByRecoveryKey(
|
||||||
ServerDomain serverDomain,
|
final ServerDomain serverDomain,
|
||||||
String recoveryKey,
|
final String recoveryKey,
|
||||||
ServerRecoveryCapabilities recoveryCapabilities,
|
final ServerRecoveryCapabilities recoveryCapabilities,
|
||||||
) async {
|
) async {
|
||||||
var serverApi = ServerApi(
|
final ServerApi serverApi = ServerApi(
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
overrideDomain: serverDomain.domainName,
|
overrideDomain: serverDomain.domainName,
|
||||||
);
|
);
|
||||||
final serverIp = await getServerIpFromDomain(serverDomain);
|
final String serverIp = await getServerIpFromDomain(serverDomain);
|
||||||
final apiResponse = await serverApi.useRecoveryToken(
|
final ApiResponse<String> apiResponse = await serverApi.useRecoveryToken(
|
||||||
DeviceToken(device: await getDeviceName(), token: recoveryKey));
|
DeviceToken(device: await getDeviceName(), token: recoveryKey),);
|
||||||
|
|
||||||
if (apiResponse.isSuccess) {
|
if (apiResponse.isSuccess) {
|
||||||
return ServerHostingDetails(
|
return ServerHostingDetails(
|
||||||
|
@ -457,18 +457,18 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ServerHostingDetails> authorizeByApiToken(
|
Future<ServerHostingDetails> authorizeByApiToken(
|
||||||
ServerDomain serverDomain,
|
final ServerDomain serverDomain,
|
||||||
String apiToken,
|
final String apiToken,
|
||||||
ServerRecoveryCapabilities recoveryCapabilities,
|
final ServerRecoveryCapabilities recoveryCapabilities,
|
||||||
) async {
|
) async {
|
||||||
var serverApi = ServerApi(
|
final ServerApi serverApi = ServerApi(
|
||||||
isWithToken: false,
|
isWithToken: false,
|
||||||
overrideDomain: serverDomain.domainName,
|
overrideDomain: serverDomain.domainName,
|
||||||
customToken: apiToken,
|
customToken: apiToken,
|
||||||
);
|
);
|
||||||
final serverIp = await getServerIpFromDomain(serverDomain);
|
final String serverIp = await getServerIpFromDomain(serverDomain);
|
||||||
if (recoveryCapabilities == ServerRecoveryCapabilities.legacy) {
|
if (recoveryCapabilities == ServerRecoveryCapabilities.legacy) {
|
||||||
final apiResponse = await serverApi.servicesPowerCheck();
|
final Map<ServiceTypes, bool> apiResponse = await serverApi.servicesPowerCheck();
|
||||||
if (apiResponse.isNotEmpty) {
|
if (apiResponse.isNotEmpty) {
|
||||||
return ServerHostingDetails(
|
return ServerHostingDetails(
|
||||||
apiToken: apiToken,
|
apiToken: apiToken,
|
||||||
|
@ -484,13 +484,13 @@ class ServerInstallationRepository {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw ServerAuthorizationException(
|
throw ServerAuthorizationException(
|
||||||
'Couldn\'t connect to server with this token',
|
"Couldn't connect to server with this token",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final deviceAuthKey = await serverApi.createDeviceToken();
|
final ApiResponse<String> deviceAuthKey = await serverApi.createDeviceToken();
|
||||||
final apiResponse = await serverApi.authorizeDevice(
|
final ApiResponse<String> apiResponse = await serverApi.authorizeDevice(
|
||||||
DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data));
|
DeviceToken(device: await getDeviceName(), token: deviceAuthKey.data),);
|
||||||
|
|
||||||
if (apiResponse.isSuccess) {
|
if (apiResponse.isSuccess) {
|
||||||
return ServerHostingDetails(
|
return ServerHostingDetails(
|
||||||
|
@ -513,21 +513,21 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<User> getMainUser() async {
|
Future<User> getMainUser() async {
|
||||||
var serverApi = ServerApi();
|
final ServerApi serverApi = ServerApi();
|
||||||
const fallbackUser = User(
|
const User fallbackUser = User(
|
||||||
isFoundOnServer: false,
|
isFoundOnServer: false,
|
||||||
note: 'Couldn\'t find main user on server, API is outdated',
|
note: "Couldn't find main user on server, API is outdated",
|
||||||
login: 'UNKNOWN',
|
login: 'UNKNOWN',
|
||||||
sshKeys: [],
|
sshKeys: [],
|
||||||
);
|
);
|
||||||
|
|
||||||
final serverApiVersion = await serverApi.getApiVersion();
|
final String? serverApiVersion = await serverApi.getApiVersion();
|
||||||
final users = await serverApi.getUsersList(withMainUser: true);
|
final ApiResponse<List<String>> users = await serverApi.getUsersList(withMainUser: true);
|
||||||
if (serverApiVersion == null || !users.isSuccess) {
|
if (serverApiVersion == null || !users.isSuccess) {
|
||||||
return fallbackUser;
|
return fallbackUser;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final parsedVersion = Version.parse(serverApiVersion);
|
final Version parsedVersion = Version.parse(serverApiVersion);
|
||||||
if (!VersionConstraint.parse('>=1.2.5').allows(parsedVersion)) {
|
if (!VersionConstraint.parse('>=1.2.5').allows(parsedVersion)) {
|
||||||
return fallbackUser;
|
return fallbackUser;
|
||||||
}
|
}
|
||||||
|
@ -541,25 +541,25 @@ class ServerInstallationRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ServerBasicInfo>> getServersOnHetznerAccount() async {
|
Future<List<ServerBasicInfo>> getServersOnHetznerAccount() async {
|
||||||
var hetznerApi = HetznerApi();
|
final HetznerApi hetznerApi = HetznerApi();
|
||||||
final servers = await hetznerApi.getServers();
|
final List<HetznerServerInfo> servers = await hetznerApi.getServers();
|
||||||
return servers
|
return servers
|
||||||
.map((server) => ServerBasicInfo(
|
.map((final HetznerServerInfo server) => ServerBasicInfo(
|
||||||
id: server.id,
|
id: server.id,
|
||||||
name: server.name,
|
name: server.name,
|
||||||
ip: server.publicNet.ipv4.ip,
|
ip: server.publicNet.ipv4.ip,
|
||||||
reverseDns: server.publicNet.ipv4.reverseDns,
|
reverseDns: server.publicNet.ipv4.reverseDns,
|
||||||
created: server.created,
|
created: server.created,
|
||||||
volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0,
|
volumeId: server.volumes.isNotEmpty ? server.volumes[0] : 0,
|
||||||
))
|
),)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveServerDetails(ServerHostingDetails serverDetails) async {
|
Future<void> saveServerDetails(final ServerHostingDetails serverDetails) async {
|
||||||
await getIt<ApiConfigModel>().storeServerDetails(serverDetails);
|
await getIt<ApiConfigModel>().storeServerDetails(serverDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveHetznerKey(String key) async {
|
Future<void> saveHetznerKey(final String key) async {
|
||||||
print('saved');
|
print('saved');
|
||||||
await getIt<ApiConfigModel>().storeHetznerKey(key);
|
await getIt<ApiConfigModel>().storeHetznerKey(key);
|
||||||
}
|
}
|
||||||
|
@ -569,15 +569,15 @@ class ServerInstallationRepository {
|
||||||
getIt<ApiConfigModel>().init();
|
getIt<ApiConfigModel>().init();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveBackblazeKey(BackblazeCredential backblazeCredential) async {
|
Future<void> saveBackblazeKey(final BackblazeCredential backblazeCredential) async {
|
||||||
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
|
await getIt<ApiConfigModel>().storeBackblazeCredential(backblazeCredential);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveCloudFlareKey(String key) async {
|
Future<void> saveCloudFlareKey(final String key) async {
|
||||||
await getIt<ApiConfigModel>().storeCloudFlareKey(key);
|
await getIt<ApiConfigModel>().storeCloudFlareKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveDomain(ServerDomain serverDomain) async {
|
Future<void> saveDomain(final ServerDomain serverDomain) async {
|
||||||
await getIt<ApiConfigModel>().storeServerDomain(serverDomain);
|
await getIt<ApiConfigModel>().storeServerDomain(serverDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,33 +586,33 @@ class ServerInstallationRepository {
|
||||||
getIt<ApiConfigModel>().init();
|
getIt<ApiConfigModel>().init();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveIsServerStarted(bool value) async {
|
Future<void> saveIsServerStarted(final bool value) async {
|
||||||
await box.put(BNames.isServerStarted, value);
|
await box.put(BNames.isServerStarted, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveIsServerResetedFirstTime(bool value) async {
|
Future<void> saveIsServerResetedFirstTime(final bool value) async {
|
||||||
await box.put(BNames.isServerResetedFirstTime, value);
|
await box.put(BNames.isServerResetedFirstTime, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveIsServerResetedSecondTime(bool value) async {
|
Future<void> saveIsServerResetedSecondTime(final bool value) async {
|
||||||
await box.put(BNames.isServerResetedSecondTime, value);
|
await box.put(BNames.isServerResetedSecondTime, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveRootUser(User rootUser) async {
|
Future<void> saveRootUser(final User rootUser) async {
|
||||||
await box.put(BNames.rootUser, rootUser);
|
await box.put(BNames.rootUser, rootUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveIsRecoveringServer(bool value) async {
|
Future<void> saveIsRecoveringServer(final bool value) async {
|
||||||
await box.put(BNames.isRecoveringServer, value);
|
await box.put(BNames.isRecoveringServer, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveHasFinalChecked(bool value) async {
|
Future<void> saveHasFinalChecked(final bool value) async {
|
||||||
await box.put(BNames.hasFinalChecked, value);
|
await box.put(BNames.hasFinalChecked, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteServer(ServerDomain serverDomain) async {
|
Future<void> deleteServer(final ServerDomain serverDomain) async {
|
||||||
var hetznerApi = HetznerApi();
|
final HetznerApi hetznerApi = HetznerApi();
|
||||||
var cloudFlare = CloudflareApi();
|
final CloudflareApi cloudFlare = CloudflareApi();
|
||||||
|
|
||||||
await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
|
await hetznerApi.deleteSelfprivacyServerAndAllVolumes(
|
||||||
domainName: serverDomain.domainName,
|
domainName: serverDomain.domainName,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of '../server_installation/server_installation_cubit.dart';
|
part of '../server_installation/server_installation_cubit.dart';
|
||||||
|
|
||||||
abstract class ServerInstallationState extends Equatable {
|
abstract class ServerInstallationState extends Equatable {
|
||||||
|
@ -42,9 +44,9 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
bool get isUserFilled => rootUser != null;
|
bool get isUserFilled => rootUser != null;
|
||||||
bool get isServerCreated => serverDetails != null;
|
bool get isServerCreated => serverDetails != null;
|
||||||
|
|
||||||
bool get isFullyInitilized => _fulfilementList.every((el) => el!);
|
bool get isFullyInitilized => _fulfilementList.every((final el) => el!);
|
||||||
ServerSetupProgress get progress =>
|
ServerSetupProgress get progress =>
|
||||||
ServerSetupProgress.values[_fulfilementList.where((el) => el!).length];
|
ServerSetupProgress.values[_fulfilementList.where((final el) => el!).length];
|
||||||
|
|
||||||
int get porgressBar {
|
int get porgressBar {
|
||||||
if (progress.index < 6) {
|
if (progress.index < 6) {
|
||||||
|
@ -57,7 +59,7 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<bool?> get _fulfilementList {
|
List<bool?> get _fulfilementList {
|
||||||
var res = [
|
final List<bool> res = [
|
||||||
isHetznerFilled,
|
isHetznerFilled,
|
||||||
isCloudFlareFilled,
|
isCloudFlareFilled,
|
||||||
isBackblazeFilled,
|
isBackblazeFilled,
|
||||||
|
@ -76,9 +78,9 @@ abstract class ServerInstallationState extends Equatable {
|
||||||
class TimerState extends ServerInstallationNotFinished {
|
class TimerState extends ServerInstallationNotFinished {
|
||||||
TimerState({
|
TimerState({
|
||||||
required this.dataState,
|
required this.dataState,
|
||||||
|
required final super.isLoading,
|
||||||
this.timerStart,
|
this.timerStart,
|
||||||
this.duration,
|
this.duration,
|
||||||
required bool isLoading,
|
|
||||||
}) : super(
|
}) : super(
|
||||||
hetznerKey: dataState.hetznerKey,
|
hetznerKey: dataState.hetznerKey,
|
||||||
cloudFlareKey: dataState.cloudFlareKey,
|
cloudFlareKey: dataState.cloudFlareKey,
|
||||||
|
@ -89,7 +91,6 @@ class TimerState extends ServerInstallationNotFinished {
|
||||||
isServerStarted: dataState.isServerStarted,
|
isServerStarted: dataState.isServerStarted,
|
||||||
isServerResetedFirstTime: dataState.isServerResetedFirstTime,
|
isServerResetedFirstTime: dataState.isServerResetedFirstTime,
|
||||||
isServerResetedSecondTime: dataState.isServerResetedSecondTime,
|
isServerResetedSecondTime: dataState.isServerResetedSecondTime,
|
||||||
isLoading: isLoading,
|
|
||||||
dnsMatches: dataState.dnsMatches,
|
dnsMatches: dataState.dnsMatches,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -119,32 +120,22 @@ enum ServerSetupProgress {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerInstallationNotFinished extends ServerInstallationState {
|
class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
final bool isLoading;
|
|
||||||
final Map<String, bool>? dnsMatches;
|
|
||||||
|
|
||||||
const ServerInstallationNotFinished({
|
const ServerInstallationNotFinished({
|
||||||
String? hetznerKey,
|
required final super.isServerStarted,
|
||||||
String? cloudFlareKey,
|
required final super.isServerResetedFirstTime,
|
||||||
BackblazeCredential? backblazeCredential,
|
required final super.isServerResetedSecondTime,
|
||||||
ServerDomain? serverDomain,
|
required final this.isLoading,
|
||||||
User? rootUser,
|
|
||||||
ServerHostingDetails? serverDetails,
|
|
||||||
required bool isServerStarted,
|
|
||||||
required bool isServerResetedFirstTime,
|
|
||||||
required bool isServerResetedSecondTime,
|
|
||||||
required this.isLoading,
|
|
||||||
required this.dnsMatches,
|
required this.dnsMatches,
|
||||||
}) : super(
|
final super.hetznerKey,
|
||||||
hetznerKey: hetznerKey,
|
final super.cloudFlareKey,
|
||||||
cloudFlareKey: cloudFlareKey,
|
final super.backblazeCredential,
|
||||||
backblazeCredential: backblazeCredential,
|
final super.serverDomain,
|
||||||
serverDomain: serverDomain,
|
final super.rootUser,
|
||||||
rootUser: rootUser,
|
final super.serverDetails,
|
||||||
serverDetails: serverDetails,
|
});
|
||||||
isServerStarted: isServerStarted,
|
final bool isLoading;
|
||||||
isServerResetedFirstTime: isServerResetedFirstTime,
|
final Map<String, bool>? dnsMatches;
|
||||||
isServerResetedSecondTime: isServerResetedSecondTime,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
|
@ -161,17 +152,17 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
];
|
];
|
||||||
|
|
||||||
ServerInstallationNotFinished copyWith({
|
ServerInstallationNotFinished copyWith({
|
||||||
String? hetznerKey,
|
final String? hetznerKey,
|
||||||
String? cloudFlareKey,
|
final String? cloudFlareKey,
|
||||||
BackblazeCredential? backblazeCredential,
|
final BackblazeCredential? backblazeCredential,
|
||||||
ServerDomain? serverDomain,
|
final ServerDomain? serverDomain,
|
||||||
User? rootUser,
|
final User? rootUser,
|
||||||
ServerHostingDetails? serverDetails,
|
final ServerHostingDetails? serverDetails,
|
||||||
bool? isServerStarted,
|
final bool? isServerStarted,
|
||||||
bool? isServerResetedFirstTime,
|
final bool? isServerResetedFirstTime,
|
||||||
bool? isServerResetedSecondTime,
|
final bool? isServerResetedSecondTime,
|
||||||
bool? isLoading,
|
final bool? isLoading,
|
||||||
Map<String, bool>? dnsMatches,
|
final Map<String, bool>? dnsMatches,
|
||||||
}) =>
|
}) =>
|
||||||
ServerInstallationNotFinished(
|
ServerInstallationNotFinished(
|
||||||
hetznerKey: hetznerKey ?? this.hetznerKey,
|
hetznerKey: hetznerKey ?? this.hetznerKey,
|
||||||
|
@ -221,26 +212,16 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished {
|
||||||
|
|
||||||
class ServerInstallationFinished extends ServerInstallationState {
|
class ServerInstallationFinished extends ServerInstallationState {
|
||||||
const ServerInstallationFinished({
|
const ServerInstallationFinished({
|
||||||
required String hetznerKey,
|
required final String super.hetznerKey,
|
||||||
required String cloudFlareKey,
|
required final String super.cloudFlareKey,
|
||||||
required BackblazeCredential backblazeCredential,
|
required final BackblazeCredential super.backblazeCredential,
|
||||||
required ServerDomain serverDomain,
|
required final ServerDomain super.serverDomain,
|
||||||
required User rootUser,
|
required final User super.rootUser,
|
||||||
required ServerHostingDetails serverDetails,
|
required final ServerHostingDetails super.serverDetails,
|
||||||
required bool isServerStarted,
|
required final super.isServerStarted,
|
||||||
required bool isServerResetedFirstTime,
|
required final super.isServerResetedFirstTime,
|
||||||
required bool isServerResetedSecondTime,
|
required final super.isServerResetedSecondTime,
|
||||||
}) : super(
|
});
|
||||||
hetznerKey: hetznerKey,
|
|
||||||
cloudFlareKey: cloudFlareKey,
|
|
||||||
backblazeCredential: backblazeCredential,
|
|
||||||
serverDomain: serverDomain,
|
|
||||||
rootUser: rootUser,
|
|
||||||
serverDetails: serverDetails,
|
|
||||||
isServerStarted: isServerStarted,
|
|
||||||
isServerResetedFirstTime: isServerResetedFirstTime,
|
|
||||||
isServerResetedSecondTime: isServerResetedSecondTime,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
|
@ -279,29 +260,23 @@ enum ServerRecoveryMethods {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerInstallationRecovery extends ServerInstallationState {
|
class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
final RecoveryStep currentStep;
|
|
||||||
final ServerRecoveryCapabilities recoveryCapabilities;
|
|
||||||
|
|
||||||
const ServerInstallationRecovery({
|
const ServerInstallationRecovery({
|
||||||
String? hetznerKey,
|
|
||||||
String? cloudFlareKey,
|
|
||||||
BackblazeCredential? backblazeCredential,
|
|
||||||
ServerDomain? serverDomain,
|
|
||||||
User? rootUser,
|
|
||||||
ServerHostingDetails? serverDetails,
|
|
||||||
required this.currentStep,
|
required this.currentStep,
|
||||||
required this.recoveryCapabilities,
|
required this.recoveryCapabilities,
|
||||||
|
final super.hetznerKey,
|
||||||
|
final super.cloudFlareKey,
|
||||||
|
final super.backblazeCredential,
|
||||||
|
final super.serverDomain,
|
||||||
|
final super.rootUser,
|
||||||
|
final super.serverDetails,
|
||||||
}) : super(
|
}) : super(
|
||||||
hetznerKey: hetznerKey,
|
|
||||||
cloudFlareKey: cloudFlareKey,
|
|
||||||
backblazeCredential: backblazeCredential,
|
|
||||||
serverDomain: serverDomain,
|
|
||||||
rootUser: rootUser,
|
|
||||||
serverDetails: serverDetails,
|
|
||||||
isServerStarted: true,
|
isServerStarted: true,
|
||||||
isServerResetedFirstTime: true,
|
isServerResetedFirstTime: true,
|
||||||
isServerResetedSecondTime: true,
|
isServerResetedSecondTime: true,
|
||||||
);
|
);
|
||||||
|
final RecoveryStep currentStep;
|
||||||
|
final ServerRecoveryCapabilities recoveryCapabilities;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
|
@ -317,14 +292,14 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
];
|
];
|
||||||
|
|
||||||
ServerInstallationRecovery copyWith({
|
ServerInstallationRecovery copyWith({
|
||||||
String? hetznerKey,
|
final String? hetznerKey,
|
||||||
String? cloudFlareKey,
|
final String? cloudFlareKey,
|
||||||
BackblazeCredential? backblazeCredential,
|
final BackblazeCredential? backblazeCredential,
|
||||||
ServerDomain? serverDomain,
|
final ServerDomain? serverDomain,
|
||||||
User? rootUser,
|
final User? rootUser,
|
||||||
ServerHostingDetails? serverDetails,
|
final ServerHostingDetails? serverDetails,
|
||||||
RecoveryStep? currentStep,
|
final RecoveryStep? currentStep,
|
||||||
ServerRecoveryCapabilities? recoveryCapabilities,
|
final ServerRecoveryCapabilities? recoveryCapabilities,
|
||||||
}) =>
|
}) =>
|
||||||
ServerInstallationRecovery(
|
ServerInstallationRecovery(
|
||||||
hetznerKey: hetznerKey ?? this.hetznerKey,
|
hetznerKey: hetznerKey ?? this.hetznerKey,
|
||||||
|
@ -337,8 +312,7 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
recoveryCapabilities: recoveryCapabilities ?? this.recoveryCapabilities,
|
recoveryCapabilities: recoveryCapabilities ?? this.recoveryCapabilities,
|
||||||
);
|
);
|
||||||
|
|
||||||
ServerInstallationFinished finish() {
|
ServerInstallationFinished finish() => ServerInstallationFinished(
|
||||||
return ServerInstallationFinished(
|
|
||||||
hetznerKey: hetznerKey!,
|
hetznerKey: hetznerKey!,
|
||||||
cloudFlareKey: cloudFlareKey!,
|
cloudFlareKey: cloudFlareKey!,
|
||||||
backblazeCredential: backblazeCredential!,
|
backblazeCredential: backblazeCredential!,
|
||||||
|
@ -349,5 +323,4 @@ class ServerInstallationRecovery extends ServerInstallationState {
|
||||||
isServerResetedFirstTime: true,
|
isServerResetedFirstTime: true,
|
||||||
isServerResetedSecondTime: true,
|
isServerResetedSecondTime: true,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_depe
|
||||||
part 'services_state.dart';
|
part 'services_state.dart';
|
||||||
|
|
||||||
class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
|
class ServicesCubit extends ServerInstallationDependendCubit<ServicesState> {
|
||||||
ServicesCubit(ServerInstallationCubit serverInstallationCubit)
|
ServicesCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(serverInstallationCubit, ServicesState.allOff());
|
: super(serverInstallationCubit, ServicesState.allOff());
|
||||||
final api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
@override
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
var statuses = await api.servicesPowerCheck();
|
final Map<ServiceTypes, bool> statuses = await api.servicesPowerCheck();
|
||||||
emit(
|
emit(
|
||||||
ServicesState(
|
ServicesState(
|
||||||
isPasswordManagerEnable: statuses[ServiceTypes.passwordManager]!,
|
isPasswordManagerEnable: statuses[ServiceTypes.passwordManager]!,
|
||||||
|
|
|
@ -1,6 +1,23 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'services_cubit.dart';
|
part of 'services_cubit.dart';
|
||||||
|
|
||||||
class ServicesState extends ServerInstallationDependendState {
|
class ServicesState extends ServerInstallationDependendState {
|
||||||
|
factory ServicesState.allOn() => const ServicesState(
|
||||||
|
isPasswordManagerEnable: true,
|
||||||
|
isCloudEnable: true,
|
||||||
|
isGitEnable: true,
|
||||||
|
isSocialNetworkEnable: true,
|
||||||
|
isVpnEnable: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
factory ServicesState.allOff() => const ServicesState(
|
||||||
|
isPasswordManagerEnable: false,
|
||||||
|
isCloudEnable: false,
|
||||||
|
isGitEnable: false,
|
||||||
|
isSocialNetworkEnable: false,
|
||||||
|
isVpnEnable: false,
|
||||||
|
);
|
||||||
const ServicesState({
|
const ServicesState({
|
||||||
required this.isPasswordManagerEnable,
|
required this.isPasswordManagerEnable,
|
||||||
required this.isCloudEnable,
|
required this.isCloudEnable,
|
||||||
|
@ -15,23 +32,8 @@ class ServicesState extends ServerInstallationDependendState {
|
||||||
final bool isSocialNetworkEnable;
|
final bool isSocialNetworkEnable;
|
||||||
final bool isVpnEnable;
|
final bool isVpnEnable;
|
||||||
|
|
||||||
factory ServicesState.allOff() => const ServicesState(
|
|
||||||
isPasswordManagerEnable: false,
|
|
||||||
isCloudEnable: false,
|
|
||||||
isGitEnable: false,
|
|
||||||
isSocialNetworkEnable: false,
|
|
||||||
isVpnEnable: false,
|
|
||||||
);
|
|
||||||
factory ServicesState.allOn() => const ServicesState(
|
|
||||||
isPasswordManagerEnable: true,
|
|
||||||
isCloudEnable: true,
|
|
||||||
isGitEnable: true,
|
|
||||||
isSocialNetworkEnable: true,
|
|
||||||
isVpnEnable: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
ServicesState enableList(
|
ServicesState enableList(
|
||||||
List<ServiceTypes> list,
|
final List<ServiceTypes> list,
|
||||||
) =>
|
) =>
|
||||||
ServicesState(
|
ServicesState(
|
||||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
||||||
|
@ -48,7 +50,7 @@ class ServicesState extends ServerInstallationDependendState {
|
||||||
);
|
);
|
||||||
|
|
||||||
ServicesState disableList(
|
ServicesState disableList(
|
||||||
List<ServiceTypes> list,
|
final List<ServiceTypes> list,
|
||||||
) =>
|
) =>
|
||||||
ServicesState(
|
ServicesState(
|
||||||
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager)
|
||||||
|
@ -74,7 +76,7 @@ class ServicesState extends ServerInstallationDependendState {
|
||||||
isVpnEnable
|
isVpnEnable
|
||||||
];
|
];
|
||||||
|
|
||||||
bool isEnableByType(ServiceTypes type) {
|
bool isEnableByType(final ServiceTypes type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ServiceTypes.passwordManager:
|
case ServiceTypes.passwordManager:
|
||||||
return isPasswordManagerEnable;
|
return isPasswordManagerEnable;
|
||||||
|
|
|
@ -1,78 +1,80 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_config_dependent/authentication_dependend_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
|
||||||
import '../../api_maps/server.dart';
|
import 'package:selfprivacy/logic/api_maps/server.dart';
|
||||||
|
|
||||||
export 'package:provider/provider.dart';
|
export 'package:provider/provider.dart';
|
||||||
|
|
||||||
part 'users_state.dart';
|
part 'users_state.dart';
|
||||||
|
|
||||||
class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
UsersCubit(ServerInstallationCubit serverInstallationCubit)
|
UsersCubit(final ServerInstallationCubit serverInstallationCubit)
|
||||||
: super(
|
: super(
|
||||||
serverInstallationCubit,
|
serverInstallationCubit,
|
||||||
const UsersState(
|
const UsersState(
|
||||||
<User>[], User(login: 'root'), User(login: 'loading...')));
|
<User>[], User(login: 'root'), User(login: 'loading...'),),);
|
||||||
Box<User> box = Hive.box<User>(BNames.usersBox);
|
Box<User> box = Hive.box<User>(BNames.usersBox);
|
||||||
Box serverInstallationBox = Hive.box(BNames.serverInstallationBox);
|
Box serverInstallationBox = Hive.box(BNames.serverInstallationBox);
|
||||||
|
|
||||||
final api = ServerApi();
|
final ServerApi api = ServerApi();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
if (serverInstallationCubit.state is ServerInstallationFinished) {
|
||||||
var loadedUsers = box.values.toList();
|
final List<User> loadedUsers = box.values.toList();
|
||||||
final primaryUser = serverInstallationBox.get(BNames.rootUser,
|
final primaryUser = serverInstallationBox.get(BNames.rootUser,
|
||||||
defaultValue: const User(login: 'loading...'));
|
defaultValue: const User(login: 'loading...'),);
|
||||||
List<String> rootKeys = [
|
final List<String> rootKeys = [
|
||||||
...serverInstallationBox.get(BNames.rootKeys, defaultValue: [])
|
...serverInstallationBox.get(BNames.rootKeys, defaultValue: [])
|
||||||
];
|
];
|
||||||
if (loadedUsers.isNotEmpty) {
|
if (loadedUsers.isNotEmpty) {
|
||||||
emit(UsersState(
|
emit(UsersState(
|
||||||
loadedUsers, User(login: 'root', sshKeys: rootKeys), primaryUser));
|
loadedUsers, User(login: 'root', sshKeys: rootKeys), primaryUser,),);
|
||||||
}
|
}
|
||||||
|
|
||||||
final usersFromServer = await api.getUsersList();
|
final ApiResponse<List<String>> usersFromServer = await api.getUsersList();
|
||||||
if (usersFromServer.isSuccess) {
|
if (usersFromServer.isSuccess) {
|
||||||
final updatedList =
|
final List<User> updatedList =
|
||||||
mergeLocalAndServerUsers(loadedUsers, usersFromServer.data);
|
mergeLocalAndServerUsers(loadedUsers, usersFromServer.data);
|
||||||
emit(UsersState(
|
emit(UsersState(
|
||||||
updatedList, User(login: 'root', sshKeys: rootKeys), primaryUser));
|
updatedList, User(login: 'root', sshKeys: rootKeys), primaryUser,),);
|
||||||
}
|
}
|
||||||
|
|
||||||
final usersWithSshKeys = await loadSshKeys(state.users);
|
final List<User> usersWithSshKeys = await loadSshKeys(state.users);
|
||||||
// Update the users it the box
|
// Update the users it the box
|
||||||
box.clear();
|
box.clear();
|
||||||
box.addAll(usersWithSshKeys);
|
box.addAll(usersWithSshKeys);
|
||||||
|
|
||||||
final rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
|
final User rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
|
||||||
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
|
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
|
||||||
final primaryUserWithSshKeys =
|
final User primaryUserWithSshKeys =
|
||||||
(await loadSshKeys([state.primaryUser])).first;
|
(await loadSshKeys([state.primaryUser])).first;
|
||||||
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
|
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
|
||||||
emit(UsersState(
|
emit(UsersState(
|
||||||
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys));
|
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys,),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<User> mergeLocalAndServerUsers(
|
List<User> mergeLocalAndServerUsers(
|
||||||
List<User> localUsers, List<String> serverUsers) {
|
final List<User> localUsers, final List<String> serverUsers,) {
|
||||||
// If local user not exists on server, add it with isFoundOnServer = false
|
// If local user not exists on server, add it with isFoundOnServer = false
|
||||||
// If server user not exists on local, add it
|
// If server user not exists on local, add it
|
||||||
|
|
||||||
List<User> mergedUsers = [];
|
final List<User> mergedUsers = [];
|
||||||
List<String> serverUsersCopy = List.from(serverUsers);
|
final List<String> serverUsersCopy = List.from(serverUsers);
|
||||||
|
|
||||||
for (var localUser in localUsers) {
|
for (final User localUser in localUsers) {
|
||||||
if (serverUsersCopy.contains(localUser.login)) {
|
if (serverUsersCopy.contains(localUser.login)) {
|
||||||
mergedUsers.add(User(
|
mergedUsers.add(User(
|
||||||
login: localUser.login,
|
login: localUser.login,
|
||||||
isFoundOnServer: true,
|
isFoundOnServer: true,
|
||||||
password: localUser.password,
|
password: localUser.password,
|
||||||
sshKeys: localUser.sshKeys,
|
sshKeys: localUser.sshKeys,
|
||||||
));
|
),);
|
||||||
serverUsersCopy.remove(localUser.login);
|
serverUsersCopy.remove(localUser.login);
|
||||||
} else {
|
} else {
|
||||||
mergedUsers.add(User(
|
mergedUsers.add(User(
|
||||||
|
@ -80,28 +82,28 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
isFoundOnServer: false,
|
isFoundOnServer: false,
|
||||||
password: localUser.password,
|
password: localUser.password,
|
||||||
note: localUser.note,
|
note: localUser.note,
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var serverUser in serverUsersCopy) {
|
for (final String serverUser in serverUsersCopy) {
|
||||||
mergedUsers.add(User(
|
mergedUsers.add(User(
|
||||||
login: serverUser,
|
login: serverUser,
|
||||||
isFoundOnServer: true,
|
isFoundOnServer: true,
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mergedUsers;
|
return mergedUsers;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<User>> loadSshKeys(List<User> users) async {
|
Future<List<User>> loadSshKeys(final List<User> users) async {
|
||||||
List<User> updatedUsers = [];
|
final List<User> updatedUsers = [];
|
||||||
|
|
||||||
for (var user in users) {
|
for (final User user in users) {
|
||||||
if (user.isFoundOnServer ||
|
if (user.isFoundOnServer ||
|
||||||
user.login == 'root' ||
|
user.login == 'root' ||
|
||||||
user.login == state.primaryUser.login) {
|
user.login == state.primaryUser.login) {
|
||||||
final sshKeys = await api.getUserSshKeys(user);
|
final ApiResponse<List<String>> sshKeys = await api.getUserSshKeys(user);
|
||||||
print('sshKeys for $user: ${sshKeys.data}');
|
print('sshKeys for $user: ${sshKeys.data}');
|
||||||
if (sshKeys.isSuccess) {
|
if (sshKeys.isSuccess) {
|
||||||
updatedUsers.add(User(
|
updatedUsers.add(User(
|
||||||
|
@ -110,14 +112,14 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
password: user.password,
|
password: user.password,
|
||||||
sshKeys: sshKeys.data,
|
sshKeys: sshKeys.data,
|
||||||
note: user.note,
|
note: user.note,
|
||||||
));
|
),);
|
||||||
} else {
|
} else {
|
||||||
updatedUsers.add(User(
|
updatedUsers.add(User(
|
||||||
login: user.login,
|
login: user.login,
|
||||||
isFoundOnServer: true,
|
isFoundOnServer: true,
|
||||||
password: user.password,
|
password: user.password,
|
||||||
note: user.note,
|
note: user.note,
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updatedUsers.add(User(
|
updatedUsers.add(User(
|
||||||
|
@ -125,7 +127,7 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
isFoundOnServer: false,
|
isFoundOnServer: false,
|
||||||
password: user.password,
|
password: user.password,
|
||||||
note: user.note,
|
note: user.note,
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updatedUsers;
|
return updatedUsers;
|
||||||
|
@ -133,27 +135,27 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
|
|
||||||
Future<void> refresh() async {
|
Future<void> refresh() async {
|
||||||
List<User> updatedUsers = List<User>.from(state.users);
|
List<User> updatedUsers = List<User>.from(state.users);
|
||||||
final usersFromServer = await api.getUsersList();
|
final ApiResponse<List<String>> usersFromServer = await api.getUsersList();
|
||||||
if (usersFromServer.isSuccess) {
|
if (usersFromServer.isSuccess) {
|
||||||
updatedUsers =
|
updatedUsers =
|
||||||
mergeLocalAndServerUsers(updatedUsers, usersFromServer.data);
|
mergeLocalAndServerUsers(updatedUsers, usersFromServer.data);
|
||||||
}
|
}
|
||||||
final usersWithSshKeys = await loadSshKeys(updatedUsers);
|
final List<User> usersWithSshKeys = await loadSshKeys(updatedUsers);
|
||||||
box.clear();
|
box.clear();
|
||||||
box.addAll(usersWithSshKeys);
|
box.addAll(usersWithSshKeys);
|
||||||
final rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
|
final User rootUserWithSshKeys = (await loadSshKeys([state.rootUser])).first;
|
||||||
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
|
serverInstallationBox.put(BNames.rootKeys, rootUserWithSshKeys.sshKeys);
|
||||||
final primaryUserWithSshKeys =
|
final User primaryUserWithSshKeys =
|
||||||
(await loadSshKeys([state.primaryUser])).first;
|
(await loadSshKeys([state.primaryUser])).first;
|
||||||
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
|
serverInstallationBox.put(BNames.rootUser, primaryUserWithSshKeys);
|
||||||
emit(UsersState(
|
emit(UsersState(
|
||||||
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys));
|
usersWithSshKeys, rootUserWithSshKeys, primaryUserWithSshKeys,),);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createUser(User user) async {
|
Future<void> createUser(final User user) async {
|
||||||
// If user exists on server, do nothing
|
// If user exists on server, do nothing
|
||||||
if (state.users.any((u) => u.login == user.login && u.isFoundOnServer)) {
|
if (state.users.any((final User u) => u.login == user.login && u.isFoundOnServer)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If user is root or primary user, do nothing
|
// If user is root or primary user, do nothing
|
||||||
|
@ -161,41 +163,41 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If API returned error, do nothing
|
// If API returned error, do nothing
|
||||||
final result = await api.createUser(user);
|
final ApiResponse<User> result = await api.createUser(user);
|
||||||
if (!result.isSuccess) {
|
if (!result.isSuccess) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadedUsers = List<User>.from(state.users);
|
final List<User> loadedUsers = List<User>.from(state.users);
|
||||||
loadedUsers.add(result.data);
|
loadedUsers.add(result.data);
|
||||||
await box.clear();
|
await box.clear();
|
||||||
await box.addAll(loadedUsers);
|
await box.addAll(loadedUsers);
|
||||||
emit(state.copyWith(users: loadedUsers));
|
emit(state.copyWith(users: loadedUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteUser(User user) async {
|
Future<void> deleteUser(final User user) async {
|
||||||
// If user is primary or root, don't delete
|
// If user is primary or root, don't delete
|
||||||
if (user.login == state.primaryUser.login || user.login == 'root') {
|
if (user.login == state.primaryUser.login || user.login == 'root') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var loadedUsers = List<User>.from(state.users);
|
final List<User> loadedUsers = List<User>.from(state.users);
|
||||||
final result = await api.deleteUser(user);
|
final bool result = await api.deleteUser(user);
|
||||||
if (result) {
|
if (result) {
|
||||||
loadedUsers.removeWhere((u) => u.login == user.login);
|
loadedUsers.removeWhere((final User u) => u.login == user.login);
|
||||||
await box.clear();
|
await box.clear();
|
||||||
await box.addAll(loadedUsers);
|
await box.addAll(loadedUsers);
|
||||||
emit(state.copyWith(users: loadedUsers));
|
emit(state.copyWith(users: loadedUsers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addSshKey(User user, String publicKey) async {
|
Future<void> addSshKey(final User user, final String publicKey) async {
|
||||||
// If adding root key, use api.addRootSshKey
|
// If adding root key, use api.addRootSshKey
|
||||||
// Otherwise, use api.addUserSshKey
|
// Otherwise, use api.addUserSshKey
|
||||||
if (user.login == 'root') {
|
if (user.login == 'root') {
|
||||||
final result = await api.addRootSshKey(publicKey);
|
final ApiResponse<void> result = await api.addRootSshKey(publicKey);
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
// Add ssh key to the array of root keys
|
// Add ssh key to the array of root keys
|
||||||
final rootKeys = serverInstallationBox
|
final List<String> rootKeys = serverInstallationBox
|
||||||
.get(BNames.rootKeys, defaultValue: []) as List<String>;
|
.get(BNames.rootKeys, defaultValue: []) as List<String>;
|
||||||
rootKeys.add(publicKey);
|
rootKeys.add(publicKey);
|
||||||
serverInstallationBox.put(BNames.rootKeys, rootKeys);
|
serverInstallationBox.put(BNames.rootKeys, rootKeys);
|
||||||
|
@ -207,17 +209,17 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
sshKeys: rootKeys,
|
sshKeys: rootKeys,
|
||||||
note: state.rootUser.note,
|
note: state.rootUser.note,
|
||||||
),
|
),
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final result = await api.addUserSshKey(user, publicKey);
|
final ApiResponse<void> result = await api.addUserSshKey(user, publicKey);
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
// If it is primary user, update primary user
|
// If it is primary user, update primary user
|
||||||
if (user.login == state.primaryUser.login) {
|
if (user.login == state.primaryUser.login) {
|
||||||
List<String> primaryUserKeys =
|
final List<String> primaryUserKeys =
|
||||||
List<String>.from(state.primaryUser.sshKeys);
|
List<String>.from(state.primaryUser.sshKeys);
|
||||||
primaryUserKeys.add(publicKey);
|
primaryUserKeys.add(publicKey);
|
||||||
final updatedUser = User(
|
final User updatedUser = User(
|
||||||
login: state.primaryUser.login,
|
login: state.primaryUser.login,
|
||||||
isFoundOnServer: true,
|
isFoundOnServer: true,
|
||||||
password: state.primaryUser.password,
|
password: state.primaryUser.password,
|
||||||
|
@ -227,12 +229,12 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
serverInstallationBox.put(BNames.rootUser, updatedUser);
|
serverInstallationBox.put(BNames.rootUser, updatedUser);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
primaryUser: updatedUser,
|
primaryUser: updatedUser,
|
||||||
));
|
),);
|
||||||
} else {
|
} else {
|
||||||
// If it is not primary user, update user
|
// If it is not primary user, update user
|
||||||
List<String> userKeys = List<String>.from(user.sshKeys);
|
final List<String> userKeys = List<String>.from(user.sshKeys);
|
||||||
userKeys.add(publicKey);
|
userKeys.add(publicKey);
|
||||||
final updatedUser = User(
|
final User updatedUser = User(
|
||||||
login: user.login,
|
login: user.login,
|
||||||
isFoundOnServer: true,
|
isFoundOnServer: true,
|
||||||
password: user.password,
|
password: user.password,
|
||||||
|
@ -242,23 +244,23 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
await box.putAt(box.values.toList().indexOf(user), updatedUser);
|
await box.putAt(box.values.toList().indexOf(user), updatedUser);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
users: box.values.toList(),
|
users: box.values.toList(),
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteSshKey(User user, String publicKey) async {
|
Future<void> deleteSshKey(final User user, final String publicKey) async {
|
||||||
// All keys are deleted via api.deleteUserSshKey
|
// All keys are deleted via api.deleteUserSshKey
|
||||||
|
|
||||||
final result = await api.deleteUserSshKey(user, publicKey);
|
final ApiResponse<void> result = await api.deleteUserSshKey(user, publicKey);
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
// If it is root user, delete key from root keys
|
// If it is root user, delete key from root keys
|
||||||
// If it is primary user, update primary user
|
// If it is primary user, update primary user
|
||||||
// If it is not primary user, update user
|
// If it is not primary user, update user
|
||||||
|
|
||||||
if (user.login == 'root') {
|
if (user.login == 'root') {
|
||||||
final rootKeys = serverInstallationBox
|
final List<String> rootKeys = serverInstallationBox
|
||||||
.get(BNames.rootKeys, defaultValue: []) as List<String>;
|
.get(BNames.rootKeys, defaultValue: []) as List<String>;
|
||||||
rootKeys.remove(publicKey);
|
rootKeys.remove(publicKey);
|
||||||
serverInstallationBox.put(BNames.rootKeys, rootKeys);
|
serverInstallationBox.put(BNames.rootKeys, rootKeys);
|
||||||
|
@ -270,14 +272,14 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
sshKeys: rootKeys,
|
sshKeys: rootKeys,
|
||||||
note: state.rootUser.note,
|
note: state.rootUser.note,
|
||||||
),
|
),
|
||||||
));
|
),);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (user.login == state.primaryUser.login) {
|
if (user.login == state.primaryUser.login) {
|
||||||
List<String> primaryUserKeys =
|
final List<String> primaryUserKeys =
|
||||||
List<String>.from(state.primaryUser.sshKeys);
|
List<String>.from(state.primaryUser.sshKeys);
|
||||||
primaryUserKeys.remove(publicKey);
|
primaryUserKeys.remove(publicKey);
|
||||||
final updatedUser = User(
|
final User updatedUser = User(
|
||||||
login: state.primaryUser.login,
|
login: state.primaryUser.login,
|
||||||
isFoundOnServer: true,
|
isFoundOnServer: true,
|
||||||
password: state.primaryUser.password,
|
password: state.primaryUser.password,
|
||||||
|
@ -287,12 +289,12 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
serverInstallationBox.put(BNames.rootUser, updatedUser);
|
serverInstallationBox.put(BNames.rootUser, updatedUser);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
primaryUser: updatedUser,
|
primaryUser: updatedUser,
|
||||||
));
|
),);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<String> userKeys = List<String>.from(user.sshKeys);
|
final List<String> userKeys = List<String>.from(user.sshKeys);
|
||||||
userKeys.remove(publicKey);
|
userKeys.remove(publicKey);
|
||||||
final updatedUser = User(
|
final User updatedUser = User(
|
||||||
login: user.login,
|
login: user.login,
|
||||||
isFoundOnServer: true,
|
isFoundOnServer: true,
|
||||||
password: user.password,
|
password: user.password,
|
||||||
|
@ -302,13 +304,13 @@ class UsersCubit extends ServerInstallationDependendCubit<UsersState> {
|
||||||
await box.putAt(box.values.toList().indexOf(user), updatedUser);
|
await box.putAt(box.values.toList().indexOf(user), updatedUser);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
users: box.values.toList(),
|
users: box.values.toList(),
|
||||||
));
|
),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void clear() async {
|
void clear() async {
|
||||||
emit(const UsersState(
|
emit(const UsersState(
|
||||||
<User>[], User(login: 'root'), User(login: 'loading...')));
|
<User>[], User(login: 'root'), User(login: 'loading...'),),);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'users_cubit.dart';
|
part of 'users_cubit.dart';
|
||||||
|
|
||||||
class UsersState extends ServerInstallationDependendState {
|
class UsersState extends ServerInstallationDependendState {
|
||||||
|
@ -11,22 +13,18 @@ class UsersState extends ServerInstallationDependendState {
|
||||||
List<Object> get props => [users, rootUser, primaryUser];
|
List<Object> get props => [users, rootUser, primaryUser];
|
||||||
|
|
||||||
UsersState copyWith({
|
UsersState copyWith({
|
||||||
List<User>? users,
|
final List<User>? users,
|
||||||
User? rootUser,
|
final User? rootUser,
|
||||||
User? primaryUser,
|
final User? primaryUser,
|
||||||
}) {
|
}) => UsersState(
|
||||||
return UsersState(
|
|
||||||
users ?? this.users,
|
users ?? this.users,
|
||||||
rootUser ?? this.rootUser,
|
rootUser ?? this.rootUser,
|
||||||
primaryUser ?? this.primaryUser,
|
primaryUser ?? this.primaryUser,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
bool isLoginRegistered(String login) {
|
bool isLoginRegistered(final String login) => users.any((final User user) => user.login == login) ||
|
||||||
return users.any((user) => user.login == login) ||
|
|
||||||
login == rootUser.login ||
|
login == rootUser.login ||
|
||||||
login == primaryUser.login;
|
login == primaryUser.login;
|
||||||
}
|
|
||||||
|
|
||||||
bool get isEmpty => users.isEmpty;
|
bool get isEmpty => users.isEmpty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:selfprivacy/config/hive_config.dart';
|
import 'package:selfprivacy/config/hive_config.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
import 'package:selfprivacy/logic/models/hive/backblaze_bucket.dart';
|
||||||
|
@ -22,38 +24,38 @@ class ApiConfigModel {
|
||||||
ServerDomain? _serverDomain;
|
ServerDomain? _serverDomain;
|
||||||
BackblazeBucket? _backblazeBucket;
|
BackblazeBucket? _backblazeBucket;
|
||||||
|
|
||||||
Future<void> storeHetznerKey(String value) async {
|
Future<void> storeHetznerKey(final String value) async {
|
||||||
await _box.put(BNames.hetznerKey, value);
|
await _box.put(BNames.hetznerKey, value);
|
||||||
_hetznerKey = value;
|
_hetznerKey = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeCloudFlareKey(String value) async {
|
Future<void> storeCloudFlareKey(final String value) async {
|
||||||
await _box.put(BNames.cloudFlareKey, value);
|
await _box.put(BNames.cloudFlareKey, value);
|
||||||
_cloudFlareKey = value;
|
_cloudFlareKey = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeBackblazeCredential(BackblazeCredential value) async {
|
Future<void> storeBackblazeCredential(final BackblazeCredential value) async {
|
||||||
await _box.put(BNames.backblazeCredential, value);
|
await _box.put(BNames.backblazeCredential, value);
|
||||||
|
|
||||||
_backblazeCredential = value;
|
_backblazeCredential = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeServerDomain(ServerDomain value) async {
|
Future<void> storeServerDomain(final ServerDomain value) async {
|
||||||
await _box.put(BNames.serverDomain, value);
|
await _box.put(BNames.serverDomain, value);
|
||||||
_serverDomain = value;
|
_serverDomain = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeServerDetails(ServerHostingDetails value) async {
|
Future<void> storeServerDetails(final ServerHostingDetails value) async {
|
||||||
await _box.put(BNames.serverDetails, value);
|
await _box.put(BNames.serverDetails, value);
|
||||||
_serverDetails = value;
|
_serverDetails = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> storeBackblazeBucket(BackblazeBucket value) async {
|
Future<void> storeBackblazeBucket(final BackblazeBucket value) async {
|
||||||
await _box.put(BNames.backblazeBucket, value);
|
await _box.put(BNames.backblazeBucket, value);
|
||||||
_backblazeBucket = value;
|
_backblazeBucket = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
void clear() {
|
||||||
_hetznerKey = null;
|
_hetznerKey = null;
|
||||||
_cloudFlareKey = null;
|
_cloudFlareKey = null;
|
||||||
_backblazeCredential = null;
|
_backblazeCredential = null;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/models/message.dart';
|
import 'package:selfprivacy/logic/models/message.dart';
|
||||||
|
|
||||||
|
@ -6,7 +8,7 @@ class ConsoleModel extends ChangeNotifier {
|
||||||
|
|
||||||
List<Message> get messages => _messages;
|
List<Message> get messages => _messages;
|
||||||
|
|
||||||
void addMessage(Message message) {
|
void addMessage(final Message message) {
|
||||||
messages.add(message);
|
messages.add(message);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,18 @@ class NavigationService {
|
||||||
|
|
||||||
NavigatorState? get navigator => navigatorKey.currentState;
|
NavigatorState? get navigator => navigatorKey.currentState;
|
||||||
|
|
||||||
void showPopUpDialog(AlertDialog dialog) {
|
void showPopUpDialog(final AlertDialog dialog) {
|
||||||
final context = navigatorKey.currentState!.overlay!.context;
|
final BuildContext context = navigatorKey.currentState!.overlay!.context;
|
||||||
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (_) => dialog,
|
builder: (final _) => dialog,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showSnackBar(String text) {
|
void showSnackBar(final String text) {
|
||||||
final state = scaffoldMessengerKey.currentState!;
|
final ScaffoldMessengerState state = scaffoldMessengerKey.currentState!;
|
||||||
final snack = SnackBar(
|
final SnackBar snack = SnackBar(
|
||||||
backgroundColor: BrandColors.black.withOpacity(0.8),
|
backgroundColor: BrandColors.black.withOpacity(0.8),
|
||||||
content: Text(text, style: buttonTitleText),
|
content: Text(text, style: buttonTitleText),
|
||||||
duration: const Duration(seconds: 2),
|
duration: const Duration(seconds: 2),
|
||||||
|
|
|
@ -8,7 +8,7 @@ class BackblazeBucket {
|
||||||
{required this.bucketId,
|
{required this.bucketId,
|
||||||
required this.bucketName,
|
required this.bucketName,
|
||||||
required this.applicationKeyId,
|
required this.applicationKeyId,
|
||||||
required this.applicationKey});
|
required this.applicationKey,});
|
||||||
|
|
||||||
@HiveField(0)
|
@HiveField(0)
|
||||||
final String bucketId;
|
final String bucketId;
|
||||||
|
@ -23,7 +23,5 @@ class BackblazeBucket {
|
||||||
final String bucketName;
|
final String bucketName;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => bucketName;
|
||||||
return bucketName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,14 @@ class BackblazeCredential {
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
final String applicationKey;
|
final String applicationKey;
|
||||||
|
|
||||||
get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
String get encodedApiKey => encodedBackblazeKey(keyId, applicationKey);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => '$keyId: $encodedApiKey';
|
||||||
return '$keyId: $encodedApiKey';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String encodedBackblazeKey(String? keyId, String? applicationKey) {
|
String encodedBackblazeKey(final String? keyId, final String? applicationKey) {
|
||||||
String apiKey = '$keyId:$applicationKey';
|
final String apiKey = '$keyId:$applicationKey';
|
||||||
String encodedApiKey = base64.encode(utf8.encode(apiKey));
|
final String encodedApiKey = base64.encode(utf8.encode(apiKey));
|
||||||
return encodedApiKey;
|
return encodedApiKey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,7 @@ class ServerHostingDetails {
|
||||||
@HiveField(6, defaultValue: ServerProvider.hetzner)
|
@HiveField(6, defaultValue: ServerProvider.hetzner)
|
||||||
final ServerProvider provider;
|
final ServerProvider provider;
|
||||||
|
|
||||||
ServerHostingDetails copyWith({DateTime? startTime}) {
|
ServerHostingDetails copyWith({final DateTime? startTime}) => ServerHostingDetails(
|
||||||
return ServerHostingDetails(
|
|
||||||
startTime: startTime ?? this.startTime,
|
startTime: startTime ?? this.startTime,
|
||||||
createTime: createTime,
|
createTime: createTime,
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -45,7 +44,6 @@ class ServerHostingDetails {
|
||||||
apiToken: apiToken,
|
apiToken: apiToken,
|
||||||
provider: provider,
|
provider: provider,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => id.toString();
|
String toString() => id.toString();
|
||||||
|
|
|
@ -20,9 +20,7 @@ class ServerDomain {
|
||||||
final DnsProvider provider;
|
final DnsProvider provider;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => '$domainName: $zoneId';
|
||||||
return '$domainName: $zoneId';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@HiveType(typeId: 100)
|
@HiveType(typeId: 100)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
@ -37,7 +39,5 @@ class User extends Equatable {
|
||||||
Color get color => stringToColor(login);
|
Color get color => stringToColor(login);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => '$login, ${isFoundOnServer ? 'found' : 'not found'}, ${sshKeys.length} ssh keys, note: $note';
|
||||||
return '$login, ${isFoundOnServer ? 'found' : 'not found'}, ${sshKeys.length} ssh keys, note: $note';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
part of 'user.dart';
|
part of 'user.dart';
|
||||||
|
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
@ -11,9 +13,9 @@ class UserAdapter extends TypeAdapter<User> {
|
||||||
final int typeId = 1;
|
final int typeId = 1;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
User read(BinaryReader reader) {
|
User read(final BinaryReader reader) {
|
||||||
final numOfFields = reader.readByte();
|
final int numOfFields = reader.readByte();
|
||||||
final fields = <int, dynamic>{
|
final Map<int, dynamic> fields = <int, dynamic>{
|
||||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||||
};
|
};
|
||||||
return User(
|
return User(
|
||||||
|
@ -26,7 +28,7 @@ class UserAdapter extends TypeAdapter<User> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, User obj) {
|
void write(final BinaryWriter writer, final User obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(5)
|
..writeByte(5)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
|
@ -45,7 +47,7 @@ class UserAdapter extends TypeAdapter<User> {
|
||||||
int get hashCode => typeId.hashCode;
|
int get hashCode => typeId.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(final Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
other is UserAdapter &&
|
other is UserAdapter &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
import 'package:selfprivacy/logic/common_enum/common_enum.dart';
|
||||||
import 'package:selfprivacy/utils/password_generator.dart';
|
import 'package:selfprivacy/utils/password_generator.dart';
|
||||||
|
|
||||||
import 'hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class Job extends Equatable {
|
class Job extends Equatable {
|
||||||
Job({
|
Job({
|
||||||
String? id,
|
|
||||||
required this.title,
|
required this.title,
|
||||||
|
final String? id,
|
||||||
}) : id = id ?? StringGenerators.simpleId();
|
}) : id = id ?? StringGenerators.simpleId();
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
|
@ -45,8 +47,8 @@ class DeleteUserJob extends Job {
|
||||||
class ToggleJob extends Job {
|
class ToggleJob extends Job {
|
||||||
ToggleJob({
|
ToggleJob({
|
||||||
required this.type,
|
required this.type,
|
||||||
required String title,
|
required final super.title,
|
||||||
}) : super(title: title);
|
});
|
||||||
|
|
||||||
final dynamic type;
|
final dynamic type;
|
||||||
|
|
||||||
|
@ -56,12 +58,11 @@ class ToggleJob extends Job {
|
||||||
|
|
||||||
class ServiceToggleJob extends ToggleJob {
|
class ServiceToggleJob extends ToggleJob {
|
||||||
ServiceToggleJob({
|
ServiceToggleJob({
|
||||||
required ServiceTypes type,
|
required final ServiceTypes super.type,
|
||||||
required this.needToTurnOn,
|
required this.needToTurnOn,
|
||||||
}) : super(
|
}) : super(
|
||||||
title:
|
title:
|
||||||
'${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}',
|
'${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}',
|
||||||
type: type,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
final bool needToTurnOn;
|
final bool needToTurnOn;
|
||||||
|
|
|
@ -4,6 +4,9 @@ part 'api_token.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class ApiToken {
|
class ApiToken {
|
||||||
|
|
||||||
|
factory ApiToken.fromJson(final Map<String, dynamic> json) =>
|
||||||
|
_$ApiTokenFromJson(json);
|
||||||
ApiToken({
|
ApiToken({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.date,
|
required this.date,
|
||||||
|
@ -14,7 +17,4 @@ class ApiToken {
|
||||||
final DateTime date;
|
final DateTime date;
|
||||||
@JsonKey(name: 'is_caller')
|
@JsonKey(name: 'is_caller')
|
||||||
final bool isCaller;
|
final bool isCaller;
|
||||||
|
|
||||||
factory ApiToken.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ApiTokenFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
@ -5,18 +7,18 @@ part 'auto_upgrade_settings.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(createToJson: true)
|
@JsonSerializable(createToJson: true)
|
||||||
class AutoUpgradeSettings extends Equatable {
|
class AutoUpgradeSettings extends Equatable {
|
||||||
final bool enable;
|
factory AutoUpgradeSettings.fromJson(final Map<String, dynamic> json) =>
|
||||||
final bool allowReboot;
|
_$AutoUpgradeSettingsFromJson(json);
|
||||||
|
|
||||||
const AutoUpgradeSettings({
|
const AutoUpgradeSettings({
|
||||||
required this.enable,
|
required this.enable,
|
||||||
required this.allowReboot,
|
required this.allowReboot,
|
||||||
});
|
});
|
||||||
|
final bool enable;
|
||||||
|
final bool allowReboot;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [enable, allowReboot];
|
List<Object?> get props => [enable, allowReboot];
|
||||||
factory AutoUpgradeSettings.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$AutoUpgradeSettingsFromJson(json);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => _$AutoUpgradeSettingsToJson(this);
|
Map<String, dynamic> toJson() => _$AutoUpgradeSettingsToJson(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,14 @@ part 'backup.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Backup {
|
class Backup {
|
||||||
|
|
||||||
|
factory Backup.fromJson(final Map<String, dynamic> json) => _$BackupFromJson(json);
|
||||||
Backup({required this.time, required this.id});
|
Backup({required this.time, required this.id});
|
||||||
|
|
||||||
// Time of the backup
|
// Time of the backup
|
||||||
final DateTime time;
|
final DateTime time;
|
||||||
@JsonKey(name: 'short_id')
|
@JsonKey(name: 'short_id')
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
factory Backup.fromJson(Map<String, dynamic> json) => _$BackupFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BackupStatusEnum {
|
enum BackupStatusEnum {
|
||||||
|
@ -33,16 +33,16 @@ enum BackupStatusEnum {
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class BackupStatus {
|
class BackupStatus {
|
||||||
|
|
||||||
|
factory BackupStatus.fromJson(final Map<String, dynamic> json) =>
|
||||||
|
_$BackupStatusFromJson(json);
|
||||||
BackupStatus(
|
BackupStatus(
|
||||||
{required this.status,
|
{required this.status,
|
||||||
required this.progress,
|
required this.progress,
|
||||||
required this.errorMessage});
|
required this.errorMessage,});
|
||||||
|
|
||||||
final BackupStatusEnum status;
|
final BackupStatusEnum status;
|
||||||
final double progress;
|
final double progress;
|
||||||
@JsonKey(name: 'error_message')
|
@JsonKey(name: 'error_message')
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
|
||||||
factory BackupStatus.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$BackupStatusFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ part 'device_token.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class DeviceToken {
|
class DeviceToken {
|
||||||
|
|
||||||
|
factory DeviceToken.fromJson(final Map<String, dynamic> json) =>
|
||||||
|
_$DeviceTokenFromJson(json);
|
||||||
DeviceToken({
|
DeviceToken({
|
||||||
required this.device,
|
required this.device,
|
||||||
required this.token,
|
required this.token,
|
||||||
|
@ -11,7 +14,4 @@ class DeviceToken {
|
||||||
|
|
||||||
final String device;
|
final String device;
|
||||||
final String token;
|
final String token;
|
||||||
|
|
||||||
factory DeviceToken.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$DeviceTokenFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,5 @@ class DnsRecord {
|
||||||
final int priority;
|
final int priority;
|
||||||
final bool proxied;
|
final bool proxied;
|
||||||
|
|
||||||
toJson() => _$DnsRecordToJson(this);
|
Map<String, dynamic> toJson() => _$DnsRecordToJson(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,22 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
part 'hetzner_server_info.g.dart';
|
part 'hetzner_server_info.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class HetznerServerInfo {
|
class HetznerServerInfo {
|
||||||
|
|
||||||
|
HetznerServerInfo(
|
||||||
|
this.id,
|
||||||
|
this.name,
|
||||||
|
this.status,
|
||||||
|
this.created,
|
||||||
|
this.serverType,
|
||||||
|
this.location,
|
||||||
|
this.publicNet,
|
||||||
|
this.volumes,
|
||||||
|
);
|
||||||
final int id;
|
final int id;
|
||||||
final String name;
|
final String name;
|
||||||
final ServerStatus status;
|
final ServerStatus status;
|
||||||
|
@ -19,46 +32,35 @@ class HetznerServerInfo {
|
||||||
@JsonKey(name: 'public_net')
|
@JsonKey(name: 'public_net')
|
||||||
final HetznerPublicNetInfo publicNet;
|
final HetznerPublicNetInfo publicNet;
|
||||||
|
|
||||||
static HetznerLocation locationFromJson(Map json) =>
|
static HetznerLocation locationFromJson(final Map json) =>
|
||||||
HetznerLocation.fromJson(json['location']);
|
HetznerLocation.fromJson(json['location']);
|
||||||
|
|
||||||
static HetznerServerInfo fromJson(Map<String, dynamic> json) =>
|
static HetznerServerInfo fromJson(final Map<String, dynamic> json) =>
|
||||||
_$HetznerServerInfoFromJson(json);
|
_$HetznerServerInfoFromJson(json);
|
||||||
|
|
||||||
HetznerServerInfo(
|
|
||||||
this.id,
|
|
||||||
this.name,
|
|
||||||
this.status,
|
|
||||||
this.created,
|
|
||||||
this.serverType,
|
|
||||||
this.location,
|
|
||||||
this.publicNet,
|
|
||||||
this.volumes,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class HetznerPublicNetInfo {
|
class HetznerPublicNetInfo {
|
||||||
final HetznerIp4 ipv4;
|
|
||||||
|
|
||||||
static HetznerPublicNetInfo fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$HetznerPublicNetInfoFromJson(json);
|
|
||||||
|
|
||||||
HetznerPublicNetInfo(this.ipv4);
|
HetznerPublicNetInfo(this.ipv4);
|
||||||
|
final HetznerIp4 ipv4;
|
||||||
|
|
||||||
|
static HetznerPublicNetInfo fromJson(final Map<String, dynamic> json) =>
|
||||||
|
_$HetznerPublicNetInfoFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class HetznerIp4 {
|
class HetznerIp4 {
|
||||||
|
|
||||||
|
HetznerIp4(this.id, this.ip, this.blocked, this.reverseDns);
|
||||||
final bool blocked;
|
final bool blocked;
|
||||||
@JsonKey(name: 'dns_ptr')
|
@JsonKey(name: 'dns_ptr')
|
||||||
final String reverseDns;
|
final String reverseDns;
|
||||||
final int id;
|
final int id;
|
||||||
final String ip;
|
final String ip;
|
||||||
|
|
||||||
static HetznerIp4 fromJson(Map<String, dynamic> json) =>
|
static HetznerIp4 fromJson(final Map<String, dynamic> json) =>
|
||||||
_$HetznerIp4FromJson(json);
|
_$HetznerIp4FromJson(json);
|
||||||
|
|
||||||
HetznerIp4(this.id, this.ip, this.blocked, this.reverseDns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ServerStatus {
|
enum ServerStatus {
|
||||||
|
@ -75,15 +77,15 @@ enum ServerStatus {
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class HetznerServerTypeInfo {
|
class HetznerServerTypeInfo {
|
||||||
|
|
||||||
|
HetznerServerTypeInfo(this.cores, this.memory, this.disk, this.prices);
|
||||||
final int cores;
|
final int cores;
|
||||||
final num memory;
|
final num memory;
|
||||||
final int disk;
|
final int disk;
|
||||||
|
|
||||||
final List<HetznerPriceInfo> prices;
|
final List<HetznerPriceInfo> prices;
|
||||||
|
|
||||||
HetznerServerTypeInfo(this.cores, this.memory, this.disk, this.prices);
|
static HetznerServerTypeInfo fromJson(final Map<String, dynamic> json) =>
|
||||||
|
|
||||||
static HetznerServerTypeInfo fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$HetznerServerTypeInfoFromJson(json);
|
_$HetznerServerTypeInfoFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,14 +99,16 @@ class HetznerPriceInfo {
|
||||||
@JsonKey(name: 'price_monthly', fromJson: HetznerPriceInfo.getPrice)
|
@JsonKey(name: 'price_monthly', fromJson: HetznerPriceInfo.getPrice)
|
||||||
final double monthly;
|
final double monthly;
|
||||||
|
|
||||||
static HetznerPriceInfo fromJson(Map<String, dynamic> json) =>
|
static HetznerPriceInfo fromJson(final Map<String, dynamic> json) =>
|
||||||
_$HetznerPriceInfoFromJson(json);
|
_$HetznerPriceInfoFromJson(json);
|
||||||
|
|
||||||
static double getPrice(Map json) => double.parse(json['gross'] as String);
|
static double getPrice(final Map json) => double.parse(json['gross'] as String);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class HetznerLocation {
|
class HetznerLocation {
|
||||||
|
|
||||||
|
HetznerLocation(this.country, this.city, this.description, this.zone);
|
||||||
final String country;
|
final String country;
|
||||||
final String city;
|
final String city;
|
||||||
final String description;
|
final String description;
|
||||||
|
@ -112,8 +116,6 @@ class HetznerLocation {
|
||||||
@JsonKey(name: 'network_zone')
|
@JsonKey(name: 'network_zone')
|
||||||
final String zone;
|
final String zone;
|
||||||
|
|
||||||
HetznerLocation(this.country, this.city, this.description, this.zone);
|
static HetznerLocation fromJson(final Map<String, dynamic> json) =>
|
||||||
|
|
||||||
static HetznerLocation fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$HetznerLocationFromJson(json);
|
_$HetznerLocationFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
@ -5,6 +7,9 @@ part 'recovery_token_status.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class RecoveryKeyStatus extends Equatable {
|
class RecoveryKeyStatus extends Equatable {
|
||||||
|
|
||||||
|
factory RecoveryKeyStatus.fromJson(final Map<String, dynamic> json) =>
|
||||||
|
_$RecoveryKeyStatusFromJson(json);
|
||||||
const RecoveryKeyStatus({
|
const RecoveryKeyStatus({
|
||||||
required this.exists,
|
required this.exists,
|
||||||
required this.valid,
|
required this.valid,
|
||||||
|
@ -20,9 +25,6 @@ class RecoveryKeyStatus extends Equatable {
|
||||||
final int? usesLeft;
|
final int? usesLeft;
|
||||||
final bool valid;
|
final bool valid;
|
||||||
|
|
||||||
factory RecoveryKeyStatus.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$RecoveryKeyStatusFromJson(json);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
exists,
|
exists,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
|
@ -5,6 +7,9 @@ part 'server_configurations.g.dart';
|
||||||
|
|
||||||
@JsonSerializable(createToJson: true)
|
@JsonSerializable(createToJson: true)
|
||||||
class AutoUpgradeConfigurations extends Equatable {
|
class AutoUpgradeConfigurations extends Equatable {
|
||||||
|
|
||||||
|
factory AutoUpgradeConfigurations.fromJson(final Map<String, dynamic> json) =>
|
||||||
|
_$AutoUpgradeConfigurationsFromJson(json);
|
||||||
const AutoUpgradeConfigurations({
|
const AutoUpgradeConfigurations({
|
||||||
required this.enable,
|
required this.enable,
|
||||||
required this.allowReboot,
|
required this.allowReboot,
|
||||||
|
@ -12,9 +17,6 @@ class AutoUpgradeConfigurations extends Equatable {
|
||||||
|
|
||||||
final bool enable;
|
final bool enable;
|
||||||
final bool allowReboot;
|
final bool allowReboot;
|
||||||
|
|
||||||
factory AutoUpgradeConfigurations.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$AutoUpgradeConfigurationsFromJson(json);
|
|
||||||
Map<String, dynamic> toJson() => _$AutoUpgradeConfigurationsToJson(this);
|
Map<String, dynamic> toJson() => _$AutoUpgradeConfigurationsToJson(this);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
final formatter = DateFormat('hh:mm');
|
final DateFormat formatter = DateFormat('hh:mm');
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
Message({this.text, this.type = MessageType.normal}) : time = DateTime.now();
|
Message({this.text, this.type = MessageType.normal}) : time = DateTime.now();
|
||||||
|
@ -10,7 +10,7 @@ class Message {
|
||||||
final MessageType type;
|
final MessageType type;
|
||||||
String get timeString => formatter.format(time);
|
String get timeString => formatter.format(time);
|
||||||
|
|
||||||
static Message warn({String? text}) => Message(
|
static Message warn({final String? text}) => Message(
|
||||||
text: text,
|
text: text,
|
||||||
type: MessageType.warning,
|
type: MessageType.warning,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:selfprivacy/logic/models/state_types.dart';
|
import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
@ -15,7 +17,7 @@ class ProviderModel extends Equatable {
|
||||||
final StateType state;
|
final StateType state;
|
||||||
final ProviderType type;
|
final ProviderType type;
|
||||||
|
|
||||||
ProviderModel updateState(StateType newState) => ProviderModel(
|
ProviderModel updateState(final StateType newState) => ProviderModel(
|
||||||
state: newState,
|
state: newState,
|
||||||
type: type,
|
type: type,
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
class ServerBasicInfo {
|
class ServerBasicInfo {
|
||||||
final int id;
|
|
||||||
final String name;
|
|
||||||
final String reverseDns;
|
|
||||||
final String ip;
|
|
||||||
final DateTime created;
|
|
||||||
final int volumeId;
|
|
||||||
|
|
||||||
ServerBasicInfo({
|
ServerBasicInfo({
|
||||||
required this.id,
|
required this.id,
|
||||||
|
@ -14,34 +10,20 @@ class ServerBasicInfo {
|
||||||
required this.created,
|
required this.created,
|
||||||
required this.volumeId,
|
required this.volumeId,
|
||||||
});
|
});
|
||||||
|
final int id;
|
||||||
|
final String name;
|
||||||
|
final String reverseDns;
|
||||||
|
final String ip;
|
||||||
|
final DateTime created;
|
||||||
|
final int volumeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
final bool isIpValid;
|
|
||||||
final bool isReverseDnsValid;
|
|
||||||
|
|
||||||
ServerBasicInfoWithValidators({
|
|
||||||
required int id,
|
|
||||||
required String name,
|
|
||||||
required String reverseDns,
|
|
||||||
required String ip,
|
|
||||||
required DateTime created,
|
|
||||||
required int volumeId,
|
|
||||||
required this.isIpValid,
|
|
||||||
required this.isReverseDnsValid,
|
|
||||||
}) : super(
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
reverseDns: reverseDns,
|
|
||||||
ip: ip,
|
|
||||||
created: created,
|
|
||||||
volumeId: volumeId,
|
|
||||||
);
|
|
||||||
|
|
||||||
ServerBasicInfoWithValidators.fromServerBasicInfo({
|
ServerBasicInfoWithValidators.fromServerBasicInfo({
|
||||||
required ServerBasicInfo serverBasicInfo,
|
required final ServerBasicInfo serverBasicInfo,
|
||||||
required isIpValid,
|
required final isIpValid,
|
||||||
required isReverseDnsValid,
|
required final isReverseDnsValid,
|
||||||
}) : this(
|
}) : this(
|
||||||
id: serverBasicInfo.id,
|
id: serverBasicInfo.id,
|
||||||
name: serverBasicInfo.name,
|
name: serverBasicInfo.name,
|
||||||
|
@ -52,4 +34,17 @@ class ServerBasicInfoWithValidators extends ServerBasicInfo {
|
||||||
isIpValid: isIpValid,
|
isIpValid: isIpValid,
|
||||||
isReverseDnsValid: isReverseDnsValid,
|
isReverseDnsValid: isReverseDnsValid,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ServerBasicInfoWithValidators({
|
||||||
|
required final super.id,
|
||||||
|
required final super.name,
|
||||||
|
required final super.reverseDns,
|
||||||
|
required final super.ip,
|
||||||
|
required final super.created,
|
||||||
|
required final super.volumeId,
|
||||||
|
required this.isIpValid,
|
||||||
|
required this.isReverseDnsValid,
|
||||||
|
});
|
||||||
|
final bool isIpValid;
|
||||||
|
final bool isReverseDnsValid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,22 @@
|
||||||
class ServerStatus {
|
class ServerStatus {
|
||||||
final StatusTypes http;
|
|
||||||
final StatusTypes imap;
|
|
||||||
final StatusTypes smtp;
|
|
||||||
|
|
||||||
ServerStatus({
|
ServerStatus({
|
||||||
required this.http,
|
required this.http,
|
||||||
this.imap = StatusTypes.nodata,
|
this.imap = StatusTypes.nodata,
|
||||||
this.smtp = StatusTypes.nodata,
|
this.smtp = StatusTypes.nodata,
|
||||||
});
|
});
|
||||||
|
final StatusTypes http;
|
||||||
|
final StatusTypes imap;
|
||||||
|
final StatusTypes smtp;
|
||||||
|
|
||||||
ServerStatus fromJson(Map<String, dynamic> json) {
|
ServerStatus fromJson(final Map<String, dynamic> json) => ServerStatus(
|
||||||
return ServerStatus(
|
|
||||||
http: statusTypeFromNumber(json['http']),
|
http: statusTypeFromNumber(json['http']),
|
||||||
imap: statusTypeFromNumber(json['imap']),
|
imap: statusTypeFromNumber(json['imap']),
|
||||||
smtp: statusTypeFromNumber(json['smtp']),
|
smtp: statusTypeFromNumber(json['smtp']),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusTypes statusTypeFromNumber(int? number) {
|
StatusTypes statusTypeFromNumber(final int? number) {
|
||||||
if (number == 0) {
|
if (number == 0) {
|
||||||
return StatusTypes.ok;
|
return StatusTypes.ok;
|
||||||
} else if (number == 1) {
|
} else if (number == 1) {
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:timezone/timezone.dart';
|
import 'package:timezone/timezone.dart';
|
||||||
|
|
||||||
class TimeZoneSettings {
|
class TimeZoneSettings {
|
||||||
final Location timezone;
|
|
||||||
|
|
||||||
TimeZoneSettings(this.timezone);
|
factory TimeZoneSettings.fromString(final String string) {
|
||||||
|
final Location location = timeZoneDatabase.locations[string]!;
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'timezone': timezone.name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
factory TimeZoneSettings.fromString(String string) {
|
|
||||||
var location = timeZoneDatabase.locations[string]!;
|
|
||||||
return TimeZoneSettings(location);
|
return TimeZoneSettings(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeZoneSettings(this.timezone);
|
||||||
|
final Location timezone;
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'timezone': timezone.name,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
@ -11,11 +13,11 @@ import 'package:selfprivacy/ui/pages/root_route.dart';
|
||||||
import 'package:wakelock/wakelock.dart';
|
import 'package:wakelock/wakelock.dart';
|
||||||
import 'package:timezone/data/latest.dart' as tz;
|
import 'package:timezone/data/latest.dart' as tz;
|
||||||
|
|
||||||
import 'config/bloc_config.dart';
|
import 'package:selfprivacy/config/bloc_config.dart';
|
||||||
import 'config/bloc_observer.dart';
|
import 'package:selfprivacy/config/bloc_observer.dart';
|
||||||
import 'config/get_it_config.dart';
|
import 'package:selfprivacy/config/get_it_config.dart';
|
||||||
import 'config/localization.dart';
|
import 'package:selfprivacy/config/localization.dart';
|
||||||
import 'logic/cubit/app_settings/app_settings_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
@ -34,11 +36,11 @@ void main() async {
|
||||||
await EasyLocalization.ensureInitialized();
|
await EasyLocalization.ensureInitialized();
|
||||||
tz.initializeTimeZones();
|
tz.initializeTimeZones();
|
||||||
|
|
||||||
final lightThemeData = await AppThemeFactory.create(
|
final ThemeData lightThemeData = await AppThemeFactory.create(
|
||||||
isDark: false,
|
isDark: false,
|
||||||
fallbackColor: BrandColors.primary,
|
fallbackColor: BrandColors.primary,
|
||||||
);
|
);
|
||||||
final darkThemeData = await AppThemeFactory.create(
|
final ThemeData darkThemeData = await AppThemeFactory.create(
|
||||||
isDark: true,
|
isDark: true,
|
||||||
fallbackColor: BrandColors.primary,
|
fallbackColor: BrandColors.primary,
|
||||||
);
|
);
|
||||||
|
@ -48,30 +50,28 @@ void main() async {
|
||||||
child: MyApp(
|
child: MyApp(
|
||||||
lightThemeData: lightThemeData,
|
lightThemeData: lightThemeData,
|
||||||
darkThemeData: darkThemeData,
|
darkThemeData: darkThemeData,
|
||||||
))),
|
),),),
|
||||||
blocObserver: SimpleBlocObserver(),
|
blocObserver: SimpleBlocObserver(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({
|
const MyApp({
|
||||||
Key? key,
|
|
||||||
required this.lightThemeData,
|
required this.lightThemeData,
|
||||||
required this.darkThemeData,
|
required this.darkThemeData,
|
||||||
}) : super(key: key);
|
final super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final ThemeData lightThemeData;
|
final ThemeData lightThemeData;
|
||||||
final ThemeData darkThemeData;
|
final ThemeData darkThemeData;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Localization(
|
||||||
return Localization(
|
|
||||||
child: AnnotatedRegion<SystemUiOverlayStyle>(
|
child: AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle.light, // Manually changing appbar color
|
value: SystemUiOverlayStyle.light, // Manually changing appbar color
|
||||||
child: BlocAndProviderConfig(
|
child: BlocAndProviderConfig(
|
||||||
child: BlocBuilder<AppSettingsCubit, AppSettingsState>(
|
child: BlocBuilder<AppSettingsCubit, AppSettingsState>(
|
||||||
builder: (context, appSettings) {
|
builder: (final BuildContext context, final AppSettingsState appSettings) => MaterialApp(
|
||||||
return MaterialApp(
|
|
||||||
scaffoldMessengerKey:
|
scaffoldMessengerKey:
|
||||||
getIt.get<NavigationService>().scaffoldMessengerKey,
|
getIt.get<NavigationService>().scaffoldMessengerKey,
|
||||||
navigatorKey: getIt.get<NavigationService>().navigatorKey,
|
navigatorKey: getIt.get<NavigationService>().navigatorKey,
|
||||||
|
@ -87,20 +87,18 @@ class MyApp extends StatelessWidget {
|
||||||
home: appSettings.isOnboardingShowing
|
home: appSettings.isOnboardingShowing
|
||||||
? const OnboardingPage(nextPage: InitializingPage())
|
? const OnboardingPage(nextPage: InitializingPage())
|
||||||
: const RootPage(),
|
: const RootPage(),
|
||||||
builder: (BuildContext context, Widget? widget) {
|
builder: (final BuildContext context, final Widget? widget) {
|
||||||
Widget error = const Text('...rendering error...');
|
Widget error = const Text('...rendering error...');
|
||||||
if (widget is Scaffold || widget is Navigator) {
|
if (widget is Scaffold || widget is Navigator) {
|
||||||
error = Scaffold(body: Center(child: error));
|
error = Scaffold(body: Center(child: error));
|
||||||
}
|
}
|
||||||
ErrorWidget.builder =
|
ErrorWidget.builder =
|
||||||
(FlutterErrorDetails errorDetails) => error;
|
(final FlutterErrorDetails errorDetails) => error;
|
||||||
return widget!;
|
return widget!;
|
||||||
},
|
},
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:material_color_utilities/palettes/core_palette.dart';
|
||||||
import 'package:system_theme/system_theme.dart';
|
import 'package:system_theme/system_theme.dart';
|
||||||
import 'package:gtk_theme_fl/gtk_theme_fl.dart';
|
import 'package:gtk_theme_fl/gtk_theme_fl.dart';
|
||||||
|
|
||||||
|
@ -10,27 +13,25 @@ abstract class AppThemeFactory {
|
||||||
AppThemeFactory._();
|
AppThemeFactory._();
|
||||||
|
|
||||||
static Future<ThemeData> create(
|
static Future<ThemeData> create(
|
||||||
{required bool isDark, required Color fallbackColor}) {
|
{required final bool isDark, required final Color fallbackColor,}) => _createAppTheme(
|
||||||
return _createAppTheme(
|
|
||||||
isDark: isDark,
|
isDark: isDark,
|
||||||
fallbackColor: fallbackColor,
|
fallbackColor: fallbackColor,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
static Future<ThemeData> _createAppTheme({
|
static Future<ThemeData> _createAppTheme({
|
||||||
bool isDark = false,
|
required final Color fallbackColor,
|
||||||
required Color fallbackColor,
|
final bool isDark = false,
|
||||||
}) async {
|
}) async {
|
||||||
ColorScheme? gtkColorsScheme;
|
ColorScheme? gtkColorsScheme;
|
||||||
var brightness = isDark ? Brightness.dark : Brightness.light;
|
final Brightness brightness = isDark ? Brightness.dark : Brightness.light;
|
||||||
|
|
||||||
final dynamicColorsScheme = await _getDynamicColors(brightness);
|
final ColorScheme? dynamicColorsScheme = await _getDynamicColors(brightness);
|
||||||
|
|
||||||
if (Platform.isLinux) {
|
if (Platform.isLinux) {
|
||||||
GtkThemeData themeData = await GtkThemeData.initialize();
|
final GtkThemeData themeData = await GtkThemeData.initialize();
|
||||||
final isGtkDark =
|
final bool isGtkDark =
|
||||||
Color(themeData.theme_bg_color).computeLuminance() < 0.5;
|
Color(themeData.theme_bg_color).computeLuminance() < 0.5;
|
||||||
final isInverseNeeded = isGtkDark != isDark;
|
final bool isInverseNeeded = isGtkDark != isDark;
|
||||||
gtkColorsScheme = ColorScheme.fromSeed(
|
gtkColorsScheme = ColorScheme.fromSeed(
|
||||||
seedColor: Color(themeData.theme_selected_bg_color),
|
seedColor: Color(themeData.theme_selected_bg_color),
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
|
@ -39,7 +40,7 @@ abstract class AppThemeFactory {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final accentColor = SystemAccentColor(fallbackColor);
|
final SystemAccentColor accentColor = SystemAccentColor(fallbackColor);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await accentColor.load();
|
await accentColor.load();
|
||||||
|
@ -47,17 +48,17 @@ abstract class AppThemeFactory {
|
||||||
print('_createAppTheme: ${e.message}');
|
print('_createAppTheme: ${e.message}');
|
||||||
}
|
}
|
||||||
|
|
||||||
final fallbackColorScheme = ColorScheme.fromSeed(
|
final ColorScheme fallbackColorScheme = ColorScheme.fromSeed(
|
||||||
seedColor: accentColor.accent,
|
seedColor: accentColor.accent,
|
||||||
brightness: brightness,
|
brightness: brightness,
|
||||||
);
|
);
|
||||||
|
|
||||||
final colorScheme =
|
final ColorScheme colorScheme =
|
||||||
dynamicColorsScheme ?? gtkColorsScheme ?? fallbackColorScheme;
|
dynamicColorsScheme ?? gtkColorsScheme ?? fallbackColorScheme;
|
||||||
|
|
||||||
final appTypography = Typography.material2021();
|
final Typography appTypography = Typography.material2021();
|
||||||
|
|
||||||
final materialThemeData = ThemeData(
|
final ThemeData materialThemeData = ThemeData(
|
||||||
colorScheme: colorScheme,
|
colorScheme: colorScheme,
|
||||||
brightness: colorScheme.brightness,
|
brightness: colorScheme.brightness,
|
||||||
typography: appTypography,
|
typography: appTypography,
|
||||||
|
@ -73,10 +74,10 @@ abstract class AppThemeFactory {
|
||||||
return materialThemeData;
|
return materialThemeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<ColorScheme?> _getDynamicColors(Brightness brightness) {
|
static Future<ColorScheme?> _getDynamicColors(final Brightness brightness) {
|
||||||
try {
|
try {
|
||||||
return DynamicColorPlugin.getCorePalette().then(
|
return DynamicColorPlugin.getCorePalette().then(
|
||||||
(corePallet) => corePallet?.toColorScheme(brightness: brightness));
|
(final CorePalette? corePallet) => corePallet?.toColorScheme(brightness: brightness),);
|
||||||
} on PlatformException {
|
} on PlatformException {
|
||||||
return Future.value(null);
|
return Future.value(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,19 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class ActionButton extends StatelessWidget {
|
class ActionButton extends StatelessWidget {
|
||||||
const ActionButton({
|
const ActionButton({
|
||||||
Key? key,
|
final super.key,
|
||||||
this.text,
|
this.text,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.isRed = false,
|
this.isRed = false,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String? text;
|
final String? text;
|
||||||
final bool isRed;
|
final bool isRed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
var navigator = Navigator.of(context);
|
final NavigatorState navigator = Navigator.of(context);
|
||||||
|
|
||||||
return TextButton(
|
return TextButton(
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
|
@ -2,14 +2,12 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BrandAlert extends AlertDialog {
|
class BrandAlert extends AlertDialog {
|
||||||
BrandAlert({
|
BrandAlert({
|
||||||
Key? key,
|
final super.key,
|
||||||
String? title,
|
final String? title,
|
||||||
String? contentText,
|
final String? contentText,
|
||||||
List<Widget>? actions,
|
final super.actions,
|
||||||
}) : super(
|
}) : super(
|
||||||
key: key,
|
|
||||||
title: title != null ? Text(title) : null,
|
title: title != null ? Text(title) : null,
|
||||||
content: title != null ? Text(contentText!) : null,
|
content: title != null ? Text(contentText!) : null,
|
||||||
actions: actions,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class BrandBottomSheet extends StatelessWidget {
|
class BrandBottomSheet extends StatelessWidget {
|
||||||
const BrandBottomSheet({
|
const BrandBottomSheet({
|
||||||
Key? key,
|
|
||||||
required this.child,
|
required this.child,
|
||||||
|
final super.key,
|
||||||
this.isExpended = false,
|
this.isExpended = false,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final bool isExpended;
|
final bool isExpended;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
var mainHeight = MediaQuery.of(context).size.height -
|
final double mainHeight = MediaQuery.of(context).size.height -
|
||||||
MediaQuery.of(context).padding.top -
|
MediaQuery.of(context).padding.top -
|
||||||
100;
|
100;
|
||||||
late Widget innerWidget;
|
late Widget innerWidget;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_button/filled_button.dart';
|
import 'package:selfprivacy/ui/components/brand_button/filled_button.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
@ -5,11 +7,11 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
enum BrandButtonTypes { rised, text, iconText }
|
enum BrandButtonTypes { rised, text, iconText }
|
||||||
|
|
||||||
class BrandButton {
|
class BrandButton {
|
||||||
static rised({
|
static ConstrainedBox rised({
|
||||||
Key? key,
|
required final VoidCallback? onPressed,
|
||||||
required VoidCallback? onPressed,
|
final Key? key,
|
||||||
String? text,
|
final String? text,
|
||||||
Widget? child,
|
final Widget? child,
|
||||||
}) {
|
}) {
|
||||||
assert(text == null || child == null, 'required title or child');
|
assert(text == null || child == null, 'required title or child');
|
||||||
assert(text != null || child != null, 'required title or child');
|
assert(text != null || child != null, 'required title or child');
|
||||||
|
@ -27,10 +29,10 @@ class BrandButton {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static text({
|
static ConstrainedBox text({
|
||||||
Key? key,
|
required final VoidCallback onPressed,
|
||||||
required VoidCallback onPressed,
|
required final String title,
|
||||||
required String title,
|
final Key? key,
|
||||||
}) =>
|
}) =>
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
|
@ -40,11 +42,11 @@ class BrandButton {
|
||||||
child: TextButton(onPressed: onPressed, child: Text(title)),
|
child: TextButton(onPressed: onPressed, child: Text(title)),
|
||||||
);
|
);
|
||||||
|
|
||||||
static emptyWithIconText({
|
static _IconTextButton emptyWithIconText({
|
||||||
Key? key,
|
required final VoidCallback onPressed,
|
||||||
required VoidCallback onPressed,
|
required final String title,
|
||||||
required String title,
|
required final Icon icon,
|
||||||
required Icon icon,
|
final Key? key,
|
||||||
}) =>
|
}) =>
|
||||||
_IconTextButton(
|
_IconTextButton(
|
||||||
key: key,
|
key: key,
|
||||||
|
@ -55,16 +57,14 @@ class BrandButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IconTextButton extends StatelessWidget {
|
class _IconTextButton extends StatelessWidget {
|
||||||
const _IconTextButton({Key? key, this.onPressed, this.title, this.icon})
|
const _IconTextButton({final super.key, this.onPressed, this.title, this.icon});
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String? title;
|
final String? title;
|
||||||
final Icon? icon;
|
final Icon? icon;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Material(
|
||||||
return Material(
|
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onPressed,
|
onTap: onPressed,
|
||||||
|
@ -85,5 +85,4 @@ class _IconTextButton extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,12 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class FilledButton extends StatelessWidget {
|
class FilledButton extends StatelessWidget {
|
||||||
const FilledButton({
|
const FilledButton({
|
||||||
Key? key,
|
final super.key,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
this.title,
|
this.title,
|
||||||
this.child,
|
this.child,
|
||||||
this.disabled = false,
|
this.disabled = false,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final VoidCallback? onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String? title;
|
final String? title;
|
||||||
|
@ -15,7 +15,7 @@ class FilledButton extends StatelessWidget {
|
||||||
final bool disabled;
|
final bool disabled;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
final ButtonStyle enabledStyle = ElevatedButton.styleFrom(
|
final ButtonStyle enabledStyle = ElevatedButton.styleFrom(
|
||||||
onPrimary: Theme.of(context).colorScheme.onPrimary,
|
onPrimary: Theme.of(context).colorScheme.onPrimary,
|
||||||
primary: Theme.of(context).colorScheme.primary,
|
primary: Theme.of(context).colorScheme.primary,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BrandCards {
|
class BrandCards {
|
||||||
static Widget big({required Widget child}) => _BrandCard(
|
static Widget big({required final Widget child}) => _BrandCard(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 20,
|
horizontal: 20,
|
||||||
vertical: 15,
|
vertical: 15,
|
||||||
|
@ -10,7 +12,7 @@ class BrandCards {
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
static Widget small({required Widget child}) => _BrandCard(
|
static Widget small({required final Widget child}) => _BrandCard(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 15,
|
horizontal: 15,
|
||||||
vertical: 10,
|
vertical: 10,
|
||||||
|
@ -19,10 +21,11 @@ class BrandCards {
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
static Widget outlined({required Widget child}) => _OutlinedCard(
|
static Widget outlined({required final Widget child}) => _OutlinedCard(
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
static Widget filled({required Widget child, bool tertiary = false}) =>
|
static Widget filled(
|
||||||
|
{required final Widget child, final bool tertiary = false,}) =>
|
||||||
_FilledCard(
|
_FilledCard(
|
||||||
tertiary: tertiary,
|
tertiary: tertiary,
|
||||||
child: child,
|
child: child,
|
||||||
|
@ -31,12 +34,12 @@ class BrandCards {
|
||||||
|
|
||||||
class _BrandCard extends StatelessWidget {
|
class _BrandCard extends StatelessWidget {
|
||||||
const _BrandCard({
|
const _BrandCard({
|
||||||
Key? key,
|
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.padding,
|
required this.padding,
|
||||||
required this.shadow,
|
required this.shadow,
|
||||||
required this.borderRadius,
|
required this.borderRadius,
|
||||||
}) : super(key: key);
|
final super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
@ -44,65 +47,62 @@ class _BrandCard extends StatelessWidget {
|
||||||
final BorderRadius borderRadius;
|
final BorderRadius borderRadius;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Container(
|
||||||
return Container(
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: Theme.of(context).colorScheme.surface,
|
||||||
color: Theme.of(context).colorScheme.surface,
|
borderRadius: borderRadius,
|
||||||
borderRadius: borderRadius,
|
boxShadow: shadow,
|
||||||
boxShadow: shadow,
|
),
|
||||||
),
|
padding: padding,
|
||||||
padding: padding,
|
child: child,
|
||||||
child: child,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OutlinedCard extends StatelessWidget {
|
class _OutlinedCard extends StatelessWidget {
|
||||||
const _OutlinedCard({
|
const _OutlinedCard({
|
||||||
Key? key,
|
final super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Card(
|
||||||
return Card(
|
elevation: 0.0,
|
||||||
elevation: 0.0,
|
shape: RoundedRectangleBorder(
|
||||||
shape: RoundedRectangleBorder(
|
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
side: BorderSide(
|
||||||
side: BorderSide(
|
color: Theme.of(context).colorScheme.outline,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
),
|
||||||
),
|
),
|
||||||
),
|
clipBehavior: Clip.antiAlias,
|
||||||
clipBehavior: Clip.antiAlias,
|
child: child,
|
||||||
child: child,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FilledCard extends StatelessWidget {
|
class _FilledCard extends StatelessWidget {
|
||||||
const _FilledCard({Key? key, required this.child, required this.tertiary})
|
const _FilledCard({
|
||||||
: super(key: key);
|
required this.child,
|
||||||
|
required this.tertiary,
|
||||||
|
final super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final bool tertiary;
|
final bool tertiary;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Card(
|
||||||
return Card(
|
elevation: 0.0,
|
||||||
elevation: 0.0,
|
shape: const RoundedRectangleBorder(
|
||||||
shape: const RoundedRectangleBorder(
|
borderRadius: BorderRadius.all(Radius.circular(12)),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(12)),
|
),
|
||||||
),
|
clipBehavior: Clip.antiAlias,
|
||||||
clipBehavior: Clip.antiAlias,
|
color: tertiary
|
||||||
color: tertiary
|
? Theme.of(context).colorScheme.tertiaryContainer
|
||||||
? Theme.of(context).colorScheme.tertiaryContainer
|
: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
: Theme.of(context).colorScheme.surfaceVariant,
|
child: child,
|
||||||
child: child,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final bigShadow = [
|
final List<BoxShadow> bigShadow = [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
|
|
|
@ -2,14 +2,12 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/config/brand_colors.dart';
|
import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class BrandDivider extends StatelessWidget {
|
class BrandDivider extends StatelessWidget {
|
||||||
const BrandDivider({Key? key}) : super(key: key);
|
const BrandDivider({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Container(
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 1,
|
height: 1,
|
||||||
color: BrandColors.dividerColor,
|
color: BrandColors.dividerColor,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
|
// ignore_for_file: always_specify_types
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
|
||||||
class BrandHeader extends StatelessWidget {
|
class BrandHeader extends StatelessWidget {
|
||||||
const BrandHeader({
|
const BrandHeader({
|
||||||
Key? key,
|
final super.key,
|
||||||
this.title = '',
|
this.title = '',
|
||||||
this.hasBackButton = false,
|
this.hasBackButton = false,
|
||||||
this.onBackButtonPressed,
|
this.onBackButtonPressed,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
final bool hasBackButton;
|
final bool hasBackButton;
|
||||||
final VoidCallback? onBackButtonPressed;
|
final VoidCallback? onBackButtonPressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Container(
|
||||||
return Container(
|
|
||||||
height: 52,
|
height: 52,
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
|
@ -37,5 +38,4 @@ class BrandHeader extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,16 @@ import 'package:selfprivacy/ui/components/pre_styled_buttons/flash_fab.dart';
|
||||||
|
|
||||||
class BrandHeroScreen extends StatelessWidget {
|
class BrandHeroScreen extends StatelessWidget {
|
||||||
const BrandHeroScreen({
|
const BrandHeroScreen({
|
||||||
Key? key,
|
required this.children,
|
||||||
|
final super.key,
|
||||||
this.headerTitle = '',
|
this.headerTitle = '',
|
||||||
this.hasBackButton = true,
|
this.hasBackButton = true,
|
||||||
this.hasFlashButton = true,
|
this.hasFlashButton = true,
|
||||||
required this.children,
|
|
||||||
this.heroIcon,
|
this.heroIcon,
|
||||||
this.heroTitle,
|
this.heroTitle,
|
||||||
this.heroSubtitle,
|
this.heroSubtitle,
|
||||||
this.onBackButtonPressed,
|
this.onBackButtonPressed,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
final String headerTitle;
|
final String headerTitle;
|
||||||
|
@ -25,8 +25,7 @@ class BrandHeroScreen extends StatelessWidget {
|
||||||
final VoidCallback? onBackButtonPressed;
|
final VoidCallback? onBackButtonPressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => SafeArea(
|
||||||
return SafeArea(
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: PreferredSize(
|
appBar: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(52.0),
|
preferredSize: const Size.fromHeight(52.0),
|
||||||
|
@ -63,12 +62,11 @@ class BrandHeroScreen extends StatelessWidget {
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: Theme.of(context).colorScheme.onBackground,
|
color: Theme.of(context).colorScheme.onBackground,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.start),
|
textAlign: TextAlign.start,),
|
||||||
const SizedBox(height: 16.0),
|
const SizedBox(height: 16.0),
|
||||||
...children,
|
...children,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import 'package:flutter/widgets.dart';
|
||||||
class BrandIcons {
|
class BrandIcons {
|
||||||
BrandIcons._();
|
BrandIcons._();
|
||||||
|
|
||||||
static const _kFontFam = 'BrandIcons';
|
static const String _kFontFam = 'BrandIcons';
|
||||||
static const String? _kFontPkg = null;
|
static const String? _kFontPkg = null;
|
||||||
|
|
||||||
static const IconData connection =
|
static const IconData connection =
|
||||||
|
|
|
@ -2,13 +2,12 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
|
||||||
class BrandLoader {
|
class BrandLoader {
|
||||||
static horizontal() => _HorizontalLoader();
|
static _HorizontalLoader horizontal() => _HorizontalLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HorizontalLoader extends StatelessWidget {
|
class _HorizontalLoader extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Column(
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text('basis.wait'.tr()),
|
Text('basis.wait'.tr()),
|
||||||
|
@ -16,5 +15,4 @@ class _HorizontalLoader extends StatelessWidget {
|
||||||
const LinearProgressIndicator(minHeight: 3),
|
const LinearProgressIndicator(minHeight: 3),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class BrandMarkdown extends StatefulWidget {
|
class BrandMarkdown extends StatefulWidget {
|
||||||
const BrandMarkdown({
|
const BrandMarkdown({
|
||||||
Key? key,
|
|
||||||
required this.fileName,
|
required this.fileName,
|
||||||
}) : super(key: key);
|
final super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final String fileName;
|
final String fileName;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class _BrandMarkdownState extends State<BrandMarkdown> {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _loadMdFile() async {
|
void _loadMdFile() async {
|
||||||
String mdFromFile = await rootBundle
|
final String mdFromFile = await rootBundle
|
||||||
.loadString('assets/markdown/${widget.fileName}-${'locale'.tr()}.md');
|
.loadString('assets/markdown/${widget.fileName}-${'locale'.tr()}.md');
|
||||||
setState(() {
|
setState(() {
|
||||||
_mdContent = mdFromFile;
|
_mdContent = mdFromFile;
|
||||||
|
@ -36,9 +36,9 @@ class _BrandMarkdownState extends State<BrandMarkdown> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
var isDark = Theme.of(context).brightness == Brightness.dark;
|
final bool isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
var markdown = MarkdownStyleSheet(
|
final MarkdownStyleSheet markdown = MarkdownStyleSheet(
|
||||||
p: defaultTextStyle.copyWith(
|
p: defaultTextStyle.copyWith(
|
||||||
color: isDark ? BrandColors.white : null,
|
color: isDark ? BrandColors.white : null,
|
||||||
),
|
),
|
||||||
|
@ -58,9 +58,9 @@ class _BrandMarkdownState extends State<BrandMarkdown> {
|
||||||
return Markdown(
|
return Markdown(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
styleSheet: markdown,
|
styleSheet: markdown,
|
||||||
onTapLink: (String text, String? href, String title) {
|
onTapLink: (final String text, final String? href, final String title) {
|
||||||
if (href != null) {
|
if (href != null) {
|
||||||
canLaunchUrlString(href).then((canLaunchURL) {
|
canLaunchUrlString(href).then((final bool canLaunchURL) {
|
||||||
if (canLaunchURL) {
|
if (canLaunchURL) {
|
||||||
launchUrlString(href);
|
launchUrlString(href);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,14 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class BrandRadio extends StatelessWidget {
|
class BrandRadio extends StatelessWidget {
|
||||||
const BrandRadio({
|
const BrandRadio({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.isChecked,
|
required this.isChecked,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final bool isChecked;
|
final bool isChecked;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => Container(
|
||||||
return Container(
|
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 20,
|
width: 20,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
|
@ -31,12 +30,9 @@ class BrandRadio extends StatelessWidget {
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
BoxBorder? _getBorder() {
|
BoxBorder? _getBorder() => Border.all(
|
||||||
return Border.all(
|
|
||||||
color: isChecked ? BrandColors.primary : BrandColors.gray1,
|
color: isChecked ? BrandColors.primary : BrandColors.gray1,
|
||||||
width: 2,
|
width: 2,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,11 @@ import 'package:selfprivacy/ui/components/brand_text/brand_text.dart';
|
||||||
|
|
||||||
class BrandRadioTile extends StatelessWidget {
|
class BrandRadioTile extends StatelessWidget {
|
||||||
const BrandRadioTile({
|
const BrandRadioTile({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.isChecked,
|
required this.isChecked,
|
||||||
required this.text,
|
required this.text,
|
||||||
required this.onPress,
|
required this.onPress,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final bool isChecked;
|
final bool isChecked;
|
||||||
|
|
||||||
|
@ -16,8 +16,7 @@ class BrandRadioTile extends StatelessWidget {
|
||||||
final VoidCallback onPress;
|
final VoidCallback onPress;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => GestureDetector(
|
||||||
return GestureDetector(
|
|
||||||
onTap: onPress,
|
onTap: onPress,
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -33,5 +32,4 @@ class BrandRadioTile extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,19 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class BrandSpanButton extends TextSpan {
|
class BrandSpanButton extends TextSpan {
|
||||||
BrandSpanButton({
|
BrandSpanButton({
|
||||||
required String text,
|
required final String text,
|
||||||
required VoidCallback onTap,
|
required final VoidCallback onTap,
|
||||||
TextStyle? style,
|
final TextStyle? style,
|
||||||
}) : super(
|
}) : super(
|
||||||
recognizer: TapGestureRecognizer()..onTap = onTap,
|
recognizer: TapGestureRecognizer()..onTap = onTap,
|
||||||
text: text,
|
text: text,
|
||||||
style: (style ?? const TextStyle()).copyWith(color: BrandColors.blue),
|
style: (style ?? const TextStyle()).copyWith(color: BrandColors.blue),
|
||||||
);
|
);
|
||||||
|
|
||||||
static link({
|
static BrandSpanButton link({
|
||||||
required String text,
|
required final String text,
|
||||||
String? urlString,
|
final String? urlString,
|
||||||
TextStyle? style,
|
final TextStyle? style,
|
||||||
}) =>
|
}) =>
|
||||||
BrandSpanButton(
|
BrandSpanButton(
|
||||||
text: text,
|
text: text,
|
||||||
|
@ -25,7 +25,7 @@ class BrandSpanButton extends TextSpan {
|
||||||
onTap: () => _launchURL(urlString ?? text),
|
onTap: () => _launchURL(urlString ?? text),
|
||||||
);
|
);
|
||||||
|
|
||||||
static _launchURL(String link) async {
|
static _launchURL(final String link) async {
|
||||||
if (await canLaunchUrl(Uri.parse(link))) {
|
if (await canLaunchUrl(Uri.parse(link))) {
|
||||||
await launchUrl(Uri.parse(link));
|
await launchUrl(Uri.parse(link));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2,20 +2,18 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BrandSwitch extends StatelessWidget {
|
class BrandSwitch extends StatelessWidget {
|
||||||
const BrandSwitch({
|
const BrandSwitch({
|
||||||
Key? key,
|
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
required this.value,
|
required this.value,
|
||||||
}) : super(key: key);
|
final super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final ValueChanged<bool> onChanged;
|
final ValueChanged<bool> onChanged;
|
||||||
final bool value;
|
final bool value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) => Switch(
|
||||||
return Switch(
|
|
||||||
activeColor: Theme.of(context).colorScheme.primary,
|
activeColor: Theme.of(context).colorScheme.primary,
|
||||||
value: value,
|
value: value,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart';
|
||||||
|
|
||||||
class BrandTabBar extends StatefulWidget {
|
class BrandTabBar extends StatefulWidget {
|
||||||
const BrandTabBar({Key? key, this.controller}) : super(key: key);
|
const BrandTabBar({final super.key, this.controller});
|
||||||
|
|
||||||
final TabController? controller;
|
final TabController? controller;
|
||||||
@override
|
@override
|
||||||
|
@ -34,26 +34,22 @@ class _BrandTabBarState extends State<BrandTabBar> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => NavigationBar(
|
||||||
return NavigationBar(
|
|
||||||
destinations: [
|
destinations: [
|
||||||
_getIconButton('basis.providers'.tr(), BrandIcons.server, 0),
|
_getIconButton('basis.providers'.tr(), BrandIcons.server, 0),
|
||||||
_getIconButton('basis.services'.tr(), BrandIcons.box, 1),
|
_getIconButton('basis.services'.tr(), BrandIcons.box, 1),
|
||||||
_getIconButton('basis.users'.tr(), BrandIcons.users, 2),
|
_getIconButton('basis.users'.tr(), BrandIcons.users, 2),
|
||||||
_getIconButton('basis.more'.tr(), Icons.menu_rounded, 3),
|
_getIconButton('basis.more'.tr(), Icons.menu_rounded, 3),
|
||||||
],
|
],
|
||||||
onDestinationSelected: (index) {
|
onDestinationSelected: (final index) {
|
||||||
widget.controller!.animateTo(index);
|
widget.controller!.animateTo(index);
|
||||||
},
|
},
|
||||||
selectedIndex: currentIndex ?? 0,
|
selectedIndex: currentIndex ?? 0,
|
||||||
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
|
labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
_getIconButton(String label, IconData iconData, int index) {
|
NavigationDestination _getIconButton(final String label, final IconData iconData, final int index) => NavigationDestination(
|
||||||
return NavigationDestination(
|
|
||||||
icon: Icon(iconData),
|
icon: Icon(iconData),
|
||||||
label: label,
|
label: label,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,66 +18,10 @@ enum TextType {
|
||||||
}
|
}
|
||||||
|
|
||||||
class BrandText extends StatelessWidget {
|
class BrandText extends StatelessWidget {
|
||||||
const BrandText(
|
|
||||||
this.text, {
|
|
||||||
Key? key,
|
|
||||||
this.style,
|
|
||||||
required this.type,
|
|
||||||
this.overflow,
|
|
||||||
this.softWrap,
|
|
||||||
this.textAlign,
|
|
||||||
this.maxLines,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
final String? text;
|
|
||||||
final TextStyle? style;
|
|
||||||
final TextType type;
|
|
||||||
final TextOverflow? overflow;
|
|
||||||
final bool? softWrap;
|
|
||||||
final TextAlign? textAlign;
|
|
||||||
final int? maxLines;
|
|
||||||
|
|
||||||
factory BrandText.h1(
|
|
||||||
String? text, {
|
|
||||||
TextStyle? style,
|
|
||||||
TextOverflow? overflow,
|
|
||||||
bool? softWrap,
|
|
||||||
}) =>
|
|
||||||
BrandText(
|
|
||||||
text,
|
|
||||||
type: TextType.h1,
|
|
||||||
style: style,
|
|
||||||
);
|
|
||||||
|
|
||||||
factory BrandText.onboardingTitle(String text, {TextStyle? style}) =>
|
|
||||||
BrandText(
|
|
||||||
text,
|
|
||||||
type: TextType.onboardingTitle,
|
|
||||||
style: style,
|
|
||||||
);
|
|
||||||
factory BrandText.h2(
|
|
||||||
String? text, {
|
|
||||||
TextStyle? style,
|
|
||||||
TextAlign? textAlign,
|
|
||||||
}) =>
|
|
||||||
BrandText(
|
|
||||||
text,
|
|
||||||
type: TextType.h2,
|
|
||||||
style: style,
|
|
||||||
textAlign: textAlign,
|
|
||||||
);
|
|
||||||
factory BrandText.h3(String text, {TextStyle? style, TextAlign? textAlign}) =>
|
|
||||||
BrandText(
|
|
||||||
text,
|
|
||||||
type: TextType.h3,
|
|
||||||
style: style,
|
|
||||||
textAlign: textAlign,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
);
|
|
||||||
factory BrandText.h4(
|
factory BrandText.h4(
|
||||||
String? text, {
|
final String? text, {
|
||||||
TextStyle? style,
|
final TextStyle? style,
|
||||||
TextAlign? textAlign,
|
final TextAlign? textAlign,
|
||||||
}) =>
|
}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
|
@ -89,10 +33,25 @@ class BrandText extends StatelessWidget {
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
factory BrandText.onboardingTitle(final String text, {final TextStyle? style}) =>
|
||||||
|
BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.onboardingTitle,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
factory BrandText.h3(final String text, {final TextStyle? style, final TextAlign? textAlign}) =>
|
||||||
|
BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.h3,
|
||||||
|
style: style,
|
||||||
|
textAlign: textAlign,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
);
|
||||||
|
|
||||||
factory BrandText.h4Underlined(
|
factory BrandText.h4Underlined(
|
||||||
String? text, {
|
final String? text, {
|
||||||
TextStyle? style,
|
final TextStyle? style,
|
||||||
TextAlign? textAlign,
|
final TextAlign? textAlign,
|
||||||
}) =>
|
}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
|
@ -104,10 +63,54 @@ class BrandText extends StatelessWidget {
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
factory BrandText.h1(
|
||||||
|
final String? text, {
|
||||||
|
final TextStyle? style,
|
||||||
|
final TextOverflow? overflow,
|
||||||
|
final bool? softWrap,
|
||||||
|
}) =>
|
||||||
|
BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.h1,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
factory BrandText.h2(
|
||||||
|
final String? text, {
|
||||||
|
final TextStyle? style,
|
||||||
|
final TextAlign? textAlign,
|
||||||
|
}) =>
|
||||||
|
BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.h2,
|
||||||
|
style: style,
|
||||||
|
textAlign: textAlign,
|
||||||
|
);
|
||||||
|
factory BrandText.body1(final String? text, {final TextStyle? style}) => BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.body1,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
factory BrandText.small(final String text, {final TextStyle? style}) => BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.small,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
factory BrandText.body2(final String? text, {final TextStyle? style}) => BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.body2,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
factory BrandText.buttonTitleText(final String? text, {final TextStyle? style}) =>
|
||||||
|
BrandText(
|
||||||
|
text,
|
||||||
|
type: TextType.buttonTitleText,
|
||||||
|
style: style,
|
||||||
|
);
|
||||||
|
|
||||||
factory BrandText.h5(
|
factory BrandText.h5(
|
||||||
String? text, {
|
final String? text, {
|
||||||
TextStyle? style,
|
final TextStyle? style,
|
||||||
TextAlign? textAlign,
|
final TextAlign? textAlign,
|
||||||
}) =>
|
}) =>
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
|
@ -115,39 +118,36 @@ class BrandText extends StatelessWidget {
|
||||||
style: style,
|
style: style,
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
);
|
);
|
||||||
factory BrandText.body1(String? text, {TextStyle? style}) => BrandText(
|
factory BrandText.medium(final String? text,
|
||||||
text,
|
{final TextStyle? style, final TextAlign? textAlign}) =>
|
||||||
type: TextType.body1,
|
|
||||||
style: style,
|
|
||||||
);
|
|
||||||
factory BrandText.body2(String? text, {TextStyle? style}) => BrandText(
|
|
||||||
text,
|
|
||||||
type: TextType.body2,
|
|
||||||
style: style,
|
|
||||||
);
|
|
||||||
factory BrandText.medium(String? text,
|
|
||||||
{TextStyle? style, TextAlign? textAlign}) =>
|
|
||||||
BrandText(
|
BrandText(
|
||||||
text,
|
text,
|
||||||
type: TextType.medium,
|
type: TextType.medium,
|
||||||
style: style,
|
style: style,
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
);
|
);
|
||||||
factory BrandText.small(String text, {TextStyle? style}) => BrandText(
|
const BrandText(
|
||||||
text,
|
this.text, {
|
||||||
type: TextType.small,
|
super.key,
|
||||||
style: style,
|
this.style,
|
||||||
);
|
required this.type,
|
||||||
factory BrandText.buttonTitleText(String? text, {TextStyle? style}) =>
|
this.overflow,
|
||||||
BrandText(
|
this.softWrap,
|
||||||
text,
|
this.textAlign,
|
||||||
type: TextType.buttonTitleText,
|
this.maxLines,
|
||||||
style: style,
|
});
|
||||||
);
|
|
||||||
|
final String? text;
|
||||||
|
final TextStyle? style;
|
||||||
|
final TextType type;
|
||||||
|
final TextOverflow? overflow;
|
||||||
|
final bool? softWrap;
|
||||||
|
final TextAlign? textAlign;
|
||||||
|
final int? maxLines;
|
||||||
@override
|
@override
|
||||||
Text build(BuildContext context) {
|
Text build(final BuildContext context) {
|
||||||
TextStyle style;
|
TextStyle style;
|
||||||
var isDark = Theme.of(context).brightness == Brightness.dark;
|
final bool isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case TextType.h1:
|
case TextType.h1:
|
||||||
style = isDark
|
style = isDark
|
||||||
|
|
|
@ -7,10 +7,10 @@ import 'package:selfprivacy/utils/named_font_weight.dart';
|
||||||
|
|
||||||
class BrandTimer extends StatefulWidget {
|
class BrandTimer extends StatefulWidget {
|
||||||
const BrandTimer({
|
const BrandTimer({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.startDateTime,
|
required this.startDateTime,
|
||||||
required this.duration,
|
required this.duration,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
final DateTime startDateTime;
|
final DateTime startDateTime;
|
||||||
final Duration duration;
|
final Duration duration;
|
||||||
|
@ -31,8 +31,8 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
|
|
||||||
_timerStart() {
|
_timerStart() {
|
||||||
_timeString = differenceFromStart;
|
_timeString = differenceFromStart;
|
||||||
timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
|
timer = Timer.periodic(const Duration(seconds: 1), (final Timer t) {
|
||||||
var timePassed = DateTime.now().difference(widget.startDateTime);
|
final Duration timePassed = DateTime.now().difference(widget.startDateTime);
|
||||||
if (timePassed > widget.duration) {
|
if (timePassed > widget.duration) {
|
||||||
t.cancel();
|
t.cancel();
|
||||||
} else {
|
} else {
|
||||||
|
@ -42,7 +42,7 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(BrandTimer oldWidget) {
|
void didUpdateWidget(final BrandTimer oldWidget) {
|
||||||
if (timer.isActive) {
|
if (timer.isActive) {
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
}
|
}
|
||||||
|
@ -51,14 +51,12 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => BrandText.medium(
|
||||||
return BrandText.medium(
|
|
||||||
_timeString,
|
_timeString,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: NamedFontWeight.demiBold,
|
fontWeight: NamedFontWeight.demiBold,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
void _getTime() {
|
void _getTime() {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -69,10 +67,10 @@ class _BrandTimerState extends State<BrandTimer> {
|
||||||
String get differenceFromStart =>
|
String get differenceFromStart =>
|
||||||
_durationToString(DateTime.now().difference(widget.startDateTime));
|
_durationToString(DateTime.now().difference(widget.startDateTime));
|
||||||
|
|
||||||
String _durationToString(Duration duration) {
|
String _durationToString(final Duration duration) {
|
||||||
var timeLeft = widget.duration - duration;
|
final Duration timeLeft = widget.duration - duration;
|
||||||
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
String twoDigits(final int n) => n.toString().padLeft(2, '0');
|
||||||
String twoDigitSeconds = twoDigits(timeLeft.inSeconds);
|
final String twoDigitSeconds = twoDigits(timeLeft.inSeconds);
|
||||||
|
|
||||||
return 'timer.sec'.tr(args: [twoDigitSeconds]);
|
return 'timer.sec'.tr(args: [twoDigitSeconds]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,19 +3,19 @@ import 'package:selfprivacy/config/brand_colors.dart';
|
||||||
|
|
||||||
class DotsIndicator extends StatelessWidget {
|
class DotsIndicator extends StatelessWidget {
|
||||||
const DotsIndicator({
|
const DotsIndicator({
|
||||||
Key? key,
|
|
||||||
required this.activeIndex,
|
required this.activeIndex,
|
||||||
required this.count,
|
required this.count,
|
||||||
}) : super(key: key);
|
final super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final int activeIndex;
|
final int activeIndex;
|
||||||
final int count;
|
final int count;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
var dots = List.generate(
|
final List<Container> dots = List.generate(
|
||||||
count,
|
count,
|
||||||
(index) => Container(
|
(final index) => Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 10),
|
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 10),
|
||||||
height: 10,
|
height: 10,
|
||||||
width: 10,
|
width: 10,
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class BrandError extends StatelessWidget {
|
class BrandError extends StatelessWidget {
|
||||||
const BrandError({Key? key, this.error, this.stackTrace}) : super(key: key);
|
const BrandError({final super.key, this.error, this.stackTrace});
|
||||||
|
|
||||||
final Object? error;
|
final Object? error;
|
||||||
final StackTrace? stackTrace;
|
final StackTrace? stackTrace;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) => SafeArea(
|
||||||
return SafeArea(
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: Center(
|
body: Center(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
|
@ -24,5 +23,4 @@ class BrandError extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,16 @@ import 'package:selfprivacy/logic/models/state_types.dart';
|
||||||
|
|
||||||
class IconStatusMask extends StatelessWidget {
|
class IconStatusMask extends StatelessWidget {
|
||||||
const IconStatusMask({
|
const IconStatusMask({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.child,
|
required this.child,
|
||||||
required this.status,
|
required this.status,
|
||||||
}) : super(key: key);
|
});
|
||||||
final Icon child;
|
final Icon child;
|
||||||
|
|
||||||
final StateType status;
|
final StateType status;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(final BuildContext context) {
|
||||||
late List<Color> colors;
|
late List<Color> colors;
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case StateType.uninitialized:
|
case StateType.uninitialized:
|
||||||
|
@ -30,7 +30,7 @@ class IconStatusMask extends StatelessWidget {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return ShaderMask(
|
return ShaderMask(
|
||||||
shaderCallback: (bounds) => LinearGradient(
|
shaderCallback: (final bounds) => LinearGradient(
|
||||||
begin: const Alignment(-1, -0.8),
|
begin: const Alignment(-1, -0.8),
|
||||||
end: const Alignment(0.9, 0.9),
|
end: const Alignment(0.9, 0.9),
|
||||||
colors: colors,
|
colors: colors,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue