From 1202e4ad539c5f085b78fee67ab6371dc4f62633 Mon Sep 17 00:00:00 2001 From: Kherel Date: Wed, 18 Aug 2021 11:36:40 +0200 Subject: [PATCH 1/6] change password generator --- lib/logic/api_maps/hetzner.dart | 14 +- .../cubit/forms/user/user_form_cubit.dart | 6 +- lib/logic/cubit/users/users_cubit.dart | 18 -- lib/logic/models/jobs/job.dart | 4 +- lib/logic/models/user.dart | 6 +- lib/utils/password_generator.dart | 229 ++++++++---------- lib/utils/password_generator2.dart | 14 -- pubspec.lock | 90 +++---- pubspec.yaml | 10 +- test/widget_test.dart | 134 ++++++++-- 10 files changed, 273 insertions(+), 252 deletions(-) delete mode 100644 lib/utils/password_generator2.dart diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 037db18c..0c29a040 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -7,7 +7,7 @@ import 'package:selfprivacy/logic/api_maps/api_map.dart'; import 'package:selfprivacy/logic/models/hetzner_server_info.dart'; import 'package:selfprivacy/logic/models/server_details.dart'; import 'package:selfprivacy/logic/models/user.dart'; -import 'package:selfprivacy/utils/password_generator2.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; class HetznerApi extends ApiMap { bool hasLoger; @@ -73,30 +73,26 @@ class HetznerApi extends ApiMap { required User rootUser, required String domainName, }) async { - var dbPassword = getRandomString(40); - const chars = - 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; - - var dbStorageName = getRandomString(6, chars); var client = await getClient(); Response dbCreateResponse = await client.post( '/volumes', data: { "size": 10, - "name": dbStorageName, + "name": StringGenerators.dbStorageName(), "labels": {"labelkey": "value"}, "location": "fsn1", "automount": false, "format": "ext4" }, ); + + var dbPassword = StringGenerators.dbPassword(); var dbId = dbCreateResponse.data['volume']['id']; var data = jsonDecode( - '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}''' - ); + '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''); Response serverCreateResponse = await client.post( '/servers', diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index a30b90f8..5501b38b 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -4,8 +4,8 @@ import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/models/jobs/job.dart'; import 'package:selfprivacy/logic/models/user.dart'; -import 'package:selfprivacy/utils/password_generator.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; class UserFormCubit extends FormCubit { UserFormCubit({ @@ -32,7 +32,7 @@ class UserFormCubit extends FormCubit { ); password = FieldCubit( - initalValue: isEdit ? user!.password : genPass(), + initalValue: isEdit ? user!.password : StringGenerators.userPassword(), validations: [ RequiredStringValidation('validations.required'.tr()), ValidationModel((s) => passwordRegExp.hasMatch(s), @@ -56,7 +56,7 @@ class UserFormCubit extends FormCubit { late FieldCubit password; void genNewPassword() { - password.externalSetValue(genPass()); + password.externalSetValue(StringGenerators.userPassword()); } final JobsCubit jobsCubit; diff --git a/lib/logic/cubit/users/users_cubit.dart b/lib/logic/cubit/users/users_cubit.dart index ecc0c897..5d634dc4 100644 --- a/lib/logic/cubit/users/users_cubit.dart +++ b/lib/logic/cubit/users/users_cubit.dart @@ -34,21 +34,3 @@ class UsersCubit extends Cubit { emit(UsersState(users)); } } - -// final initMockUsers = [ -// User(login: 'Heartbreaking.Goose', password: genPass()), -// User(login: 'Alma.lawson', password: genPass()), -// User(login: 'Bee.gees', password: genPass()), -// User(login: 'Bim.jennings', password: genPass()), -// User(login: 'Debra.holt', password: genPass()), -// User(login: 'Georgia.young', password: genPass()), -// User(login: 'Kenzi.lawson', password: genPass()), -// User(login: 'Le.jennings', password: genPass()), -// User(login: 'Kirill.Zh', password: genPass()), -// User(login: 'Tina.Bolton', password: genPass()), -// User(login: 'Rebekah.Lynn', password: genPass()), -// User(login: 'Aleena.Armstrong', password: genPass()), -// User(login: 'Rosemary.Williams', password: genPass()), -// User(login: 'Sullivan.Nixon', password: genPass()), -// User(login: 'Aleena.Armstrong', password: genPass()), -// ]; diff --git a/lib/logic/models/jobs/job.dart b/lib/logic/models/jobs/job.dart index 3d6bc386..2fcc8e87 100644 --- a/lib/logic/models/jobs/job.dart +++ b/lib/logic/models/jobs/job.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; -import 'package:selfprivacy/utils/password_generator2.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; import '../user.dart'; @@ -9,7 +9,7 @@ class Job extends Equatable { Job({ String? id, required this.title, - }) : id = id ?? getRandomString(5); + }) : id = id ?? StringGenerators.simpleId(); final String title; final String id; diff --git a/lib/logic/models/user.dart b/lib/logic/models/user.dart index c50d13fe..0d1c48b3 100644 --- a/lib/logic/models/user.dart +++ b/lib/logic/models/user.dart @@ -4,6 +4,7 @@ import 'package:crypt/crypt.dart'; import 'package:equatable/equatable.dart'; import 'package:selfprivacy/utils/color_utils.dart'; import 'package:hive/hive.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; part 'user.g.dart'; @@ -25,7 +26,10 @@ class User extends Equatable { Color get color => stringToColor(login); - Crypt get hashPassword => Crypt.sha512(password); + Crypt get hashPassword => Crypt.sha512( + password, + salt: StringGenerators.passwordSalt(), + ); String toString() { return login; diff --git a/lib/utils/password_generator.dart b/lib/utils/password_generator.dart index a0ce6273..f7bdd2d6 100644 --- a/lib/utils/password_generator.dart +++ b/lib/utils/password_generator.dart @@ -1,138 +1,99 @@ import 'dart:math'; -var generator = PasswordGenerator(); +Random _rnd = Random(); -String genPass() { - generator.generate(8); - return generator.getGeneratedValue(); -} - -///Generates a password. -/// -///The password [_generatedValue] is of a specified length, including letters [_letterGen] of mixed cases, -///numbers [_numGen], and symbols[_symGen] depending on user choice. -class PasswordGenerator { - late bool _letterGen; - late bool _numGen; - late bool _symGen; - late String _generatedValue; - - ///Constructor. - /// - ///[_letterGen] is true to make password generation possible from the opening of the application, and - ///[_generatedValue] is intialized to the value below so the text containing it can be first generated - ///upon users request - PasswordGenerator() { - _letterGen = true; - _numGen = true; - _symGen = false; - _generatedValue = "Press Generate"; - } - - ///Call to generate a value, of [n] length - void generate(int n) { - //Discards the old value - _generatedValue = ""; - - ///Cannot generate a value without any character types selected - if (!_letterGen && !_numGen && !_symGen) { - _generatedValue = "No character type selected"; - return; - } - - ///'Randomly' selectes caracter type to generate and append [toAppend] to [_generatedValue] - // ignore: unnecessary_statements - for (n; n > 0; n--) { - String? toAppend; - var random = new Random(); - - ///loops until a valid character is generated, meaning the user has to check the character value - ///to be generated. 'Randomly' picks a character type. - while (toAppend == null) { - int selector = random.nextInt(3); - - if (selector == 0) { - toAppend = _generateLetter(); - } else if (selector == 1) { - toAppend = _generateNumber(); - } else { - toAppend = _generateSymbol(); - } - } - - _generatedValue += toAppend; - toAppend = null; - } - } - - ///Generates a letter when called. - String _generateLetter() { - if (!_letterGen) return ''; - - ///Finds the integer value for the range between a-z and A-Z, with [base] UTF-16 value for lowercase letters and - ///[baseUpper] UTF-16 value for uppercase letters - int base = "a".codeUnitAt(0); - int baseUpper = "A".codeUnitAt(0); - int maxRand = ("z".codeUnitAt(0) - base) + 1; - Random random = new Random(); - - ///Randomly selects between upper and lower case generation, randomly generates value from [maxRand], then adding base, - ///which creates a UTF-16 encoded character to be converted into a string of one character between a-z/A-Z. - ///This string is then returned. - if (random.nextInt(2) == 0) { - return String.fromCharCodes([random.nextInt(maxRand) + base]); - } else { - return String.fromCharCodes([random.nextInt(maxRand) + baseUpper]); - } - } - - ///Generates a number when called - String? _generateNumber() { - if (!_numGen) return null; - - ///Finds the integer value for the range between 0-9 - int base = "0".codeUnitAt(0); - int maxRand = ("9".codeUnitAt(0) - base) + 1; - Random random = new Random(); - - ///Randomly generates value from [maxRand], then adding base, which creates a UTF-16 encoded character to be converted into a - ///string of one character between 0-9. This string is then returned. - return String.fromCharCodes([random.nextInt(maxRand) + base]); - } - - ///Generates a symbol when called - String? _generateSymbol() { - if (!_symGen) return null; - - ///Finds the integer value for the range between symbols !-. - - ///(note) which includes symbols !"#$%&'()*+,=. - int base = "!".codeUnitAt(0); - int maxRand = (".".codeUnitAt(0) - base) + 1; - Random random = new Random(); - - ///Randomly generates value from [maxRand], then adding base, which creates a UTF-16 encoded character to be - ///converted into a string of one character between !-. . This string is then returned. - return String.fromCharCodes([random.nextInt(maxRand) + base]); - } - - ///Toggles letter generation - void checkLetterGen(bool value) { - _letterGen = value; - } - - ///Toggles number generation - void checkNumGen(bool value) { - _numGen = value; - } - - ///Toggles symbol generation - void checkSymGen(bool value) { - _symGen = value; - } - - ///Returns the generated value to be used by generator app - String getGeneratedValue() { - return _generatedValue; - } +typedef StringGeneratorFunction = String Function(); + +class StringGenerators { + static const letters = 'abcdefghijklmnopqrstuvwxyz'; + static const numbers = '1234567890'; + static const symbols = '_'; + + static String getRandomString( + int length, { + hasLowercaseLetters = false, + hasUppercaseLetters = false, + hasNumbers = false, + hasSymbols = false, + isStrict = false, + }) { + var chars = ''; + if (hasLowercaseLetters) chars += letters; + if (hasUppercaseLetters) chars += letters.toUpperCase(); + if (hasNumbers) chars += numbers; + if (hasSymbols) chars += symbols; + + assert(chars.isNotEmpty, 'chart empty'); + + if (!isStrict) { + return genString(length, chars); + } + + var res = ''; + var loose = length; + if (hasLowercaseLetters) { + loose -= 1; + res += genString(1, letters); + } + if (hasUppercaseLetters) { + loose -= 1; + res += genString(1, letters.toUpperCase()); + } + if (hasNumbers) { + loose -= 1; + res += genString(1, numbers.toUpperCase()); + } + if (hasSymbols) { + loose -= 1; + res += genString(1, symbols); + } + res += genString(loose, chars); + + var shuffledlist = res.split('')..shuffle(); + return shuffledlist.join(); + } + + static String genString(int length, String chars) { + return String.fromCharCodes( + Iterable.generate( + length, + (_) => chars.codeUnitAt( + _rnd.nextInt(chars.length), + ), + ), + ); + } + + static StringGeneratorFunction userPassword = () => getRandomString( + 8, + hasLowercaseLetters: true, + hasUppercaseLetters: true, + hasNumbers: true, + isStrict: true, + ); + + static StringGeneratorFunction passwordSalt = () => getRandomString( + 8, + hasLowercaseLetters: true, + ); + + static StringGeneratorFunction simpleId = () => getRandomString( + 5, + hasLowercaseLetters: true, + ); + + static StringGeneratorFunction dbPassword = () => getRandomString( + 40, + hasLowercaseLetters: true, + hasUppercaseLetters: true, + hasNumbers: true, + hasSymbols: true, + ); + + static StringGeneratorFunction dbStorageName = () => getRandomString( + 6, + hasLowercaseLetters: true, + hasUppercaseLetters: true, + hasNumbers: true, + ); } diff --git a/lib/utils/password_generator2.dart b/lib/utils/password_generator2.dart deleted file mode 100644 index 36114d55..00000000 --- a/lib/utils/password_generator2.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'dart:math'; - -const _chars = - 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890_'; -Random _rnd = Random(); - -String getRandomString(int length, [chars = _chars]) => String.fromCharCodes( - Iterable.generate( - length, - (_) => chars.codeUnitAt( - _rnd.nextInt(chars.length), - ), - ), - ); diff --git a/pubspec.lock b/pubspec.lock index dca1a951..5f0d5809 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -28,7 +28,7 @@ packages: name: args url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.2.0" async: dependency: transitive description: @@ -37,12 +37,12 @@ packages: source: hosted version: "2.6.1" basic_utils: - dependency: "direct dev" + dependency: "direct main" description: name: basic_utils url: "https://pub.dartlang.org" source: hosted - version: "3.1.0" + version: "3.4.0" bloc: dependency: transitive description: @@ -63,7 +63,7 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.3" build_config: dependency: transitive description: @@ -84,7 +84,7 @@ packages: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.4" build_runner: dependency: "direct dev" description: @@ -112,7 +112,7 @@ packages: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "8.1.0" + version: "8.1.2" characters: dependency: transitive description: @@ -140,7 +140,7 @@ packages: name: cli_util url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.3.3" clock: dependency: transitive description: @@ -168,7 +168,7 @@ packages: name: convert url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.1" coverage: dependency: transitive description: @@ -196,7 +196,7 @@ packages: name: cubit_form url: "https://pub.dartlang.org" source: hosted - version: "1.0.16" + version: "1.0.18" cupertino_icons: dependency: "direct main" description: @@ -210,7 +210,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.3" dio: dependency: "direct main" description: @@ -259,7 +259,7 @@ packages: name: extended_masked_text url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "2.3.0" fake_async: dependency: transitive description: @@ -306,14 +306,14 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "7.0.1" + version: "7.1.0" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.9.0" + version: "0.9.1" flutter_localizations: dependency: transitive description: flutter @@ -325,14 +325,14 @@ packages: name: flutter_markdown url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.4" flutter_secure_storage: dependency: "direct main" description: name: flutter_secure_storage url: "https://pub.dartlang.org" source: hosted - version: "4.2.0" + version: "4.2.1" flutter_test: dependency: "direct dev" description: flutter @@ -426,7 +426,7 @@ packages: name: io url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.0.3" ionicons: dependency: "direct main" description: @@ -454,7 +454,7 @@ packages: name: json_serializable url: "https://pub.dartlang.org" source: hosted - version: "4.1.3" + version: "4.1.4" logging: dependency: transitive description: @@ -552,14 +552,14 @@ packages: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" path_provider_platform_interface: dependency: transitive description: @@ -573,7 +573,7 @@ packages: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.3" pedantic: dependency: transitive description: @@ -601,14 +601,14 @@ packages: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" pointycastle: dependency: transitive description: name: pointycastle url: "https://pub.dartlang.org" source: hosted - version: "3.1.2" + version: "3.3.0" pool: dependency: transitive description: @@ -629,7 +629,7 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.2.1" + version: "4.2.3" provider: dependency: "direct main" description: @@ -706,14 +706,14 @@ packages: name: shared_preferences_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" shared_preferences_macos: dependency: transitive description: name: shared_preferences_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" shared_preferences_platform_interface: dependency: transitive description: @@ -727,21 +727,21 @@ packages: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.2" shelf: dependency: transitive description: name: shelf url: "https://pub.dartlang.org" source: hosted - version: "1.1.4" + version: "1.2.0" shelf_packages_handler: dependency: transitive description: @@ -755,7 +755,7 @@ packages: name: shelf_static url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "1.1.0" shelf_web_socket: dependency: transitive description: @@ -774,14 +774,14 @@ packages: name: source_gen url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "1.0.3" source_helper: dependency: transitive description: name: source_helper url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.1" source_map_stack_trace: dependency: transitive description: @@ -886,42 +886,42 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "6.0.6" + version: "6.0.9" url_launcher_linux: dependency: transitive description: name: url_launcher_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.4" url_launcher_web: dependency: transitive description: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.1" vector_math: dependency: transitive description: @@ -942,35 +942,35 @@ packages: name: wakelock url: "https://pub.dartlang.org" source: hosted - version: "0.5.2" + version: "0.5.3+3" wakelock_macos: dependency: transitive description: name: wakelock_macos url: "https://pub.dartlang.org" source: hosted - version: "0.1.0+1" + version: "0.1.0+2" wakelock_platform_interface: dependency: transitive description: name: wakelock_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "0.2.1+1" + version: "0.2.1+2" wakelock_web: dependency: transitive description: name: wakelock_web url: "https://pub.dartlang.org" source: hosted - version: "0.2.0+1" + version: "0.2.0+2" wakelock_windows: dependency: transitive description: name: wakelock_windows url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" + version: "0.1.0+1" watcher: dependency: transitive description: @@ -998,7 +998,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "2.2.5" xdg_directories: dependency: transitive description: @@ -1021,5 +1021,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.13.0 <3.0.0" - flutter: ">=2.0.0" + dart: ">=2.13.4 <3.0.0" + flutter: ">=2.2.3" diff --git a/pubspec.yaml b/pubspec.yaml index a9f7d600..44eadcb7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,8 +4,8 @@ publish_to: 'none' version: 0.1.3+5 environment: - sdk: '>=2.12.0 <3.0.0' - flutter: ">=2.0.0" + sdk: '>=2.13.4 <3.0.0' + flutter: ">=2.2.3" dependencies: flutter: @@ -16,9 +16,9 @@ dependencies: dio: ^4.0.0-beta7 easy_localization: ^3.0.0 either_option: ^2.0.1-dev.1 - equatable: ^2.0.0 + equatable: ^2.0.3 fl_chart: ^0.35.0 - flutter_bloc: ^7.0.0 + flutter_bloc: ^7.1.0 flutter_markdown: ^0.6.0 flutter_secure_storage: ^4.1.0 get_it: ^6.0.0 @@ -35,11 +35,11 @@ dependencies: unicons: ^1.0.2 url_launcher: ^6.0.2 wakelock: ^0.5.0+2 + basic_utils: ^3.4.0 dev_dependencies: flutter_test: sdk: flutter - basic_utils: ^3.0.0-nullsafety.1 build_runner: ^1.11.5 flutter_launcher_icons: ^0.9.0 hive_generator: ^1.0.0 diff --git a/test/widget_test.dart b/test/widget_test.dart index 6eed7a46..aaa33419 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -1,30 +1,122 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:selfprivacy/main.dart'; +import 'package:selfprivacy/utils/password_generator.dart'; void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(MyApp()); + group('StringGenerators', () { + group('Basic', () { + test('assert chart empty', () { + expect(() { + StringGenerators.getRandomString(8); + }, throwsAssertionError); + }); - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); + test('only lowercase string', () { + var length = 8; + var generatedString = + StringGenerators.getRandomString(length, hasLowercaseLetters: true); - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); + expect(generatedString, isNot(matches(regExpNewLines))); + expect(generatedString, isNot(matches(regExpWhiteSpaces))); + expect(generatedString, isNot(matches(regExpNumbers))); + expect(generatedString, isNot(matches(regExpUppercaseLetters))); + expect(generatedString, isNot(matches(regExpSymbols))); + expect(generatedString.length, equals(length)); + expect(generatedString, matches(regExpLowercaseLetters)); + }); - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); + test('only uppercase string', () { + var length = 8; + var generatedString = StringGenerators.getRandomString(length, + hasLowercaseLetters: false, hasUppercaseLetters: true); + + expect(generatedString, isNot(matches(regExpNewLines))); + expect(generatedString, isNot(matches(regExpWhiteSpaces))); + expect(generatedString, isNot(matches(regExpNumbers))); + expect(generatedString, isNot(matches(regExpLowercaseLetters))); + expect(generatedString, isNot(matches(regExpSymbols))); + expect(generatedString.length, equals(length)); + expect(generatedString, matches(regExpUppercaseLetters)); + }); + + test('only numbers string', () { + var length = 8; + var generatedString = StringGenerators.getRandomString(length, + hasLowercaseLetters: false, + hasUppercaseLetters: false, + hasNumbers: true); + + expect(generatedString, isNot(matches(regExpNewLines))); + expect(generatedString, isNot(matches(regExpWhiteSpaces))); + expect(generatedString, isNot(matches(regExpUppercaseLetters))); + expect(generatedString, isNot(matches(regExpLowercaseLetters))); + expect(generatedString, isNot(matches(regExpSymbols))); + expect(generatedString.length, equals(length)); + expect(generatedString, matches(regExpNumbers)); + }); + + test('only symbols string', () { + var length = 8; + var generatedString = StringGenerators.getRandomString( + length, + hasLowercaseLetters: false, + hasUppercaseLetters: false, + hasNumbers: false, + hasSymbols: true, + ); + + expect(generatedString, isNot(matches(regExpNewLines))); + expect(generatedString, isNot(matches(regExpWhiteSpaces))); + expect(generatedString, isNot(matches(regExpUppercaseLetters))); + expect(generatedString, isNot(matches(regExpLowercaseLetters))); + expect(generatedString, isNot(matches(regExpNumbers))); + expect(generatedString.length, equals(length)); + expect(generatedString, matches(regExpSymbols)); + }); + }); + + group('Strict mode', () { + test('All', () { + var length = 5; + var generatedString = StringGenerators.getRandomString(length, + hasLowercaseLetters: true, + hasUppercaseLetters: true, + hasNumbers: true, + hasSymbols: true, + isStrict: true); + + expect(generatedString, isNot(matches(regExpNewLines))); + expect(generatedString, isNot(matches(regExpWhiteSpaces))); + expect(generatedString, matches(regExpLowercaseLetters)); + expect(generatedString, matches(regExpUppercaseLetters)); + expect(generatedString, matches(regExpNumbers)); + expect(generatedString, matches(regExpSymbols)); + expect(generatedString.length, equals(length)); + }); + test('Lowercase letters and numbers', () { + var length = 3; + var generatedString = StringGenerators.getRandomString(length, + hasLowercaseLetters: true, + hasUppercaseLetters: false, + hasNumbers: true, + hasSymbols: false, + isStrict: true); + + expect(generatedString, isNot(matches(regExpNewLines))); + expect(generatedString, isNot(matches(regExpWhiteSpaces))); + expect(generatedString, isNot(matches(regExpUppercaseLetters))); + expect(generatedString, isNot(matches(regExpSymbols))); + expect(generatedString, matches(regExpLowercaseLetters)); + expect(generatedString, matches(regExpNumbers)); + expect(generatedString.length, equals(length)); + }); + }); }); } + +var regExpNewLines = RegExp(r"[\n\r]+"); +var regExpWhiteSpaces = RegExp(r"[\s]+"); +var regExpUppercaseLetters = RegExp(r"[A-Z]"); +var regExpLowercaseLetters = RegExp(r"[a-z]"); +var regExpNumbers = RegExp(r"[0-9]"); +var regExpSymbols = RegExp(r'(?:_|[^\w\s])+'); From 1a8a4e7270e32e7e37e417cb0fd1bc6ea72039f4 Mon Sep 17 00:00:00 2001 From: Kherel Date: Wed, 18 Aug 2021 12:44:46 +0200 Subject: [PATCH 2/6] update --- assets/translations/en.json | 5 +- assets/translations/ru.json | 5 +- lib/logic/common_enum/common_enum.dart | 72 ++++++++++++++++++++++++++ lib/logic/models/jobs/job.dart | 17 +++++- lib/ui/pages/services/services.dart | 59 +++------------------ 5 files changed, 102 insertions(+), 56 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 0b9b3f5e..2705b68a 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -212,7 +212,10 @@ "_comment": "Jobs list", "title": "Jobs list", "start": "Start", - "empty": "No jobs" + "empty": "No jobs", + "createUser": "Create", + "serviceTurnOff": "Turn off", + "serviceTurnOn": "Turn on" }, "validations": { "required": "Required", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index bc89ab19..553d7b01 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -212,7 +212,10 @@ "_comment": "Jobs list", "title": "Задачи", "start": "Начать выполенение", - "empty": "Пусто" + "empty": "Пусто", + "createUser": "Создать запись", + "serviceTurnOff": "Остановить", + "serviceTurnOn": "Запустить" }, "validations": { "required": "обязательное поле", diff --git a/lib/logic/common_enum/common_enum.dart b/lib/logic/common_enum/common_enum.dart index 912c4cb6..03c05678 100644 --- a/lib/logic/common_enum/common_enum.dart +++ b/lib/logic/common_enum/common_enum.dart @@ -1,3 +1,7 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; + enum InitializingSteps { setHeznerKey, setCloudFlareKey, @@ -10,3 +14,71 @@ enum InitializingSteps { } enum Period { hour, day, month } +enum ServiceTypes { + mail, + messenger, + passwordManager, + video, + cloud, + socialNetwork, + git, +} + +extension ServiceTypesExt on ServiceTypes { + String get title { + switch (this) { + case ServiceTypes.mail: + return 'services.mail.title'.tr(); + case ServiceTypes.messenger: + return 'services.messenger.title'.tr(); + case ServiceTypes.passwordManager: + return 'services.password_manager.title'.tr(); + case ServiceTypes.video: + return 'services.video.title'.tr(); + case ServiceTypes.cloud: + return 'services.cloud.title'.tr(); + case ServiceTypes.socialNetwork: + return 'services.social_network.title'.tr(); + case ServiceTypes.git: + return 'services.git.title'.tr(); + } + } + + String get subtitle { + switch (this) { + case ServiceTypes.mail: + return 'services.mail.subtitle'.tr(); + case ServiceTypes.messenger: + return 'services.messenger.subtitle'.tr(); + case ServiceTypes.passwordManager: + return 'services.password_manager.subtitle'.tr(); + case ServiceTypes.video: + return 'services.video.subtitle'.tr(); + case ServiceTypes.cloud: + return 'services.cloud.subtitle'.tr(); + case ServiceTypes.socialNetwork: + return 'services.social_network.subtitle'.tr(); + case ServiceTypes.git: + return 'services.git.subtitle'.tr(); + } + } + + IconData get icon { + switch (this) { + case ServiceTypes.mail: + return BrandIcons.envelope; + case ServiceTypes.messenger: + return BrandIcons.messanger; + case ServiceTypes.passwordManager: + return BrandIcons.key; + case ServiceTypes.video: + return BrandIcons.webcam; + case ServiceTypes.cloud: + return BrandIcons.upload; + case ServiceTypes.socialNetwork: + return BrandIcons.social; + case ServiceTypes.git: + return BrandIcons.git; + } + } +} diff --git a/lib/logic/models/jobs/job.dart b/lib/logic/models/jobs/job.dart index 2fcc8e87..69ae0b79 100644 --- a/lib/logic/models/jobs/job.dart +++ b/lib/logic/models/jobs/job.dart @@ -1,6 +1,8 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/utils/password_generator.dart'; +import 'package:easy_localization/easy_localization.dart'; import '../user.dart'; @@ -21,10 +23,23 @@ class Job extends Equatable { class CreateUserJob extends Job { CreateUserJob({ required this.user, - }) : super(title: 'Create ${user.login}'); + }) : super(title: '${"jobs.createUser".tr()} ${user.login}'); final User user; @override List get props => [id, title]; } + +class ServiceToggleJob extends Job { + ServiceToggleJob({ + required this.type, + required this.needToTurnOn, + }) : super(title: '${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} '); + + final ServiceTypes type; + final bool needToTurnOn; + + @override + List get props => [id, title]; +} diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 3957e22f..599e7e8f 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; @@ -67,44 +68,6 @@ class _Card extends StatelessWidget { IconData iconData; String subtitle; - switch (serviceType) { - case ServiceTypes.mail: - iconData = BrandIcons.envelope; - title = 'services.mail.title'.tr(); - subtitle = 'services.mail.subtitle'.tr(); - break; - case ServiceTypes.messenger: - iconData = BrandIcons.messanger; - title = 'services.messenger.title'.tr(); - subtitle = 'services.messenger.subtitle'.tr(); - break; - case ServiceTypes.passwordManager: - iconData = BrandIcons.key; - title = 'services.password_manager.title'.tr(); - subtitle = 'services.password_manager.subtitle'.tr(); - break; - case ServiceTypes.video: - iconData = BrandIcons.webcam; - title = 'services.video.title'.tr(); - subtitle = 'services.video.subtitle'.tr(); - break; - case ServiceTypes.cloud: - iconData = BrandIcons.upload; - title = 'services.cloud.title'.tr(); - subtitle = 'services.cloud.subtitle'.tr(); - break; - case ServiceTypes.socialNetwork: - iconData = BrandIcons.social; - title = 'services.social_network.title'.tr(); - subtitle = 'services.social_network.subtitle'.tr(); - break; - case ServiceTypes.git: - iconData = BrandIcons.git; - title = 'services.git.title'.tr(); - subtitle = 'services.git.subtitle'.tr(); - break; - } - var isReady = context.watch().state.isFullyInitilized; var changeTab = context.read().onPress; return GestureDetector( @@ -118,8 +81,8 @@ class _Card extends StatelessWidget { serviceType: serviceType, status: isReady ? StateType.stable : StateType.uninitialized, - title: title, - icon: iconData, + title: serviceType.title, + icon: serviceType.icon, changeTab: changeTab, ); }, @@ -131,12 +94,12 @@ class _Card extends StatelessWidget { children: [ IconStatusMask( status: isReady ? StateType.stable : StateType.uninitialized, - child: Icon(iconData, size: 30, color: Colors.white), + child: Icon(serviceType.icon, size: 30, color: Colors.white), ), SizedBox(height: 10), - BrandText.h2(title), + BrandText.h2(serviceType.title), SizedBox(height: 10), - BrandText.body2(subtitle), + BrandText.body2(serviceType.subtitle), SizedBox(height: 10), ], ), @@ -145,16 +108,6 @@ class _Card extends StatelessWidget { } } -enum ServiceTypes { - mail, - messenger, - passwordManager, - video, - cloud, - socialNetwork, - git, -} - class _ServiceDetails extends StatelessWidget { const _ServiceDetails({ Key? key, From 94a0e22b15556e46eab6e27be91d749c12fc9beb Mon Sep 17 00:00:00 2001 From: Kherel Date: Sun, 29 Aug 2021 11:50:24 +0200 Subject: [PATCH 3/6] update --- assets/translations/en.json | 4 +- assets/translations/ru.json | 3 +- ios/Flutter/AppFrameworkInfo.plist | 2 +- .../Flutter.build/dgph | Bin 0 -> 96 bytes .../Pods-Runner.build/dgph | Bin 0 -> 96 bytes .../flutter_secure_storage.build/dgph | Bin 0 -> 96 bytes .../package_info.build/dgph | Bin 0 -> 96 bytes .../path_provider.build/dgph | Bin 0 -> 96 bytes .../share_plus.build/dgph | Bin 0 -> 96 bytes .../shared_preferences.build/dgph | Bin 0 -> 96 bytes .../url_launcher.build/dgph | Bin 0 -> 96 bytes .../wakelock.build/dgph | Bin 0 -> 96 bytes lib/config/hive_config.dart | 8 +- lib/logic/api_maps/hetzner.dart | 5 +- lib/logic/cubit/jobs/jobs_cubit.dart | 6 + lib/logic/cubit/services/services_cubit.dart | 10 + lib/logic/cubit/services/services_state.dart | 10 + lib/logic/get_it/navigation.dart | 9 + lib/main.dart | 4 +- .../components/brand_switch/brand_switch.dart | 24 ++ .../pages/more/app_settings/app_setting.dart | 5 +- lib/ui/pages/server_details/cpu_chart.dart | 4 +- .../pages/server_details/network_charts.dart | 4 +- lib/ui/pages/services/services.dart | 254 ------------------ pubspec.lock | 57 ++-- pubspec.yaml | 10 +- 26 files changed, 116 insertions(+), 303 deletions(-) create mode 100644 ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/flutter_secure_storage.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/package_info.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/path_provider.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/share_plus.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/shared_preferences.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/url_launcher.build/dgph create mode 100644 ios/build/Pods.build/Release-iphonesimulator/wakelock.build/dgph create mode 100644 lib/logic/cubit/services/services_cubit.dart create mode 100644 lib/logic/cubit/services/services_state.dart create mode 100644 lib/ui/components/brand_switch/brand_switch.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 2705b68a..be579151 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -215,7 +215,9 @@ "empty": "No jobs", "createUser": "Create", "serviceTurnOff": "Turn off", - "serviceTurnOn": "Turn on" + "serviceTurnOn": "Turn on", + "jobAdded": "Job added" + }, "validations": { "required": "Required", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 553d7b01..9fa3fc62 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -215,7 +215,8 @@ "empty": "Пусто", "createUser": "Создать запись", "serviceTurnOff": "Остановить", - "serviceTurnOn": "Запустить" + "serviceTurnOn": "Запустить", + "jobAdded": "Задача добавленна" }, "validations": { "required": "обязательное поле", diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f78..f2872cf4 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/Flutter.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/Pods-Runner.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/flutter_secure_storage.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/flutter_secure_storage.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/package_info.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/package_info.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/path_provider.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/path_provider.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/share_plus.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/share_plus.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/shared_preferences.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/shared_preferences.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/url_launcher.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/url_launcher.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/ios/build/Pods.build/Release-iphonesimulator/wakelock.build/dgph b/ios/build/Pods.build/Release-iphonesimulator/wakelock.build/dgph new file mode 100644 index 0000000000000000000000000000000000000000..0c4ca337c8210b369b6d77f3095ffbc2091ccb65 GIT binary patch literal 96 zcmZ>95AZP5Gce)yD$P?+uuw2EFf!yYG_f);ure}WXJBB|XJBLx%g-szO)X|%Vs=R_ nV_@bkPR&UxD9S8LOs-^LLE*77XXY0(u(1T>rxY^+wKD(!DHRsg literal 0 HcmV?d00001 diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index e704b50c..4e84c848 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -22,15 +22,13 @@ class HiveConfig { await Hive.openBox(BNames.users); var cipher = HiveAesCipher(await getEncriptedKey()); - await Hive.openBox(BNames.appConfig, encryptionCipher: cipher); } static Future getEncriptedKey() async { - final FlutterSecureStorage secureStorage = FlutterSecureStorage(); - var containsEncryptionKey = - await secureStorage.containsKey(key: BNames.key); - if (!containsEncryptionKey) { + final secureStorage = FlutterSecureStorage(); + var hasEncryptionKey = await secureStorage.containsKey(key: BNames.key); + if (!hasEncryptionKey) { var key = Hive.generateSecureKey(); await secureStorage.write(key: BNames.key, value: base64UrlEncode(key)); } diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 0c29a040..dcf3a518 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -73,7 +73,6 @@ class HetznerApi extends ApiMap { required User rootUser, required String domainName, }) async { - var client = await getClient(); Response dbCreateResponse = await client.post( @@ -87,12 +86,12 @@ class HetznerApi extends ApiMap { "format": "ext4" }, ); - + var dbPassword = StringGenerators.dbPassword(); var dbId = dbCreateResponse.data['volume']['id']; var data = jsonDecode( - '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword.hash} SALT=${rootUser.hashPassword.salt} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''); + '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], ssh_keys:[kherel], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/development/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''); Response serverCreateResponse = await client.post( '/servers', diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index aebe2ddd..20285342 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; @@ -6,6 +7,7 @@ import 'package:selfprivacy/logic/models/jobs/job.dart'; import 'package:equatable/equatable.dart'; import 'package:selfprivacy/logic/models/user.dart'; export 'package:provider/provider.dart'; +import 'package:easy_localization/easy_localization.dart'; part 'jobs_state.dart'; @@ -21,6 +23,10 @@ class JobsCubit extends Cubit { newJobsList.addAll((state as JobsStateWithJobs).jobList); } newJobsList.add(job); + getIt().showSnackBar(SnackBar( + content: Text('jobs.jobAdded'.tr()), + duration: const Duration(seconds: 2), + )); emit(JobsStateWithJobs(newJobsList)); } diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart new file mode 100644 index 00000000..36bc3155 --- /dev/null +++ b/lib/logic/cubit/services/services_cubit.dart @@ -0,0 +1,10 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; + +part 'services_state.dart'; + +class ServicesCubit extends Cubit { + ServicesCubit() : super(ServicesInitial()); + + +} diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart new file mode 100644 index 00000000..e58e02cf --- /dev/null +++ b/lib/logic/cubit/services/services_state.dart @@ -0,0 +1,10 @@ +part of 'services_cubit.dart'; + +abstract class ServicesState extends Equatable { + const ServicesState(); + + @override + List get props => []; +} + +class ServicesInitial extends ServicesState {} diff --git a/lib/logic/get_it/navigation.dart b/lib/logic/get_it/navigation.dart index 92074c93..6d179fa7 100644 --- a/lib/logic/get_it/navigation.dart +++ b/lib/logic/get_it/navigation.dart @@ -2,7 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; class NavigationService { + final GlobalKey scaffoldMessengerKey = + GlobalKey(); final GlobalKey navigatorKey = GlobalKey(); + NavigatorState? get navigator => navigatorKey.currentState; void showPopUpDialog(AlertDialog dialog) { @@ -13,4 +16,10 @@ class NavigationService { builder: (_) => dialog, ); } + + void showSnackBar(SnackBar snackBar) { + final state = scaffoldMessengerKey.currentState!; + + state.showSnackBar(snackBar); + } } diff --git a/lib/main.dart b/lib/main.dart index 67199de0..cfd595ae 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,11 +16,11 @@ import 'config/localization.dart'; import 'logic/cubit/app_settings/app_settings_cubit.dart'; void main() async { + WidgetsFlutterBinding.ensureInitialized(); await HiveConfig.init(); Bloc.observer = SimpleBlocObserver(); Wakelock.enable(); await getItSetup(); - WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); runApp(MyApp()); @@ -37,6 +37,8 @@ class MyApp extends StatelessWidget { return AnnotatedRegion( value: SystemUiOverlayStyle.light, // Manually changnig appbar color child: MaterialApp( + scaffoldMessengerKey: + getIt.get().scaffoldMessengerKey, navigatorKey: getIt.get().navigatorKey, localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, diff --git a/lib/ui/components/brand_switch/brand_switch.dart b/lib/ui/components/brand_switch/brand_switch.dart new file mode 100644 index 00000000..60c411cf --- /dev/null +++ b/lib/ui/components/brand_switch/brand_switch.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import 'package:selfprivacy/config/brand_colors.dart'; + +class BrandSwitch extends StatelessWidget { + const BrandSwitch({ + Key? key, + required this.onChanged, + required this.value, + }) : super(key: key); + + final ValueChanged onChanged; + final bool value; + + @override + Widget build(BuildContext context) { + return Switch( + activeColor: BrandColors.green1, + activeTrackColor: BrandColors.green2, + value: value, + onChanged: onChanged, + ); + } +} diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index 6959b343..4aa1b2a4 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -7,6 +7,7 @@ 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_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; +import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/utils/named_font_weight.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -52,9 +53,7 @@ class _AppSettingsPageState extends State { ), ), SizedBox(width: 5), - Switch( - activeColor: BrandColors.green1, - activeTrackColor: BrandColors.green2, + BrandSwitch( value: Theme.of(context).brightness == Brightness.dark, onChanged: (value) => context .read() diff --git a/lib/ui/pages/server_details/cpu_chart.dart b/lib/ui/pages/server_details/cpu_chart.dart index 88163caa..96ee94ce 100644 --- a/lib/ui/pages/server_details/cpu_chart.dart +++ b/lib/ui/pages/server_details/cpu_chart.dart @@ -51,7 +51,7 @@ class CpuChart extends StatelessWidget { interval: 20, rotateAngle: 90.0, showTitles: true, - getTextStyles: (value) => const TextStyle( + getTextStyles: (_, __) => const TextStyle( fontSize: 10, color: Colors.purple, fontWeight: FontWeight.bold, @@ -60,7 +60,7 @@ class CpuChart extends StatelessWidget { return bottomTitle(value.toInt()); }), leftTitles: SideTitles( - getTextStyles: (value) => progressTextStyleLight.copyWith( + getTextStyles: (_, __) => progressTextStyleLight.copyWith( color: Theme.of(context).brightness == Brightness.dark ? BrandColors.gray4 : null, diff --git a/lib/ui/pages/server_details/network_charts.dart b/lib/ui/pages/server_details/network_charts.dart index cb352267..44d1c0ba 100644 --- a/lib/ui/pages/server_details/network_charts.dart +++ b/lib/ui/pages/server_details/network_charts.dart @@ -71,7 +71,7 @@ class NetworkChart extends StatelessWidget { interval: 20, rotateAngle: 90.0, showTitles: true, - getTextStyles: (value) => const TextStyle( + getTextStyles: (_, __) => const TextStyle( fontSize: 10, color: Colors.purple, fontWeight: FontWeight.bold, @@ -87,7 +87,7 @@ class NetworkChart extends StatelessWidget { ].reduce(max) * 1.2 / 10, - getTextStyles: (value) => progressTextStyleLight.copyWith( + getTextStyles: (_, __) => progressTextStyleLight.copyWith( color: Theme.of(context).brightness == Brightness.dark ? BrandColors.gray4 : null, diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 599e7e8f..a36d745d 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -8,7 +8,6 @@ import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.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/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; @@ -64,10 +63,6 @@ class _Card extends StatelessWidget { final ServiceTypes serviceType; @override Widget build(BuildContext context) { - String title; - IconData iconData; - String subtitle; - var isReady = context.watch().state.isFullyInitilized; var changeTab = context.read().onPress; return GestureDetector( @@ -366,252 +361,3 @@ class _ServiceDetails extends StatelessWidget { } } } - - -// class _ServiceDetails extends StatelessWidget { -// const _ServiceDetails({ -// Key? key, -// required this.serviceType, -// required this.icon, -// required this.status, -// required this.title, -// required this.changeTab, -// }) : super(key: key); - -// final ServiceTypes serviceType; -// final IconData icon; -// final StateType status; -// final String title; -// final ValueChanged changeTab; - -// @override -// Widget build(BuildContext context) { -// late Widget child; - -// var config = context.watch().state; -// var domainName = UiHelpers.getDomainName(config); - -// var linksStyle = body1Style.copyWith( -// fontSize: 15, -// color: Theme.of(context).brightness == Brightness.dark -// ? Colors.white -// : BrandColors.black, -// fontWeight: FontWeight.bold, -// decoration: TextDecoration.underline, -// // height: 1.1, -// ); - -// var textStyle = body1Style.copyWith( -// color: Theme.of(context).brightness == Brightness.dark -// ? Colors.white -// : BrandColors.black, -// ); -// switch (serviceType) { -// case ServiceTypes.mail: -// child = RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: 'services.mail.bottom_sheet.1'.tr(args: [domainName]), -// style: textStyle, -// ), -// WidgetSpan( -// child: Padding( -// padding: EdgeInsets.only(bottom: 0.8, left: 5), -// child: GestureDetector( -// child: Text( -// 'services.mail.bottom_sheet.2'.tr(), -// style: linksStyle, -// ), -// onTap: () { -// Navigator.of(context).pop(); -// changeTab(2); -// }, -// ), -// ), -// ), -// ], -// )); -// break; -// case ServiceTypes.messenger: -// child = RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: 'services.messenger.bottom_sheet.1'.tr(args: [domainName]), -// style: textStyle, -// ) -// ], -// )); -// break; -// case ServiceTypes.passwordManager: -// child = RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: 'services.password_manager.bottom_sheet.1' -// .tr(args: [domainName]), -// style: textStyle, -// ), -// WidgetSpan( -// child: Padding( -// padding: EdgeInsets.only(bottom: 0.8, left: 5), -// child: GestureDetector( -// onTap: () => _launchURL('https://password.$domainName'), -// child: Text( -// 'password.$domainName', -// style: linksStyle, -// ), -// ), -// ), -// ), -// ], -// )); -// break; -// case ServiceTypes.video: -// child = RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: 'services.video.bottom_sheet.1'.tr(args: [domainName]), -// style: textStyle, -// ), -// WidgetSpan( -// child: Padding( -// padding: EdgeInsets.only(bottom: 0.8, left: 5), -// child: GestureDetector( -// onTap: () => _launchURL('https://meet.$domainName'), -// child: Text( -// 'meet.$domainName', -// style: linksStyle, -// ), -// ), -// ), -// ), -// ], -// )); -// break; -// case ServiceTypes.cloud: -// child = RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: 'services.cloud.bottom_sheet.1'.tr(args: [domainName]), -// style: textStyle, -// ), -// WidgetSpan( -// child: Padding( -// padding: EdgeInsets.only(bottom: 0.8, left: 5), -// child: GestureDetector( -// onTap: () => _launchURL('https://cloud.$domainName'), -// child: Text( -// 'cloud.$domainName', -// style: linksStyle, -// ), -// ), -// ), -// ), -// ], -// )); -// break; -// case ServiceTypes.socialNetwork: -// child = RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: 'services.social_network.bottom_sheet.1' -// .tr(args: [domainName]), -// style: textStyle, -// ), -// WidgetSpan( -// child: Padding( -// padding: EdgeInsets.only(bottom: 0.8, left: 5), -// child: GestureDetector( -// onTap: () => _launchURL('https://social.$domainName'), -// child: Text( -// 'social.$domainName', -// style: linksStyle, -// ), -// ), -// ), -// ), -// ], -// )); -// break; -// case ServiceTypes.git: -// child = RichText( -// text: TextSpan( -// children: [ -// TextSpan( -// text: 'services.git.bottom_sheet.1'.tr(args: [domainName]), -// style: textStyle, -// ), -// WidgetSpan( -// child: Padding( -// padding: EdgeInsets.only(bottom: 0.8, left: 5), -// child: GestureDetector( -// onTap: () => _launchURL('https://git.$domainName'), -// child: Text( -// 'git.$domainName', -// style: linksStyle, -// ), -// ), -// ), -// ), -// ], -// )); -// break; -// } -// return BrandModalSheet( - // child: Navigator( - // key: navigatorKey, - // initialRoute: '/', - // onGenerateRoute: (_) { - // return materialRoute( -// Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// Padding( -// padding: brandPagePadding1, -// child: Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// SizedBox(height: 13), -// IconStatusMask( -// status: status, -// child: Icon(icon, size: 40, color: Colors.white), -// ), -// SizedBox(height: 10), -// BrandText.h1(title), -// SizedBox(height: 10), -// child, -// ], -// ), -// ) -// ], -// ), -// ); -// }, -// ), -// ); -// } - -// void _launchURL(url) async { -// var _possible = await canLaunch(url); - -// if (_possible) { -// try { -// await launch( -// url, -// forceSafariVC: true, -// enableJavaScript: true, -// ); -// } catch (e) { -// print(e); -// } -// } else { -// throw 'Could not launch $url'; -// } -// } -// } - diff --git a/pubspec.lock b/pubspec.lock index 5f0d5809..48ca09b2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,14 +42,14 @@ packages: name: basic_utils url: "https://pub.dartlang.org" source: hosted - version: "3.4.0" + version: "3.5.0" bloc: dependency: transitive description: name: bloc url: "https://pub.dartlang.org" source: hosted - version: "7.0.0" + version: "7.1.0" boolean_selector: dependency: transitive description: @@ -63,21 +63,21 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.1.0" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.7" + version: "1.0.0" build_daemon: dependency: transitive description: name: build_daemon url: "https://pub.dartlang.org" source: hosted - version: "2.1.10" + version: "3.0.0" build_resolvers: dependency: transitive description: @@ -91,14 +91,14 @@ packages: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.12.2" + version: "2.1.1" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "6.1.12" + version: "7.1.0" built_collection: dependency: transitive description: @@ -154,7 +154,7 @@ packages: name: code_builder url: "https://pub.dartlang.org" source: hosted - version: "3.7.0" + version: "4.1.0" collection: dependency: transitive description: @@ -259,7 +259,7 @@ packages: name: extended_masked_text url: "https://pub.dartlang.org" source: hosted - version: "2.3.0" + version: "2.3.1" fake_async: dependency: transitive description: @@ -294,7 +294,7 @@ packages: name: fl_chart url: "https://pub.dartlang.org" source: hosted - version: "0.35.0" + version: "0.40.0" flutter: dependency: "direct main" description: flutter @@ -306,14 +306,14 @@ packages: name: flutter_bloc url: "https://pub.dartlang.org" source: hosted - version: "7.1.0" + version: "7.2.0" flutter_launcher_icons: dependency: "direct dev" description: name: flutter_launcher_icons url: "https://pub.dartlang.org" source: hosted - version: "0.9.1" + version: "0.9.2" flutter_localizations: dependency: transitive description: flutter @@ -325,7 +325,7 @@ packages: name: flutter_markdown url: "https://pub.dartlang.org" source: hosted - version: "0.6.4" + version: "0.6.5" flutter_secure_storage: dependency: "direct main" description: @@ -343,13 +343,20 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.2" get_it: dependency: "direct main" description: name: get_it url: "https://pub.dartlang.org" source: hosted - version: "6.1.1" + version: "7.2.0" glob: dependency: transitive description: @@ -363,7 +370,7 @@ packages: name: graphs url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "2.0.0" hive: dependency: "direct main" description: @@ -594,7 +601,7 @@ packages: name: platform url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.0.2" plugin_platform_interface: dependency: transitive description: @@ -608,7 +615,7 @@ packages: name: pointycastle url: "https://pub.dartlang.org" source: hosted - version: "3.3.0" + version: "3.3.2" pool: dependency: transitive description: @@ -636,7 +643,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "5.0.0" + version: "6.0.0" pub_semver: dependency: transitive description: @@ -664,7 +671,7 @@ packages: name: share_plus_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "2.0.4" share_plus_macos: dependency: transitive description: @@ -699,7 +706,7 @@ packages: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "2.0.6" + version: "2.0.7" shared_preferences_linux: dependency: transitive description: @@ -727,7 +734,7 @@ packages: name: shared_preferences_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" shared_preferences_windows: dependency: transitive description: @@ -879,7 +886,7 @@ packages: name: unicons url: "https://pub.dartlang.org" source: hosted - version: "1.0.2" + version: "2.0.1" url_launcher: dependency: "direct main" description: @@ -914,14 +921,14 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.4" url_launcher_windows: dependency: transitive description: name: url_launcher_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.2" vector_math: dependency: transitive description: @@ -998,7 +1005,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.5" + version: "2.2.7" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 44eadcb7..5ce1bc14 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,11 +17,11 @@ dependencies: easy_localization: ^3.0.0 either_option: ^2.0.1-dev.1 equatable: ^2.0.3 - fl_chart: ^0.35.0 + fl_chart: ^0.40.0 flutter_bloc: ^7.1.0 flutter_markdown: ^0.6.0 flutter_secure_storage: ^4.1.0 - get_it: ^6.0.0 + get_it: ^7.2.0 hive: ^2.0.0 hive_flutter: ^1.0.0 ionicons: ^0.1.2 @@ -30,9 +30,9 @@ dependencies: nanoid: ^1.0.0 package_info: ^2.0.0 pretty_dio_logger: ^1.1.1 - provider: ^5.0.0 + provider: ^6.0.0 share_plus: ^2.1.4 - unicons: ^1.0.2 + unicons: ^2.0.1 url_launcher: ^6.0.2 wakelock: ^0.5.0+2 basic_utils: ^3.4.0 @@ -40,7 +40,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - build_runner: ^1.11.5 + build_runner: ^2.1.1 flutter_launcher_icons: ^0.9.0 hive_generator: ^1.0.0 json_serializable: ^4.0.2 From 84e9259ec2f1a4836da2ff5c3e45a5a413211e2b Mon Sep 17 00:00:00 2001 From: Kherel Date: Sun, 29 Aug 2021 15:54:28 +0200 Subject: [PATCH 4/6] update --- .editorconfig | 16 +++ assets/translations/en.json | 8 +- lib/config/bloc_config.dart | 15 ++- lib/config/hive_config.dart | 2 + lib/logic/api_maps/hetzner.dart | 5 +- lib/logic/api_maps/server.dart | 27 ++++ lib/logic/common_enum/common_enum.dart | 10 ++ .../cubit/app_config/app_config_cubit.dart | 9 +- lib/logic/cubit/jobs/jobs_cubit.dart | 7 +- lib/logic/cubit/services/services_cubit.dart | 57 ++++++++- lib/logic/cubit/services/services_state.dart | 84 ++++++++++++- .../pages/more/app_settings/app_setting.dart | 117 +++++++++--------- lib/ui/pages/services/services.dart | 4 + pubspec.yaml | 1 - 14 files changed, 288 insertions(+), 74 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..80a3e35b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +# editorconfig.org +root = true + +[*] +indent_size = 2 +indent_style = space +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.dart] +max_line_length = 150 + +[*.md] +trim_trailing_whitespace = false diff --git a/assets/translations/en.json b/assets/translations/en.json index be579151..2ee5202d 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -147,6 +147,13 @@ "bottom_sheet": { "1": "You can connect and create a new user here:" } + }, + "vpn": { + "title": "VPN Server", + "subtitle": "Private VPN server", + "bottom_sheet": { + "1": "Openconnect VPN Server. Engine for secure and scalable VPN infrastructure" + } } }, "users": { @@ -217,7 +224,6 @@ "serviceTurnOff": "Turn off", "serviceTurnOn": "Turn on", "jobAdded": "Job added" - }, "validations": { "required": "Required", diff --git a/lib/config/bloc_config.dart b/lib/config/bloc_config.dart index 5fe71380..bc59e531 100644 --- a/lib/config/bloc_config.dart +++ b/lib/config/bloc_config.dart @@ -4,6 +4,7 @@ import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/providers/providers_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; class BlocAndProviderConfig extends StatelessWidget { @@ -13,11 +14,9 @@ class BlocAndProviderConfig extends StatelessWidget { @override Widget build(BuildContext context) { - // var platformBrightness = - // SchedulerBinding.instance.window.platformBrightness; - // var isDark = platformBrightness == Brightness.dark; var isDark = false; var usersCubit = UsersCubit(); + var servicesCubit = ServicesCubit(); return MultiProvider( providers: [ BlocProvider( @@ -28,11 +27,17 @@ class BlocAndProviderConfig extends StatelessWidget { ), BlocProvider( lazy: false, - create: (_) => AppConfigCubit()..load(), + create: (_) => AppConfigCubit(servicesCubit)..load(), ), BlocProvider(create: (_) => ProvidersCubit()), BlocProvider(create: (_) => usersCubit..load(), lazy: false), - BlocProvider(create: (_) => JobsCubit(usersCubit)), + BlocProvider(create: (_) => servicesCubit..load(), lazy: false), + BlocProvider( + create: (_) => JobsCubit( + usersCubit: usersCubit, + servicesCubit: servicesCubit, + ), + ), ], child: child, ); diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 4e84c848..0e6f1c5b 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -20,6 +20,7 @@ class HiveConfig { await Hive.openBox(BNames.appSettings); await Hive.openBox(BNames.users); + await Hive.openBox(BNames.servicesState); var cipher = HiveAesCipher(await getEncriptedKey()); await Hive.openBox(BNames.appConfig, encryptionCipher: cipher); @@ -45,6 +46,7 @@ class BNames { static String users = 'users'; static String appSettings = 'appSettings'; + static String servicesState = 'servicesState'; static String key = 'key'; diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index dcf3a518..1bd4d10d 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -90,8 +90,11 @@ class HetznerApi extends ApiMap { var dbPassword = StringGenerators.dbPassword(); var dbId = dbCreateResponse.data['volume']['id']; + /// add ssh key when you need it: e.g. "ssh_keys":["kherel"] + /// check the branch name, it could be "development" or "master". + var data = jsonDecode( - '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], ssh_keys:[kherel], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/development/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''); + '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "ssh_keys":["kherel"], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/development/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''); Response serverCreateResponse = await client.post( '/servers', diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index aae49538..0080981e 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:selfprivacy/config/get_it_config.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/models/user.dart'; import 'api_map.dart'; @@ -90,3 +91,29 @@ class ServerApi extends ApiMap { return res; } } + + +extension UrlServerExt on ServiceTypes { + String get url { + switch (this) { + // case ServiceTypes.mail: + // return ''; // cannot be swithch off + // case ServiceTypes.messenger: + // return ''; // external service + // case ServiceTypes.video: + // return ''; // jeetsu meet not working + case ServiceTypes.passwordManager: + return 'bitwarden'; + case ServiceTypes.cloud: + return 'nextcloud'; + case ServiceTypes.socialNetwork: + return 'pleroma'; + case ServiceTypes.git: + return 'gitea'; + case ServiceTypes.vpn: + return 'ocserv'; + default: + throw Exception('wrong state'); + } + } +} \ No newline at end of file diff --git a/lib/logic/common_enum/common_enum.dart b/lib/logic/common_enum/common_enum.dart index 03c05678..b0ceb1f6 100644 --- a/lib/logic/common_enum/common_enum.dart +++ b/lib/logic/common_enum/common_enum.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; +import 'package:unicons/unicons.dart'; enum InitializingSteps { setHeznerKey, @@ -22,6 +23,7 @@ enum ServiceTypes { cloud, socialNetwork, git, + vpn, } extension ServiceTypesExt on ServiceTypes { @@ -41,6 +43,8 @@ extension ServiceTypesExt on ServiceTypes { return 'services.social_network.title'.tr(); case ServiceTypes.git: return 'services.git.title'.tr(); + case ServiceTypes.vpn: + return 'services.vpn.title'.tr(); } } @@ -60,6 +64,8 @@ extension ServiceTypesExt on ServiceTypes { return 'services.social_network.subtitle'.tr(); case ServiceTypes.git: return 'services.git.subtitle'.tr(); + case ServiceTypes.vpn: + return 'services.vpn.subtitle'.tr(); } } @@ -79,6 +85,10 @@ extension ServiceTypesExt on ServiceTypes { return BrandIcons.social; case ServiceTypes.git: return BrandIcons.git; + case ServiceTypes.vpn: + return UniconsLine.cloud_lock; } } + + String get txt => this.toString().split('.')[1]; } diff --git a/lib/logic/cubit/app_config/app_config_cubit.dart b/lib/logic/cubit/app_config/app_config_cubit.dart index d8b8f36e..e86cead1 100644 --- a/lib/logic/cubit/app_config/app_config_cubit.dart +++ b/lib/logic/cubit/app_config/app_config_cubit.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/models/backblaze_credential.dart'; import 'package:selfprivacy/logic/models/cloudflare_domain.dart'; @@ -43,9 +44,10 @@ part 'app_config_state.dart'; /// c. if server is okay set that fully checked class AppConfigCubit extends Cubit { - AppConfigCubit() : super(InitialAppConfigState()); + AppConfigCubit(this.servicesCubit) : super(InitialAppConfigState()); final repository = AppConfigRepository(); + final ServicesCubit servicesCubit; Future load() async { var state = await repository.load(); @@ -232,6 +234,7 @@ class AppConfigCubit extends Cubit { if (isServerWorking) { await repository.saveHasFinalChecked(true); + servicesCubit.allOn(); emit(state.copyWith( hasFinalChecked: true, @@ -259,12 +262,16 @@ class AppConfigCubit extends Cubit { void clearAppConfig() { closeTimer(); + servicesCubit.allOff(); + repository.clearAppConfig(); emit(InitialAppConfigState()); } Future serverDelete() async { closeTimer(); + servicesCubit.allOff(); + if (state.hetznerServer != null) { await repository.deleteServer(state.cloudFlareDomain!); } diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index 20285342..100d837b 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; import 'package:selfprivacy/logic/models/jobs/job.dart'; import 'package:equatable/equatable.dart'; @@ -12,10 +13,14 @@ import 'package:easy_localization/easy_localization.dart'; part 'jobs_state.dart'; class JobsCubit extends Cubit { - JobsCubit(this.usersCubit) : super(JobsStateEmpty()); + JobsCubit({ + required this.usersCubit, + required this.servicesCubit, + }) : super(JobsStateEmpty()); final api = ServerApi(); final UsersCubit usersCubit; + final ServicesCubit servicesCubit; void addJob(Job job) { var newJobsList = []; diff --git a/lib/logic/cubit/services/services_cubit.dart b/lib/logic/cubit/services/services_cubit.dart index 36bc3155..c5fb6cf3 100644 --- a/lib/logic/cubit/services/services_cubit.dart +++ b/lib/logic/cubit/services/services_cubit.dart @@ -1,10 +1,63 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:hive/hive.dart'; +import 'package:selfprivacy/config/hive_config.dart'; +import 'package:selfprivacy/logic/common_enum/common_enum.dart'; part 'services_state.dart'; class ServicesCubit extends Cubit { - ServicesCubit() : super(ServicesInitial()); + ServicesCubit() : super(ServicesState.allOff()); - + Box box = Hive.box(BNames.servicesState); + + void load() { + emit( + ServicesState( + isPasswordManagerEnable: + box.get(ServiceTypes.passwordManager.txt, defaultValue: false), + isCloudEnable: box.get(ServiceTypes.cloud.txt, defaultValue: false), + isGitEnable: box.get(ServiceTypes.git.txt, defaultValue: false), + isSocialNetworkEnable: + box.get(ServiceTypes.socialNetwork.txt, defaultValue: false), + isVpnEnable: box.get(ServiceTypes.vpn.txt, defaultValue: false), + ), + ); + } + + void allOn() { + box.put(ServiceTypes.passwordManager.txt, true); + box.put(ServiceTypes.cloud.txt, true); + box.put(ServiceTypes.git.txt, true); + box.put(ServiceTypes.socialNetwork.txt, true); + box.put(ServiceTypes.vpn.txt, true); + + emit(ServicesState.allOn()); + } + + void allOff() { + box.put(ServiceTypes.passwordManager.txt, false); + box.put(ServiceTypes.cloud.txt, false); + box.put(ServiceTypes.git.txt, false); + box.put(ServiceTypes.socialNetwork.txt, false); + box.put(ServiceTypes.vpn.txt, false); + + emit(ServicesState.allOff()); + } + + void turnOffList(List list) { + for (final service in list) { + box.put(service.txt, false); + } + + emit(state.disableList(list)); + } + + void turnOnist(List list) { + for (final service in list) { + box.put(service.txt, true); + } + + emit(state.enableList(list)); + } } diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart index e58e02cf..ff111e99 100644 --- a/lib/logic/cubit/services/services_state.dart +++ b/lib/logic/cubit/services/services_state.dart @@ -1,10 +1,84 @@ part of 'services_cubit.dart'; -abstract class ServicesState extends Equatable { - const ServicesState(); +const switchableServices = [ + ServiceTypes.passwordManager, + ServiceTypes.cloud, + ServiceTypes.socialNetwork, + ServiceTypes.git, + ServiceTypes.vpn, +]; + +class ServicesState extends Equatable { + const ServicesState({ + required this.isPasswordManagerEnable, + required this.isCloudEnable, + required this.isGitEnable, + required this.isSocialNetworkEnable, + required this.isVpnEnable, + }); + + final bool isPasswordManagerEnable; + final bool isCloudEnable; + final bool isGitEnable; + final bool isSocialNetworkEnable; + final bool isVpnEnable; + + factory ServicesState.allOff() => ServicesState( + isPasswordManagerEnable: false, + isCloudEnable: false, + isGitEnable: false, + isSocialNetworkEnable: false, + isVpnEnable: false, + ); + factory ServicesState.allOn() => ServicesState( + isPasswordManagerEnable: true, + isCloudEnable: true, + isGitEnable: true, + isSocialNetworkEnable: true, + isVpnEnable: true, + ); + + ServicesState enableList( + List list, + ) => + ServicesState( + isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager) + ? true + : isPasswordManagerEnable, + isCloudEnable: list.contains(ServiceTypes.cloud) ? true : isCloudEnable, + isGitEnable: + list.contains(ServiceTypes.git) ? true : isPasswordManagerEnable, + isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork) + ? true + : isPasswordManagerEnable, + isVpnEnable: + list.contains(ServiceTypes.vpn) ? true : isPasswordManagerEnable, + ); + + ServicesState disableList( + List list, + ) => + ServicesState( + isPasswordManagerEnable: list.contains(ServiceTypes.passwordManager) + ? false + : isPasswordManagerEnable, + isCloudEnable: + list.contains(ServiceTypes.cloud) ? false : isCloudEnable, + isGitEnable: + list.contains(ServiceTypes.git) ? false : isPasswordManagerEnable, + isSocialNetworkEnable: list.contains(ServiceTypes.socialNetwork) + ? false + : isPasswordManagerEnable, + isVpnEnable: + list.contains(ServiceTypes.vpn) ? false : isPasswordManagerEnable, + ); @override - List get props => []; + List get props => [ + isPasswordManagerEnable, + isCloudEnable, + isGitEnable, + isSocialNetworkEnable, + isVpnEnable + ]; } - -class ServicesInitial extends ServicesState {} diff --git a/lib/ui/pages/more/app_settings/app_setting.dart b/lib/ui/pages/more/app_settings/app_setting.dart index 4aa1b2a4..a9a15c99 100644 --- a/lib/ui/pages/more/app_settings/app_setting.dart +++ b/lib/ui/pages/more/app_settings/app_setting.dart @@ -119,69 +119,72 @@ class _AppSettingsPageState extends State { ], ), ), - Container( - padding: EdgeInsets.only(top: 20, bottom: 5), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide(width: 1, color: BrandColors.dividerColor), - )), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: _TextColumn( - title: 'more.settings.5'.tr(), - value: 'more.settings.6'.tr(), - ), - ), - SizedBox(width: 5), - ElevatedButton( - style: ElevatedButton.styleFrom( - primary: BrandColors.red1, - ), - child: Text( - 'basis.delete'.tr(), - style: TextStyle( - color: BrandColors.white, - fontWeight: NamedFontWeight.demiBold, - ), - ), - onPressed: () { - showDialog( - context: context, - builder: (_) { - return BrandAlert( - title: 'modals.3'.tr(), - contentText: 'modals.6'.tr(), - acitons: [ - ActionButton( - text: 'modals.7'.tr(), - isRed: true, - onPressed: () async { - await context - .read() - .serverDelete(); - Navigator.of(context).pop(); - }), - ActionButton( - text: 'basis.cancel'.tr(), - ), - ], - ); - }, - ); - }, - ), - ], - ), - ) + // deleteServer(context) ], ), ); }), ); } + + Widget deleteServer(BuildContext context) { + // todo: need to check + return Container( + padding: EdgeInsets.only(top: 20, bottom: 5), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide(width: 1, color: BrandColors.dividerColor), + )), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: _TextColumn( + title: 'more.settings.5'.tr(), + value: 'more.settings.6'.tr(), + ), + ), + SizedBox(width: 5), + ElevatedButton( + style: ElevatedButton.styleFrom( + primary: BrandColors.red1, + ), + child: Text( + 'basis.delete'.tr(), + style: TextStyle( + color: BrandColors.white, + fontWeight: NamedFontWeight.demiBold, + ), + ), + onPressed: () { + showDialog( + context: context, + builder: (_) { + return BrandAlert( + title: 'modals.3'.tr(), + contentText: 'modals.6'.tr(), + acitons: [ + ActionButton( + text: 'modals.7'.tr(), + isRed: true, + onPressed: () async { + await context.read().serverDelete(); + Navigator.of(context).pop(); + }), + ActionButton( + text: 'basis.cancel'.tr(), + ), + ], + ); + }, + ); + }, + ), + ], + ), + ); + } } class _TextColumn extends StatelessWidget { diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index a36d745d..5f16a262 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -301,6 +301,10 @@ class _ServiceDetails extends StatelessWidget { ], )); break; + case ServiceTypes.vpn: + child = Text( + 'services.vpn.bottom_sheet.1'.tr(), + ); } return Dialog( shape: RoundedRectangleBorder( diff --git a/pubspec.yaml b/pubspec.yaml index 5ce1bc14..2ea2e7c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,7 +24,6 @@ dependencies: get_it: ^7.2.0 hive: ^2.0.0 hive_flutter: ^1.0.0 - ionicons: ^0.1.2 json_annotation: ^4.0.0 modal_bottom_sheet: ^2.0.0 nanoid: ^1.0.0 From 26607251d968a4c54fe71a7c29706477f29ebacc Mon Sep 17 00:00:00 2001 From: Kherel Date: Sun, 29 Aug 2021 17:02:51 +0200 Subject: [PATCH 5/6] update --- lib/logic/api_maps/server.dart | 9 +++- .../cubit/forms/user/user_form_cubit.dart | 2 +- lib/logic/cubit/jobs/jobs_cubit.dart | 26 +++++++++- lib/logic/cubit/services/services_state.dart | 17 +++++++ lib/logic/models/{jobs => }/job.dart | 10 ++-- .../components/jobs_content/jobs_content.dart | 6 +-- lib/ui/pages/services/services.dart | 50 +++++++++++++++++-- lib/ui/pages/users/users.dart | 2 +- 8 files changed, 105 insertions(+), 17 deletions(-) rename lib/logic/models/{jobs => }/job.dart (76%) diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 0080981e..7c729789 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -90,8 +90,13 @@ class ServerApi extends ApiMap { close(client); return res; } -} + Future switchService(ServiceTypes type, bool needToTurnOn) async { + var client = await getClient(); + client.post('/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}'); + client.close(); + } +} extension UrlServerExt on ServiceTypes { String get url { @@ -116,4 +121,4 @@ extension UrlServerExt on ServiceTypes { throw Exception('wrong state'); } } -} \ No newline at end of file +} diff --git a/lib/logic/cubit/forms/user/user_form_cubit.dart b/lib/logic/cubit/forms/user/user_form_cubit.dart index 5501b38b..dd4d2bcf 100644 --- a/lib/logic/cubit/forms/user/user_form_cubit.dart +++ b/lib/logic/cubit/forms/user/user_form_cubit.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:cubit_form/cubit_form.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; -import 'package:selfprivacy/logic/models/jobs/job.dart'; +import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/user.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:selfprivacy/utils/password_generator.dart'; diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index 100d837b..19ad09de 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -4,7 +4,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; -import 'package:selfprivacy/logic/models/jobs/job.dart'; +import 'package:selfprivacy/logic/models/job.dart'; import 'package:equatable/equatable.dart'; import 'package:selfprivacy/logic/models/user.dart'; export 'package:provider/provider.dart'; @@ -40,6 +40,23 @@ class JobsCubit extends Cubit { emit(newState); } + void createOrRemove(ServiceToggleJob job) { + var newJobsList = []; + if (state is JobsStateWithJobs) { + newJobsList.addAll((state as JobsStateWithJobs).jobList); + } + var needToRemoveJob = + newJobsList.any((el) => el is ServiceToggleJob && el.type == job.type); + if (needToRemoveJob) { + var removingJob = newJobsList + .firstWhere(((el) => el is ServiceToggleJob && el.type == job.type)); + removeJob(removingJob.id); + } else { + newJobsList.add(job); + emit(JobsStateWithJobs(newJobsList)); + } + } + Future applyAll() async { if (state is JobsStateWithJobs) { var jobs = (state as JobsStateWithJobs).jobList; @@ -50,6 +67,13 @@ class JobsCubit extends Cubit { if (job is CreateUserJob) { newUsers.add(job.user); await api.createUser(job.user); + } else if (job is ServiceToggleJob) { + await api.switchService(job.type, job.needToTurnOn); + if (job.needToTurnOn) { + servicesCubit.turnOnist([job.type]); + } else { + servicesCubit.turnOffList([job.type]); + } } } diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart index ff111e99..ed84ffa8 100644 --- a/lib/logic/cubit/services/services_state.dart +++ b/lib/logic/cubit/services/services_state.dart @@ -81,4 +81,21 @@ class ServicesState extends Equatable { isSocialNetworkEnable, isVpnEnable ]; + + bool isEnableByType(ServiceTypes type) { + switch (type) { + case ServiceTypes.passwordManager: + return isPasswordManagerEnable; + case ServiceTypes.cloud: + return isCloudEnable; + case ServiceTypes.socialNetwork: + return isSocialNetworkEnable; + case ServiceTypes.git: + return isGitEnable; + case ServiceTypes.vpn: + return isVpnEnable; + default: + throw Exception('wrong state'); + } + } } diff --git a/lib/logic/models/jobs/job.dart b/lib/logic/models/job.dart similarity index 76% rename from lib/logic/models/jobs/job.dart rename to lib/logic/models/job.dart index 69ae0b79..d1fb0193 100644 --- a/lib/logic/models/jobs/job.dart +++ b/lib/logic/models/job.dart @@ -4,7 +4,7 @@ import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/utils/password_generator.dart'; import 'package:easy_localization/easy_localization.dart'; -import '../user.dart'; +import 'user.dart'; @immutable class Job extends Equatable { @@ -28,18 +28,20 @@ class CreateUserJob extends Job { final User user; @override - List get props => [id, title]; + List get props => [id, title, user]; } class ServiceToggleJob extends Job { ServiceToggleJob({ required this.type, required this.needToTurnOn, - }) : super(title: '${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} '); + }) : super( + title: + '${needToTurnOn ? "jobs.serviceTurnOn".tr() : "jobs.serviceTurnOff".tr()} ${type.title}'); final ServiceTypes type; final bool needToTurnOn; @override - List get props => [id, title]; + List get props => [id, title, type, needToTurnOn]; } diff --git a/lib/ui/components/jobs_content/jobs_content.dart b/lib/ui/components/jobs_content/jobs_content.dart index aa4b1c65..10879f0e 100644 --- a/lib/ui/components/jobs_content/jobs_content.dart +++ b/lib/ui/components/jobs_content/jobs_content.dart @@ -36,11 +36,7 @@ class JobsContent extends StatelessWidget { children: [ Expanded( child: BrandCards.small( - child: Row( - children: [ - BrandText.body1(j.title), - ], - ), + child: Text(j.title), ), ), SizedBox(width: 10), diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 5f16a262..3a1de59b 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -4,10 +4,14 @@ import 'package:selfprivacy/config/brand_theme.dart'; import 'package:selfprivacy/config/text_themes.dart'; import 'package:selfprivacy/logic/common_enum/common_enum.dart'; import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; +import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; +import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/state_types.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_cards/brand_cards.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.dart'; +import 'package:selfprivacy/ui/components/brand_switch/brand_switch.dart'; import 'package:selfprivacy/ui/components/brand_text/brand_text.dart'; import 'package:selfprivacy/ui/components/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/components/not_ready_card/not_ready_card.dart'; @@ -65,6 +69,11 @@ class _Card extends StatelessWidget { Widget build(BuildContext context) { var isReady = context.watch().state.isFullyInitilized; var changeTab = context.read().onPress; + + var serviceState = context.watch().state; + var jobsCubit = context.watch(); + var hasSwitcher = switchableServices.contains(serviceType); + return GestureDetector( onTap: isReady ? () => showDialog( @@ -87,9 +96,43 @@ class _Card extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - IconStatusMask( - status: isReady ? StateType.stable : StateType.uninitialized, - child: Icon(serviceType.icon, size: 30, color: Colors.white), + Row( + children: [ + IconStatusMask( + status: isReady ? StateType.stable : StateType.uninitialized, + child: Icon(serviceType.icon, size: 30, color: Colors.white), + ), + if (hasSwitcher) ...[ + Spacer(), + Builder( + builder: (context) { + late bool isActive; + var jobState = jobsCubit.state; + if (jobState is JobsStateWithJobs && + jobState.jobList.any((el) => + el is ServiceToggleJob && + el.type == serviceType)) { + isActive = (jobState.jobList.firstWhere((el) => + el is ServiceToggleJob && + el.type == serviceType) as ServiceToggleJob) + .needToTurnOn; + } else { + isActive = serviceState.isEnableByType(serviceType); + } + + return BrandSwitch( + value: isActive, + onChanged: (value) => jobsCubit.createOrRemove( + ServiceToggleJob( + type: serviceType, + needToTurnOn: value, + ), + ), + ); + }, + ), + ] + ], ), SizedBox(height: 10), BrandText.h2(serviceType.title), @@ -306,6 +349,7 @@ class _ServiceDetails extends StatelessWidget { 'services.vpn.bottom_sheet.1'.tr(), ); } + return Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), diff --git a/lib/ui/pages/users/users.dart b/lib/ui/pages/users/users.dart index da994ef9..3088116d 100644 --- a/lib/ui/pages/users/users.dart +++ b/lib/ui/pages/users/users.dart @@ -6,7 +6,7 @@ import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart'; import 'package:selfprivacy/logic/cubit/forms/user/user_form_cubit.dart'; import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; -import 'package:selfprivacy/logic/models/jobs/job.dart'; +import 'package:selfprivacy/logic/models/job.dart'; import 'package:selfprivacy/logic/models/user.dart'; import 'package:selfprivacy/ui/components/brand_bottom_sheet/brand_bottom_sheet.dart'; import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; From 90d64d8f51127c92f7fca19f0a099f16ae4a0a84 Mon Sep 17 00:00:00 2001 From: Kherel Date: Thu, 2 Sep 2021 21:32:07 +0200 Subject: [PATCH 6/6] update --- assets/translations/en.json | 11 +- assets/translations/ru.json | 11 +- ios/Podfile.lock | 2 +- lib/config/get_it_config.dart | 3 +- lib/config/hive_config.dart | 16 +- lib/logic/api_maps/hetzner.dart | 2 +- lib/logic/api_maps/server.dart | 9 + lib/logic/common_enum/common_enum.dart | 4 +- lib/logic/cubit/jobs/jobs_cubit.dart | 28 ++- lib/logic/cubit/services/services_state.dart | 8 - lib/logic/get_it/ssh_helper.dart | 33 ++++ lib/logic/models/job.dart | 7 + lib/ui/components/brand_text/brand_text.dart | 8 +- lib/ui/pages/more/more.dart | 190 ++++++++++++++++--- lib/ui/pages/services/services.dart | 85 +++++++-- pubspec.lock | 58 ++++-- pubspec.yaml | 5 +- 17 files changed, 394 insertions(+), 86 deletions(-) create mode 100644 lib/logic/get_it/ssh_helper.dart diff --git a/assets/translations/en.json b/assets/translations/en.json index 2ee5202d..42178a89 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -26,7 +26,8 @@ "details": "Details", "no_data": "No data", "wait": "Wait", - "remove": "Remove" + "remove": "Remove", + "apply": "Apply" }, "more": { "_comment": "'More' tab", @@ -34,6 +35,9 @@ "about_project": "About us", "about_app": "About application", "onboarding": "Onboarding", + "create_ssh_key": "Create ssh key", + "generate_key": "Generate key", + "generate_key_text": "You can generate ssh key", "console": "Console", "about_app_page": { "text": "Application version v.{}" @@ -223,7 +227,8 @@ "createUser": "Create", "serviceTurnOff": "Turn off", "serviceTurnOn": "Turn on", - "jobAdded": "Job added" + "jobAdded": "Job added", + "runJobs": "Run jobs" }, "validations": { "required": "Required", @@ -233,4 +238,4 @@ "length": "Length is [] shoud be {}", "user_alredy_exist": "Already exists" } -} \ No newline at end of file +} diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 9fa3fc62..ac731421 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -26,7 +26,8 @@ "details": "Детальная информация", "no_data": "Нет данных", "wait": "Ожидайте", - "remove": "Удалить" + "remove": "Удалить", + "apply": "Подать" }, "more": { "_comment": "вкладка еще", @@ -35,6 +36,9 @@ "about_app": "О приложении", "onboarding": "Приветствие", "console": "Консоль", + "create_ssh_key": "Создать ssh ключ", + "generate_key": "Сгенерировать ключ", + "generate_key_text": "Вы сможете сгенерировать ключ", "about_app_page": { "text": "Версия приложения: v.{}" }, @@ -216,7 +220,8 @@ "createUser": "Создать запись", "serviceTurnOff": "Остановить", "serviceTurnOn": "Запустить", - "jobAdded": "Задача добавленна" + "jobAdded": "Задача добавленна", + "runJobs": "Запустите задачи" }, "validations": { "required": "обязательное поле", @@ -226,4 +231,4 @@ "length": "Длина строки [] должна быть {}", "user_alredy_exist": "Имя уже используется" } -} \ No newline at end of file +} diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 285337b4..373cec68 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -44,7 +44,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/wakelock/ios" SPEC CHECKSUMS: - Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c diff --git a/lib/config/get_it_config.dart b/lib/config/get_it_config.dart index 3f01e6d3..99b61cc8 100644 --- a/lib/config/get_it_config.dart +++ b/lib/config/get_it_config.dart @@ -2,6 +2,7 @@ import 'package:get_it/get_it.dart'; import 'package:selfprivacy/logic/get_it/api_config.dart'; import 'package:selfprivacy/logic/get_it/console.dart'; import 'package:selfprivacy/logic/get_it/navigation.dart'; +import 'package:selfprivacy/logic/get_it/ssh_helper.dart'; import 'package:selfprivacy/logic/get_it/timer.dart'; export 'package:selfprivacy/logic/get_it/api_config.dart'; @@ -9,7 +10,6 @@ export 'package:selfprivacy/logic/get_it/console.dart'; export 'package:selfprivacy/logic/get_it/navigation.dart'; export 'package:selfprivacy/logic/get_it/timer.dart'; - final getIt = GetIt.instance; Future getItSetup() async { @@ -17,6 +17,7 @@ Future getItSetup() async { getIt.registerSingleton(ConsoleModel()); getIt.registerSingleton(TimerModel()); + getIt.registerSingleton(SSHModel()..init()); getIt.registerSingleton(ApiConfigModel()..init()); await getIt.allReady(); diff --git a/lib/config/hive_config.dart b/lib/config/hive_config.dart index 0e6f1c5b..be47b879 100644 --- a/lib/config/hive_config.dart +++ b/lib/config/hive_config.dart @@ -22,19 +22,21 @@ class HiveConfig { await Hive.openBox(BNames.users); await Hive.openBox(BNames.servicesState); - var cipher = HiveAesCipher(await getEncriptedKey()); + var cipher = HiveAesCipher(await getEncriptedKey(BNames.key)); await Hive.openBox(BNames.appConfig, encryptionCipher: cipher); + var sshCipher = HiveAesCipher(await getEncriptedKey(BNames.sshEnckey)); + await Hive.openBox(BNames.sshConfig, encryptionCipher: sshCipher); } - static Future getEncriptedKey() async { + static Future getEncriptedKey(String encKey) async { final secureStorage = FlutterSecureStorage(); - var hasEncryptionKey = await secureStorage.containsKey(key: BNames.key); + var hasEncryptionKey = await secureStorage.containsKey(key: encKey); if (!hasEncryptionKey) { var key = Hive.generateSecureKey(); - await secureStorage.write(key: BNames.key, value: base64UrlEncode(key)); + await secureStorage.write(key: encKey, value: base64UrlEncode(key)); } - String? string = await secureStorage.read(key: BNames.key); + String? string = await secureStorage.read(key: encKey); return base64Url.decode(string!); } } @@ -49,6 +51,7 @@ class BNames { static String servicesState = 'servicesState'; static String key = 'key'; + static String sshEnckey = 'sshEngkey'; static String cloudFlareDomain = 'cloudFlareDomain'; static String hetznerKey = 'hetznerKey'; @@ -61,4 +64,7 @@ class BNames { static String isLoading = 'isLoading'; static String isServerResetedFirstTime = 'isServerResetedFirstTime'; static String isServerResetedSecondTime = 'isServerResetedSecondTime'; + static String sshConfig = 'sshConfig'; + static String sshPrivateKey = "sshPrivateKey"; + static String sshPublicKey = "sshPublicKey"; } diff --git a/lib/logic/api_maps/hetzner.dart b/lib/logic/api_maps/hetzner.dart index 1bd4d10d..2b714aad 100644 --- a/lib/logic/api_maps/hetzner.dart +++ b/lib/logic/api_maps/hetzner.dart @@ -94,7 +94,7 @@ class HetznerApi extends ApiMap { /// check the branch name, it could be "development" or "master". var data = jsonDecode( - '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "ssh_keys":["kherel"], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/development/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''); + '''{"name":"$domainName","server_type":"cx11","start_after_create":false,"image":"ubuntu-20.04", "volumes":[$dbId], "networks":[], "user_data":"#cloud-config\\nruncmd:\\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-21.05 DOMAIN=$domainName LUSER=${rootUser.login} PASSWORD=${rootUser.password} CF_TOKEN=$cloudFlareKey DB_PASSWORD=$dbPassword bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":true, "location": "fsn1"}'''); Response serverCreateResponse = await client.post( '/servers', diff --git a/lib/logic/api_maps/server.dart b/lib/logic/api_maps/server.dart index 7c729789..6e5686f6 100644 --- a/lib/logic/api_maps/server.dart +++ b/lib/logic/api_maps/server.dart @@ -96,6 +96,15 @@ class ServerApi extends ApiMap { client.post('/services/${type.url}/${needToTurnOn ? 'enable' : 'disable'}'); client.close(); } + + Future sendSsh(String ssh) async { + var client = await getClient(); + client.post( + '/services/ssh/enable', + data: {"public_key": ssh}, + ); + client.close(); + } } extension UrlServerExt on ServiceTypes { diff --git a/lib/logic/common_enum/common_enum.dart b/lib/logic/common_enum/common_enum.dart index b0ceb1f6..e3d81309 100644 --- a/lib/logic/common_enum/common_enum.dart +++ b/lib/logic/common_enum/common_enum.dart @@ -1,7 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; +import 'package:ionicons/ionicons.dart'; import 'package:selfprivacy/ui/components/brand_icons/brand_icons.dart'; -import 'package:unicons/unicons.dart'; enum InitializingSteps { setHeznerKey, @@ -86,7 +86,7 @@ extension ServiceTypesExt on ServiceTypes { case ServiceTypes.git: return BrandIcons.git; case ServiceTypes.vpn: - return UniconsLine.cloud_lock; + return Ionicons.shield_checkmark_outline; } } diff --git a/lib/logic/cubit/jobs/jobs_cubit.dart b/lib/logic/cubit/jobs/jobs_cubit.dart index 19ad09de..d9d902a9 100644 --- a/lib/logic/cubit/jobs/jobs_cubit.dart +++ b/lib/logic/cubit/jobs/jobs_cubit.dart @@ -4,6 +4,7 @@ import 'package:selfprivacy/config/get_it_config.dart'; import 'package:selfprivacy/logic/api_maps/server.dart'; import 'package:selfprivacy/logic/cubit/services/services_cubit.dart'; import 'package:selfprivacy/logic/cubit/users/users_cubit.dart'; +import 'package:selfprivacy/logic/get_it/ssh_helper.dart'; import 'package:selfprivacy/logic/models/job.dart'; import 'package:equatable/equatable.dart'; import 'package:selfprivacy/logic/models/user.dart'; @@ -40,7 +41,7 @@ class JobsCubit extends Cubit { emit(newState); } - void createOrRemove(ServiceToggleJob job) { + void createOrRemoveServiceToggleJob(ServiceToggleJob job) { var newJobsList = []; if (state is JobsStateWithJobs) { newJobsList.addAll((state as JobsStateWithJobs).jobList); @@ -53,6 +54,26 @@ class JobsCubit extends Cubit { removeJob(removingJob.id); } else { newJobsList.add(job); + getIt().showSnackBar(SnackBar( + content: Text('jobs.jobAdded'.tr()), + duration: const Duration(seconds: 2), + )); + emit(JobsStateWithJobs(newJobsList)); + } + } + + void createShhJobIfNotExist(CreateSSHKeyJob job) { + var newJobsList = []; + if (state is JobsStateWithJobs) { + newJobsList.addAll((state as JobsStateWithJobs).jobList); + } + var isExistInJobList = newJobsList.any((el) => el is CreateSSHKeyJob); + if (!isExistInJobList) { + newJobsList.add(job); + getIt().showSnackBar(SnackBar( + content: Text('jobs.jobAdded'.tr()), + duration: const Duration(seconds: 2), + )); emit(JobsStateWithJobs(newJobsList)); } } @@ -61,7 +82,6 @@ class JobsCubit extends Cubit { if (state is JobsStateWithJobs) { var jobs = (state as JobsStateWithJobs).jobList; emit(JobsStateLoading()); - var newUsers = []; for (var job in jobs) { if (job is CreateUserJob) { @@ -75,6 +95,10 @@ class JobsCubit extends Cubit { servicesCubit.turnOffList([job.type]); } } + if (job is CreateSSHKeyJob) { + await getIt().generateKeys(); + api.sendSsh(getIt().savedPubKey!); + } } usersCubit.addUsers(newUsers); diff --git a/lib/logic/cubit/services/services_state.dart b/lib/logic/cubit/services/services_state.dart index ed84ffa8..a41b5a59 100644 --- a/lib/logic/cubit/services/services_state.dart +++ b/lib/logic/cubit/services/services_state.dart @@ -1,13 +1,5 @@ part of 'services_cubit.dart'; -const switchableServices = [ - ServiceTypes.passwordManager, - ServiceTypes.cloud, - ServiceTypes.socialNetwork, - ServiceTypes.git, - ServiceTypes.vpn, -]; - class ServicesState extends Equatable { const ServicesState({ required this.isPasswordManagerEnable, diff --git a/lib/logic/get_it/ssh_helper.dart b/lib/logic/get_it/ssh_helper.dart new file mode 100644 index 00000000..46ded1a6 --- /dev/null +++ b/lib/logic/get_it/ssh_helper.dart @@ -0,0 +1,33 @@ +import 'dart:developer'; + +import 'package:hive/hive.dart'; +import 'package:pointycastle/pointycastle.dart'; +import 'package:rsa_encrypt/rsa_encrypt.dart'; +import 'package:selfprivacy/config/hive_config.dart'; +import 'package:pointycastle/api.dart' as crypto; +import 'package:ssh_key/ssh_key.dart' as ssh_key; + +class SSHModel { + Box _box = Hive.box(BNames.sshConfig); + String? savedPrivateKey; + String? savedPubKey; + + Future generateKeys() async { + var helper = RsaKeyHelper(); + crypto.AsymmetricKeyPair pair = + await helper.computeRSAKeyPair(helper.getSecureRandom()); + var privateKey = pair.privateKey as RSAPrivateKey; + var publicKey = pair.publicKey as RSAPublicKey; + + savedPrivateKey = helper.encodePrivateKeyToPemPKCS1(privateKey); + savedPubKey = publicKey.encode(ssh_key.PubKeyEncoding.openSsh); + + await _box.put(BNames.sshPrivateKey, savedPrivateKey); + await _box.put(BNames.sshPublicKey, savedPubKey); + } + + void init() { + savedPrivateKey = _box.get(BNames.sshPrivateKey); + savedPubKey = _box.get(BNames.sshPublicKey); + } +} diff --git a/lib/logic/models/job.dart b/lib/logic/models/job.dart index d1fb0193..c06d4ebd 100644 --- a/lib/logic/models/job.dart +++ b/lib/logic/models/job.dart @@ -45,3 +45,10 @@ class ServiceToggleJob extends Job { @override List get props => [id, title, type, needToTurnOn]; } + +class CreateSSHKeyJob extends Job { + CreateSSHKeyJob() : super(title: '${"more.create_ssh_key".tr()}'); + + @override + List get props => [id, title]; +} diff --git a/lib/ui/components/brand_text/brand_text.dart b/lib/ui/components/brand_text/brand_text.dart index 9f272782..beaf2937 100644 --- a/lib/ui/components/brand_text/brand_text.dart +++ b/lib/ui/components/brand_text/brand_text.dart @@ -52,10 +52,16 @@ class BrandText extends StatelessWidget { type: TextType.onboardingTitle, style: style, ); - factory BrandText.h2(String? text, {TextStyle? style}) => BrandText( + 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( diff --git a/lib/ui/pages/more/more.dart b/lib/ui/pages/more/more.dart index 4c1a2d9b..da9b116a 100644 --- a/lib/ui/pages/more/more.dart +++ b/lib/ui/pages/more/more.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:ionicons/ionicons.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; +import 'package:selfprivacy/config/text_themes.dart'; +import 'package:selfprivacy/logic/cubit/jobs/jobs_cubit.dart'; +import 'package:selfprivacy/logic/models/job.dart'; +import 'package:selfprivacy/logic/models/state_types.dart'; +import 'package:selfprivacy/ui/components/brand_button/brand_button.dart'; import 'package:selfprivacy/ui/components/brand_divider/brand_divider.dart'; import 'package:selfprivacy/ui/components/brand_header/brand_header.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/icon_status_mask/icon_status_mask.dart'; import 'package:selfprivacy/ui/pages/initializing/initializing.dart'; import 'package:selfprivacy/ui/pages/onboarding/onboarding.dart'; import 'package:selfprivacy/ui/pages/rootRoute.dart'; @@ -21,9 +28,14 @@ class MorePage extends StatelessWidget { @override Widget build(BuildContext context) { + var jobsCubit = context.watch(); + return Scaffold( appBar: PreferredSize( - child: BrandHeader(title: 'basis.more'.tr()), + child: BrandHeader( + title: 'basis.more'.tr(), + hasFlashButton: true, + ), preferredSize: Size.fromHeight(52), ), body: ListView( @@ -63,6 +75,25 @@ class MorePage extends StatelessWidget { iconData: BrandIcons.terminal, goTo: Console(), ), + _MoreMenuTapItem( + title: 'more.create_ssh_key'.tr(), + iconData: Ionicons.key_outline, + onTap: () { + showDialog( + context: context, + builder: (BuildContext context) { + return _MoreDetails( + title: 'more.create_ssh_key'.tr(), + icon: Ionicons.key_outline, + onTap: () { + jobsCubit.createShhJobIfNotExist(CreateSSHKeyJob()); + }, + text: 'more.generate_key_text'.tr(), + ); + }, + ); + }, + ), ], ), ) @@ -72,6 +103,73 @@ class MorePage extends StatelessWidget { } } +class _MoreDetails extends StatelessWidget { + const _MoreDetails({ + Key? key, + required this.icon, + required this.title, + required this.onTap, + required this.text, + }) : super(key: key); + final String title; + final IconData icon; + final Function onTap; + final String text; + @override + Widget build(BuildContext context) { + var textStyle = body1Style.copyWith( + color: Theme.of(context).brightness == Brightness.dark + ? Colors.white + : BrandColors.black); + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: SingleChildScrollView( + child: Container( + width: 350, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: paddingH15V30, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + IconStatusMask( + status: StateType.stable, + child: Icon(icon, size: 40, color: Colors.white), + ), + SizedBox(height: 10), + BrandText.h2(title), + SizedBox(height: 10), + Text( + text, + style: textStyle, + ), + SizedBox(height: 40), + Center( + child: Container( + child: BrandButton.rised( + onPressed: () { + Navigator.of(context).pop(); + onTap(); + }, + text: 'more.generate_key'.tr(), + ), + ), + ), + ], + ), + ) + ], + ), + ), + ), + ); + } +} + class _NavItem extends StatelessWidget { const _NavItem({ Key? key, @@ -88,29 +186,73 @@ class _NavItem extends StatelessWidget { Widget build(BuildContext context) { return GestureDetector( onTap: () => Navigator.of(context).push(materialRoute(goTo)), - child: Container( - padding: EdgeInsets.symmetric(vertical: 24), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - width: 1.0, - color: BrandColors.dividerColor, - ), - ), - ), - child: Row( - children: [ - BrandText.body1(title), - Spacer(), - SizedBox( - width: 56, - child: Icon( - iconData, - size: 20, - ), - ), - ], - ), + child: _MoreMenuItem( + iconData: iconData, + title: title, + ), + ); + } +} + +class _MoreMenuTapItem extends StatelessWidget { + const _MoreMenuTapItem({ + Key? key, + required this.iconData, + required this.onTap, + required this.title, + }) : super(key: key); + + final IconData iconData; + final Function onTap; + final String title; + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + onTap(); + }, + child: _MoreMenuItem( + iconData: iconData, + title: title, + ), + ); + } +} + +class _MoreMenuItem extends StatelessWidget { + const _MoreMenuItem({ + Key? key, + required this.iconData, + required this.title, + }) : super(key: key); + + final IconData iconData; + final String title; + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric(vertical: 24), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1.0, + color: BrandColors.dividerColor, + ), + ), + ), + child: Row( + children: [ + BrandText.body1(title), + Spacer(), + SizedBox( + width: 56, + child: Icon( + iconData, + size: 20, + ), + ), + ], ), ); } diff --git a/lib/ui/pages/services/services.dart b/lib/ui/pages/services/services.dart index 3a1de59b..0ba92220 100644 --- a/lib/ui/pages/services/services.dart +++ b/lib/ui/pages/services/services.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:flutter/material.dart'; import 'package:selfprivacy/config/brand_colors.dart'; import 'package:selfprivacy/config/brand_theme.dart'; @@ -21,6 +23,14 @@ import 'package:url_launcher/url_launcher.dart'; import '../rootRoute.dart'; +const switchableServices = [ + ServiceTypes.passwordManager, + ServiceTypes.cloud, + ServiceTypes.socialNetwork, + ServiceTypes.git, + ServiceTypes.vpn, +]; + class ServicesPage extends StatefulWidget { ServicesPage({Key? key}) : super(key: key); @@ -72,10 +82,20 @@ class _Card extends StatelessWidget { var serviceState = context.watch().state; var jobsCubit = context.watch(); - var hasSwitcher = switchableServices.contains(serviceType); + var jobState = jobsCubit.state; + + var switchebleService = switchableServices.contains(serviceType); + var hasSwitchJob = switchebleService && + jobState is JobsStateWithJobs && + jobState.jobList + .any((el) => el is ServiceToggleJob && el.type == serviceType); + + var isSwithOn = isReady && + (!switchableServices.contains(serviceType) || + serviceState.isEnableByType(serviceType)); return GestureDetector( - onTap: isReady + onTap: isSwithOn ? () => showDialog( context: context, // isScrollControlled: true, @@ -84,7 +104,7 @@ class _Card extends StatelessWidget { return _ServiceDetails( serviceType: serviceType, status: - isReady ? StateType.stable : StateType.uninitialized, + isSwithOn ? StateType.stable : StateType.uninitialized, title: serviceType.title, icon: serviceType.icon, changeTab: changeTab, @@ -99,22 +119,21 @@ class _Card extends StatelessWidget { Row( children: [ IconStatusMask( - status: isReady ? StateType.stable : StateType.uninitialized, + status: + isSwithOn ? StateType.stable : StateType.uninitialized, child: Icon(serviceType.icon, size: 30, color: Colors.white), ), - if (hasSwitcher) ...[ + if (isReady && switchebleService) ...[ Spacer(), Builder( builder: (context) { late bool isActive; - var jobState = jobsCubit.state; - if (jobState is JobsStateWithJobs && - jobState.jobList.any((el) => - el is ServiceToggleJob && - el.type == serviceType)) { - isActive = (jobState.jobList.firstWhere((el) => - el is ServiceToggleJob && - el.type == serviceType) as ServiceToggleJob) + if (hasSwitchJob) { + isActive = ((jobState as JobsStateWithJobs) + .jobList + .firstWhere((el) => + el is ServiceToggleJob && + el.type == serviceType) as ServiceToggleJob) .needToTurnOn; } else { isActive = serviceState.isEnableByType(serviceType); @@ -122,7 +141,8 @@ class _Card extends StatelessWidget { return BrandSwitch( value: isActive, - onChanged: (value) => jobsCubit.createOrRemove( + onChanged: (value) => + jobsCubit.createOrRemoveServiceToggleJob( ServiceToggleJob( type: serviceType, needToTurnOn: value, @@ -134,11 +154,38 @@ class _Card extends StatelessWidget { ] ], ), - SizedBox(height: 10), - BrandText.h2(serviceType.title), - SizedBox(height: 10), - BrandText.body2(serviceType.subtitle), - SizedBox(height: 10), + ClipRect( + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 10), + BrandText.h2(serviceType.title), + SizedBox(height: 10), + BrandText.body2(serviceType.subtitle), + SizedBox(height: 10), + ], + ), + if (hasSwitchJob) + Positioned( + bottom: 30, + left: 0, + right: 0, + child: BackdropFilter( + filter: ImageFilter.blur( + sigmaX: 3, + sigmaY: 2, + ), + child: BrandText.h2( + 'jobs.runJobs'.tr(), + textAlign: TextAlign.center, + ), + ), + ) + ], + ), + ) ], ), ), diff --git a/pubspec.lock b/pubspec.lock index 48ca09b2..944a7ea6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "1.7.1" + version: "1.7.2" archive: dependency: transitive description: @@ -29,13 +29,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.0" + asn1lib: + dependency: transitive + description: + name: asn1lib + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.8.1" basic_utils: dependency: "direct main" description: @@ -126,7 +133,7 @@ packages: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.1" checked_yaml: dependency: transitive description: @@ -489,7 +496,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.7.0" mime: dependency: transitive description: @@ -610,7 +617,7 @@ packages: source: hosted version: "2.0.1" pointycastle: - dependency: transitive + dependency: "direct main" description: name: pointycastle url: "https://pub.dartlang.org" @@ -658,6 +665,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + quiver: + dependency: transitive + description: + name: quiver + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.1" + rsa_encrypt: + dependency: "direct main" + description: + name: rsa_encrypt + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" share_plus: dependency: "direct main" description: @@ -810,6 +831,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.1" + ssh_key: + dependency: "direct main" + description: + name: ssh_key + url: "https://pub.dartlang.org" + source: hosted + version: "0.7.0" stack_trace: dependency: transitive description: @@ -851,21 +879,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.16.8" + version: "1.17.10" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.4.2" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.3.19" + version: "0.4.0" timing: dependency: transitive description: @@ -873,6 +901,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.0" + tuple: + dependency: transitive + description: + name: tuple + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" typed_data: dependency: transitive description: @@ -880,13 +915,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" - unicons: - dependency: "direct main" - description: - name: unicons - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" url_launcher: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 2ea2e7c0..e3ed9839 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,10 +31,13 @@ dependencies: pretty_dio_logger: ^1.1.1 provider: ^6.0.0 share_plus: ^2.1.4 - unicons: ^2.0.1 url_launcher: ^6.0.2 wakelock: ^0.5.0+2 basic_utils: ^3.4.0 + ionicons: ^0.1.2 + pointycastle: ^3.3.2 + rsa_encrypt: ^2.0.0 + ssh_key: ^0.7.0 dev_dependencies: flutter_test: