fdroid
Kherel 2 years ago
parent 6613949d12
commit 9a749cf006
  1. 2
      android/app/build.gradle
  2. 8
      android/app/src/main/AndroidManifest.xml
  3. BIN
      android/app/src/main/res/mipmap-hdpi/launcher_icon.png
  4. BIN
      android/app/src/main/res/mipmap-mdpi/launcher_icon.png
  5. BIN
      android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
  6. BIN
      android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
  7. BIN
      android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
  8. BIN
      assets/images/icon/logo.png
  9. BIN
      assets/images/icon/logo_android.png
  10. BIN
      assets/images/icon/logo_ios.png
  11. 6
      ios/Podfile.lock
  12. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
  13. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
  14. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
  15. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
  16. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
  17. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
  18. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
  19. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
  20. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
  21. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
  22. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
  23. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
  24. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
  25. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
  26. BIN
      ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
  27. 23
      lib/config/bloc_config.dart
  28. 20
      lib/config/brand_colors.dart
  29. 6
      lib/config/brand_theme.dart
  30. 53
      lib/config/hive_config.dart
  31. 8
      lib/config/text_themes.dart
  32. 11
      lib/logic/api_maps/api_map.dart
  33. 127
      lib/logic/api_maps/cloud_flare.dart
  34. 64
      lib/logic/api_maps/hetzner.dart
  35. 86
      lib/logic/cubit/app_config/app_config_cubit.dart
  36. 76
      lib/logic/cubit/app_config/app_config_state.dart
  37. 34
      lib/logic/cubit/app_settings/app_settings_cubit.dart
  38. 10
      lib/logic/cubit/app_settings/app_settings_state.dart
  39. 52
      lib/logic/cubit/forms/initializing/cloudflare_form_cubit.dart
  40. 60
      lib/logic/cubit/forms/initializing/domain_form_cubit.dart
  41. 38
      lib/logic/cubit/forms/initializing/hetzner_form_cubit.dart
  42. 56
      lib/logic/cubit/forms/initializing/user_form_cubit.dart
  43. 51
      lib/logic/cubit/forms/user/user.dart
  44. 13
      lib/logic/cubit/forms/validations/validations.dart
  45. 30
      lib/logic/cubit/initializing/initializing_cubit.dart
  46. 26
      lib/logic/cubit/initializing/initializing_state.dart
  47. 37
      lib/logic/cubit/users/users_cubit.dart
  48. 2
      lib/logic/cubit/users/users_state.dart
  49. 19
      lib/logic/models/cloudflare_domain.dart
  50. 44
      lib/logic/models/cloudflare_domain.g.dart
  51. 36
      lib/logic/models/config.dart
  52. 25
      lib/logic/models/dns_records.dart
  53. 17
      lib/logic/models/dns_records.g.dart
  54. 24
      lib/logic/models/server_details.dart
  55. 47
      lib/logic/models/server_details.g.dart
  56. 14
      lib/logic/models/user.dart
  57. 44
      lib/logic/models/user.g.dart
  58. 19
      lib/main.dart
  59. 44
      lib/ui/components/brand_card/brand_card.dart
  60. 21
      lib/ui/components/brand_tab_bar/brand_tab_bar.dart
  61. 42
      lib/ui/components/brand_text/brand_text.dart
  62. 4
      lib/ui/components/icon_status_mask/icon_status_mask.dart
  63. 19
      lib/ui/components/not_ready_card/not_ready_card.dart
  64. 105
      lib/ui/components/progress_bar/progress_bar.dart
  65. 610
      lib/ui/pages/initializing/initializing.dart
  66. 74
      lib/ui/pages/more/app_settings/app_setting.dart
  67. 7
      lib/ui/pages/more/more.dart
  68. 8
      lib/ui/pages/onboarding/onboarding.dart
  69. 25
      lib/ui/pages/providers/providers.dart
  70. 25
      lib/ui/pages/rootRoute.dart
  71. 10
      lib/ui/pages/services/services.dart
  72. 37
      lib/ui/pages/users/empty.dart
  73. 34
      lib/ui/pages/users/fab.dart
  74. 74
      lib/ui/pages/users/new_user.dart
  75. 40
      lib/ui/pages/users/user.dart
  76. 159
      lib/ui/pages/users/user_details.dart
  77. 339
      lib/ui/pages/users/users.dart
  78. 10
      lib/utils/crypto.dart
  79. 321
      pubspec.lock
  80. 14
      pubspec.yaml

@ -39,7 +39,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "pro.kherel.selfprivacy"
minSdkVersion 16
minSdkVersion 18
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

@ -15,11 +15,9 @@
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
android:windowSoftInputMode="adjustResize"
android:allowBackup="false" >
<!-- https://github.com/mogol/flutter_secure_storage allowBackup="false" -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@ -1,5 +1,7 @@
PODS:
- Flutter (1.0.0)
- flutter_secure_storage (3.3.1):
- Flutter
- package_info (0.0.1):
- Flutter
- path_provider (0.0.1):
@ -11,6 +13,7 @@ PODS:
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- package_info (from `.symlinks/plugins/package_info/ios`)
- path_provider (from `.symlinks/plugins/path_provider/ios`)
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
@ -19,6 +22,8 @@ DEPENDENCIES:
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
package_info:
:path: ".symlinks/plugins/package_info/ios"
path_provider:
@ -30,6 +35,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 B

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 906 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 992 B

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 907 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:selfprivacy/logic/cubit/app_settings/app_settings_cubit.dart';
import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_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';
@ -14,14 +13,22 @@ class BlocAndProviderConfig extends StatelessWidget {
@override
Widget build(BuildContext context) {
var platformBrightness =
SchedulerBinding.instance.window.platformBrightness;
var isDark = platformBrightness == Brightness.dark;
// var platformBrightness = Brightness.dark;
// var platformBrightness =
// SchedulerBinding.instance.window.platformBrightness;
// var isDark = platformBrightness == Brightness.dark;
var isDark = false;
return MultiProvider(
providers: [
BlocProvider(create: (_) => AppSettingsCubit(isDarkModeOn: isDark)),
BlocProvider(create: (_) => InitializingCubit()),
BlocProvider(
create: (_) => AppSettingsCubit(
isDarkModeOn: isDark,
isOnbordingShowing: true,
)..load(),
),
BlocProvider(
create: (_) => AppConfigCubit()..load(),
),
BlocProvider(create: (_) => ServicesCubit()),
BlocProvider(create: (_) => ProvidersCubit()),
BlocProvider(create: (_) => UsersCubit()),

@ -1,33 +1,23 @@
import 'package:flutter/material.dart';
class BrandColors {
/// ![](https://www.colorhexa.com/093CEF.png)
static const Color blue = Color(0xFF093CEF);
static const Color white = Colors.white;
static const Color black = Colors.black;
/// ![](https://www.colorhexa.com/555555.png)
static const Color gray1 = Color(0xFF555555);
/// ![](https://www.colorhexa.com/7C7C7C.png)
static const Color gray2 = Color(0xFF7C7C7C);
/// ![](https://www.colorhexa.com/fafafa.png)
static const Color gray3 = Color(0xFFFAFAFA);
/// ![](https://www.colorhexa.com/DDDDDD.png)
static const Color gray4 = Color(0xFFDDDDDD);
/// ![](https://www.colorhexa.com/EDEEF1.png)
static const Color gray5 = Color(0xFFEDEEF1);
static Color gray6 = Color(0xFF181818).withOpacity(0.7);
static const Color grey7 = Color(0xFFABABAB);
/// ![](https://www.colorhexa.com/FA0E0E.png)
static const Color red = Color(0xFFFA0E0E);
static const Color red1 = Color(0xFFFA0E0E);
static const Color red2 = Color(0xFFE65527);
/// ![](https://www.colorhexa.com/00AF54.png)
static const Color green1 = Color(0xFF00AF54);
/// ![](https://www.colorhexa.com/0F8849.png)
static const Color green2 = Color(0xFF0F8849);
static get navBackgroundLight => white.withOpacity(0.8);
@ -60,5 +50,5 @@ class BrandColors {
static const textColor1 = black;
static const textColor2 = gray1;
static const dividerColor = gray5;
static const warning = red;
static const warning = red1;
}

@ -23,20 +23,20 @@ final ligtTheme = ThemeData(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: BrandColors.red,
color: BrandColors.red1,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(4)),
borderSide: BorderSide(
width: 1,
color: BrandColors.red,
color: BrandColors.red1,
),
),
errorStyle: GoogleFonts.inter(
textStyle: TextStyle(
fontSize: 12,
color: BrandColors.red,
color: BrandColors.red1,
),
),
),

@ -0,0 +1,53 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
import 'package:selfprivacy/logic/models/server_details.dart';
import 'package:selfprivacy/logic/models/user.dart';
class HiveConfig {
static Future<void> init() async {
await Hive.initFlutter();
Hive.registerAdapter(UserAdapter());
Hive.registerAdapter(HetznerServerDetailsAdapter());
Hive.registerAdapter(CloudFlareDomainAdapter());
await Hive.openBox(BNames.appSettings);
var cipher = HiveAesCipher(await getEncriptedKey());
await Hive.openBox(BNames.appConfig, encryptionCipher: cipher);
}
static Future<Uint8List> getEncriptedKey() async {
final FlutterSecureStorage secureStorage = FlutterSecureStorage();
var containsEncryptionKey =
await secureStorage.containsKey(key: BNames.key);
if (!containsEncryptionKey) {
var key = Hive.generateSecureKey();
await secureStorage.write(key: BNames.key, value: base64UrlEncode(key));
}
return base64Url.decode(await secureStorage.read(key: BNames.key));
}
}
class BNames {
static String appConfig = 'appConfig';
static String isDarkModeOn = 'isDarkModeOn';
static String isOnbordingShowing = 'isOnbordingShowing';
static String appSettings = 'appSettings';
static String key = 'key';
static String domain = 'domain';
static String hetznerKey = 'hetznerKey';
static String cloudFlareKey = 'cloudFlareKey';
static String rootUser = 'rootUser';
static String server = 'server';
static String isDnsCheckedAndDkimSet = 'isDnsCheckedAndDkimSet';
static String serverInitStart = 'serverInitStart';
}

@ -18,6 +18,12 @@ final headline1Style = GoogleFonts.inter(
);
final headline2Style = GoogleFonts.inter(
fontSize: 24,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
);
final onboardingTitle = GoogleFonts.inter(
fontSize: 30,
fontWeight: NamedFontWeight.extraBold,
color: BrandColors.headlineColor,
@ -40,6 +46,8 @@ final body2Style = defaultTextStyle.copyWith(
color: BrandColors.textColor2,
);
final mediumStyle = defaultTextStyle.copyWith(fontSize: 13, height: 1.53);
final smallStyle = defaultTextStyle.copyWith(fontSize: 11, height: 1.45);
final linkStyle = defaultTextStyle.copyWith(color: BrandColors.blue);

@ -0,0 +1,11 @@
import 'package:dio/dio.dart';
abstract class ApiMap {
String rootAddress;
Dio client = Dio();
void close() {
client.close();
}
}

@ -0,0 +1,127 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart';
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
import 'package:selfprivacy/logic/models/dns_records.dart';
class CloudflareApi extends ApiMap {
CloudflareApi([String token]) {
if (token != null) {
client.options = BaseOptions(headers: {'Authorization': 'Bearer $token'});
}
}
@override
String rootAddress = 'https://api.cloudflare.com/client/v4';
Future<bool> isValid(String token) async {
var url = '$rootAddress/user/tokens/verify';
var options = Options(
headers: {'Authorization': 'Bearer $token'},
validateStatus: (status) {
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
},
);
Response response = await client.get(url, options: options);
if (response.statusCode == HttpStatus.ok) {
return true;
} else if (response.statusCode == HttpStatus.unauthorized) {
return false;
} else {
throw Exception('something bad happend');
}
}
Future<String> getZoneId(String token, String domain) async {
var url = '$rootAddress/zones';
var options = Options(
headers: {'Authorization': 'Bearer $token'},
validateStatus: (status) {
return status == HttpStatus.ok || status == HttpStatus.forbidden;
},
);
Response response = await client.get(
url,
options: options,
queryParameters: {'name': domain},
);
try {
return response.data['result'][0]['id'];
} catch (error) {
return null;
}
}
Future<void> createMultipleDnsRecords({
String ip4,
CloudFlareDomain cloudFlareDomain,
}) async {
var domainName = cloudFlareDomain.name;
var domainZoneId = cloudFlareDomain.zoneId;
var domainA = DnsRecords(type: 'A', name: domainName, content: ip4);
var apiA = DnsRecords(type: 'A', name: 'api', content: ip4);
var cloudA = DnsRecords(type: 'A', name: 'cloud', content: ip4);
var gitA = DnsRecords(type: 'A', name: 'git', content: ip4);
var meetA = DnsRecords(type: 'A', name: 'meet', content: ip4);
var passwordA = DnsRecords(type: 'A', name: 'password', content: ip4);
var socialA = DnsRecords(type: 'A', name: 'social', content: ip4);
var mx = DnsRecords(type: 'MX', name: '@', content: domainName);
var txt1 = DnsRecords(
type: 'TXT',
name: '_dmarc',
content: 'v=DMARC1; p=none',
ttl: 18000,
);
var txt2 = DnsRecords(
type: 'TXT',
name: cloudFlareDomain.name,
content: 'v=spf1 a mx ip4:$ip4 -all',
ttl: 18000,
);
var listDnsRecords = <DnsRecords>[
domainA,
apiA,
cloudA,
gitA,
meetA,
passwordA,
socialA,
mx,
txt1,
txt2
];
var allFutures = <Future>[];
for (var record in listDnsRecords) {
var url = '$rootAddress/zones/$domainZoneId/dns_records';
allFutures.add(
client.post(
url,
data: record.toJson(),
),
);
}
await Future.wait(allFutures);
}
// createSDKIM(String dkim) {
// var txt3 = DnsRecords(
// type: 'TXT',
// name: 'selector._domainkey',
// content: dkim,
// ttl: 18000,
// );
// }
}

@ -0,0 +1,64 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:selfprivacy/logic/api_maps/api_map.dart';
import 'package:selfprivacy/logic/models/server_details.dart';
import 'package:selfprivacy/logic/models/user.dart';
class HetznerApi extends ApiMap {
HetznerApi([String token]) {
if (token != null) {
client.options = BaseOptions(headers: {'Authorization': 'Bearer $token'});
}
}
@override
String rootAddress = 'https://api.hetzner.cloud/v1/servers';
Future<bool> isValid(String token) async {
var options = Options(
headers: {'Authorization': 'Bearer $token'},
validateStatus: (status) {
return status == HttpStatus.ok || status == HttpStatus.unauthorized;
},
);
Response response = await client.get(rootAddress, options: options);
if (response.statusCode == HttpStatus.ok) {
return true;
} else if (response.statusCode == HttpStatus.unauthorized) {
return false;
} else {
throw Exception('something bad happend');
}
}
Future<HetznerServerDetails> createServer({
@required User rootUser,
@required String domainName,
}) async {
var data = {
"name": "selfprivacy-server",
"server_type": "cx11",
"start_after_create": true,
"image": "ubuntu-20.04",
"ssh_keys": [],
"volumes": [],
"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-20.09 DOMAIN=$domainName USER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} bash 2>&1 | tee /tmp/infect.log \nruncmd:\n- curl https://git.selfprivacy.org/ilchub/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.09 DOMAIN=$domainName USER=${rootUser.login} PASSWORD=${rootUser.password} HASHED_PASSWORD=${rootUser.hashPassword} bash 2>&1 | tee /tmp/infect.log",
};
Response response = await client.post(
rootAddress,
data: data,
);
return HetznerServerDetails(
id: response.data['server']['id'],
ip4: response.data['server']['public_net']['ipv4']['ip'],
serverInitializaionDateTime: DateTime.now(),
);
}
}

@ -0,0 +1,86 @@
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/api_maps/cloud_flare.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
import 'package:selfprivacy/logic/models/server_details.dart';
import 'package:selfprivacy/logic/models/user.dart';
part 'app_config_state.dart';
class AppConfigCubit extends Cubit<AppConfigState> {
AppConfigCubit() : super(InitialAppConfigState());
Box box = Hive.box(BNames.appConfig);
void load() {
emit(
state.copyWith(
hetznerKey: box.get(BNames.hetznerKey),
cloudFlareKey: box.get(BNames.cloudFlareKey),
domain: box.get(BNames.domain),
rootUser: box.get(BNames.rootUser),
hetznerServer: box.get(BNames.server),
isDnsCheckedAndDkimSet: box.get(BNames.isDnsCheckedAndDkimSet),
),
);
}
void reset() {
box.clear();
emit(InitialAppConfigState());
}
void setHetznerKey(String key) {
box.put(BNames.hetznerKey, key);
emit(state.copyWith(hetznerKey: key));
}
void setCloudFlare(String cloudFlareKey) {
box.put(BNames.cloudFlareKey, cloudFlareKey);
emit(state.copyWith(cloudFlareKey: cloudFlareKey));
}
void setDomain(CloudFlareDomain domain) {
box.put(BNames.domain, domain);
emit(state.copyWith(domain: domain));
}
void setRootUser(User rootUser) {
box.put(BNames.rootUser, rootUser);
emit(state.copyWith(rootUser: rootUser));
}
void setIsDnsCheckedAndDkimSet() {
box.put(BNames.isDnsCheckedAndDkimSet, true);
emit(state.copyWith(isDnsCheckedAndDkimSet: true));
}
void createServer() async {
emit(state.copyWith(isLoading: true));
var hetznerApi = HetznerApi(state.hetznerKey);
var cloudflareApi = CloudflareApi(state.cloudFlareKey);
var serverDetails = await hetznerApi.createServer(
rootUser: state.rootUser,
domainName: state.cloudFlareDomain.name,
);
cloudflareApi
.createMultipleDnsRecords(
ip4: serverDetails.ip4,
cloudFlareDomain: state.cloudFlareDomain,
)
.then((_) => cloudflareApi.close());
await box.put(BNames.server, serverDetails);
hetznerApi.close();
emit(state.copyWith(
isLoading: false,
hetznerServer: serverDetails,
));
}
}

@ -0,0 +1,76 @@
part of 'app_config_cubit.dart';
class AppConfigState extends Equatable {
const AppConfigState({
this.hetznerKey,
this.cloudFlareKey,
this.cloudFlareDomain,
this.rootUser,
this.server,
this.isDnsCheckedAndDkimSet = false,
this.isLoading = false,
});
@override
List<Object> get props => [
hetznerKey,
cloudFlareKey,
cloudFlareDomain,
rootUser,
server,
isDnsCheckedAndDkimSet,
isLoading,
];
final String hetznerKey;
final String cloudFlareKey;
final CloudFlareDomain cloudFlareDomain;
final User rootUser;
final HetznerServerDetails server;
final bool isDnsCheckedAndDkimSet;
final isLoading;
AppConfigState copyWith({
String hetznerKey,
String cloudFlareKey,
CloudFlareDomain domain,
User rootUser,
HetznerServerDetails hetznerServer,
bool isDnsCheckedAndDkimSet,
bool isLoading,
DateTime serverInitStart,
}) =>
AppConfigState(
hetznerKey: hetznerKey ?? this.hetznerKey,
cloudFlareKey: cloudFlareKey ?? this.cloudFlareKey,
cloudFlareDomain: domain ?? this.cloudFlareDomain,
rootUser: rootUser ?? this.rootUser,
server: hetznerServer ?? this.server,
isDnsCheckedAndDkimSet: isDnsCheckedAndDkimSet ?? this.isDnsCheckedAndDkimSet,
isLoading: isLoading ?? this.isLoading,
);
bool get isHetznerFilled => hetznerKey != null;
bool get isCloudFlareFilled => cloudFlareKey != null;
bool get isDomainFilled => cloudFlareDomain != null;
bool get isUserFilled => rootUser != null;
bool get isServerFilled => server != null;
bool get isFullyInitilized => _fulfilementList.every((el) => el);
int get progress => _fulfilementList.where((el) => el).length;
List<bool> get _fulfilementList => [
isHetznerFilled,
isCloudFlareFilled,
isDomainFilled,
isUserFilled,
isServerFilled,
isDnsCheckedAndDkimSet,
];
}
class InitialAppConfigState extends AppConfigState {
InitialAppConfigState() : super();
}

@ -1,18 +1,44 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/widgets.dart';
import 'package:hive/hive.dart';
import 'package:selfprivacy/config/hive_config.dart';
export 'package:provider/provider.dart';
part 'app_settings_state.dart';
class AppSettingsCubit extends Cubit<AppSettingsState> {
AppSettingsCubit({
bool isDarkModeOn,
@required bool isDarkModeOn,
@required bool isOnbordingShowing,
}) : super(
AppSettingsState(isDarkModeOn: isDarkModeOn),
AppSettingsState(
isDarkModeOn: isDarkModeOn,
isOnbordingShowing: isOnbordingShowing,
),
);
void update({@required bool isDarkModeOn}) {
emit(AppSettingsState(isDarkModeOn: isDarkModeOn));
Box box = Hive.box(BNames.appSettings);
void load() {
bool isDarkModeOn = box.get(BNames.isDarkModeOn);
bool isOnbordingShowing = box.get(BNames.isOnbordingShowing);
emit(state.copyWith(
isDarkModeOn: isDarkModeOn,
isOnbordingShowing: isOnbordingShowing,
));
}
void updateDarkMode({@required bool isDarkModeOn}) {
box.put(BNames.isDarkModeOn, isDarkModeOn);
emit(state.copyWith(isDarkModeOn: isDarkModeOn));
}
void turnOffOnboarding() {
box.put(BNames.isOnbordingShowing, false);
emit(state.copyWith(isOnbordingShowing: false));
}
}

@ -3,10 +3,18 @@ part of 'app_settings_cubit.dart';
class AppSettingsState extends Equatable {
const AppSettingsState({
@required this.isDarkModeOn,
@required this.isOnbordingShowing,
});
final bool isDarkModeOn;
final bool isOnbordingShowing;
AppSettingsState copyWith({isDarkModeOn, isOnbordingShowing}) =>
AppSettingsState(
isDarkModeOn: isDarkModeOn ?? this.isDarkModeOn,
isOnbordingShowing: isOnbordingShowing ?? this.isOnbordingShowing,
);
@override
List<Object> get props => [isDarkModeOn];
List<Object> get props => [isDarkModeOn, isOnbordingShowing];
}

@ -0,0 +1,52 @@
import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/cloud_flare.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
class CloudFlareFormCubit extends FormCubit {
CloudflareApi apiClient = CloudflareApi();
CloudFlareFormCubit(this.initializingCubit) {
var regExp = RegExp(r"\s+|[!$%^&*()_@+|~=`{}\[\]:<>?,.\/]");
apiKey = FieldCubit(
initalValue: '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
(s) => regExp.hasMatch(s), 'invalid key format'),
LegnthStringValidationWithLenghShowing(40, 'length is [] shoud be 40')
],
);
super.setFields([apiKey]);
}
@override
FutureOr<void> onSubmit() async {
initializingCubit.setCloudFlare(apiKey.state.value);
}
final AppConfigCubit initializingCubit;
FieldCubit<String> apiKey;
@override
FutureOr<bool> asyncValidation() async {
var isKeyValid = await apiClient.isValid(apiKey.state.value);
if (!isKeyValid) {
apiKey.setError('bad key');
return false;
}
return true;
}
@override
Future<void> close() async {
apiClient.close();
return super.close();
}
}

@ -0,0 +1,60 @@
import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/cloud_flare.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/models/cloudflare_domain.dart';
class DomainFormCubit extends FormCubit {
CloudflareApi apiClient = CloudflareApi();
DomainFormCubit(this.initializingCubit) {
var regExp =
RegExp(r"^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}");
domainName = FieldCubit(
initalValue: '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
(s) => !regExp.hasMatch(s), 'invalid domain format'),
],
);
super.setFields([domainName]);
}
@override
FutureOr<void> onSubmit() async {
var domain = CloudFlareDomain(
name: domainName.state.value,
zoneId: zoneId,
);
initializingCubit.setDomain(domain);
}
final AppConfigCubit initializingCubit;
FieldCubit<String> domainName;
String zoneId;
@override
FutureOr<bool> asyncValidation() async {
var key = initializingCubit.state.cloudFlareKey;
var zoneId = await apiClient.getZoneId(key, domainName.state.value);
if (zoneId == null) {
domainName.setError('Domain not in the list');
return false;
}
this.zoneId = zoneId;
return true;
}
@override
Future<void> close() async {
apiClient.close();
return super.close();
}
}

@ -1,18 +1,22 @@
import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/cubit/initializing/initializing_cubit.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
import 'package:selfprivacy/logic/cubit/forms/validations/validations.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
class HetznerFormCubit extends FormCubit {
HetznerApi apiClient = HetznerApi();
HetznerFormCubit(this.initializingCubit) {
var regExp = RegExp(r"\s+|[-!$%^&*()_@+|~=`{}\[\]:" ";<>?,.\/]");
var regExp = RegExp(r"\s+|[-!$%^&*()_@+|~=`{}\[\]:<>?,.\/]");
apiKey = FieldCubit(
initalValue: '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
(s) => regExp.hasMatch(s), 'invalid key format'),
LegnthStringValidation(11, 'length is [] shoud be 11')
LegnthStringValidationWithLenghShowing(64, 'length is [] shoud be 64')
],
);
@ -21,24 +25,28 @@ class HetznerFormCubit extends FormCubit {
@override
FutureOr<void> onSubmit() async {
print(apiKey.state.value);
await Future.delayed(const Duration(milliseconds: 300));
// initializingCubit.setHetznerKey(apiKey.state.value);
initializingCubit.setHetznerKey(apiKey.state.value);
}
final InitializingCubit initializingCubit;
final AppConfigCubit initializingCubit;
FieldCubit<String> apiKey;
}
class LegnthStringValidation extends ValidationModel<String> {
LegnthStringValidation(int length, String errorText)
: super((n) => n.length != length, errorText);
@override
FutureOr<bool> asyncValidation() async {
var isKeyValid = await apiClient.isValid(apiKey.state.value);
if (!isKeyValid) {
apiKey.setError('bad key');
return false;
}
return true;
}
@override
String check(String val) {
var length = val.length;
var errorMassage = this.errorMassage.replaceAll("[]", length.toString());
return test(val) ? errorMassage : null;
Future<void> close() async {
apiClient.close();
return super.close();
}
}

@ -0,0 +1,56 @@
import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/api_maps/hetzner.dart';
import 'package:selfprivacy/logic/cubit/app_config/app_config_cubit.dart';
import 'package:selfprivacy/logic/models/user.dart';
class UserFormCubit extends FormCubit {
HetznerApi apiClient = HetznerApi();
UserFormCubit(this.initializingCubit) {
var userRegExp = RegExp(r"\W");
var passwordRegExp = RegExp(r"[\n\r\s]+");
userName = FieldCubit(
initalValue: '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
(s) => userRegExp.hasMatch(s), 'invalid format'),
],
);
password = FieldCubit(
initalValue: '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
(s) => passwordRegExp.hasMatch(s), 'invalid format'),
],
);
super.setFields([userName, password]);
}
@override
FutureOr<void> onSubmit() async {
var user = User(
login: userName.state.value,
password: password.state.value,
);
initializingCubit.setRootUser(user);
}
final AppConfigCubit initializingCubit;
FieldCubit<String> userName;
FieldCubit<String> password;
@override
Future<void> close() async {
apiClient.close();
return super.close();
}
}

@ -0,0 +1,51 @@
import 'dart:async';
import 'package:cubit_form/cubit_form.dart';
import 'package:selfprivacy/logic/cubit/users/users_cubit.dart';
import 'package:selfprivacy/logic/models/user.dart';
class CloudFlareFormCubit extends FormCubit {
CloudFlareFormCubit({
this.userCubit,
User user,
}) {
var isEdit = user != null;
var userRegExp = RegExp(r"\W");
var passwordRegExp = RegExp(r"[\n\r\s]+");
login = FieldCubit(
initalValue: isEdit ? user.login : '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
(s) => userRegExp.hasMatch(s), 'invalid format'),
],
);
password = FieldCubit(
initalValue: isEdit ? user.password : '',
validations: [
RequiredStringValidation('required'),
ValidationModel<String>(
(s) => passwordRegExp.hasMatch(s), 'invalid format'),
],
);
super.setFields([login, password]);
}
@override
FutureOr<void> onSubmit() {
var user = User(
login: login.state.value,
password: password.state.value,
);
userCubit.add(user);
}
FieldCubit<String> login;
FieldCubit<String> password;
UsersCubit userCubit;
}

@ -0,0 +1,13 @@
import 'package:cubit_form/cubit_form.dart';
class LegnthStringValidationWithLenghShowing extends ValidationModel<String> {
LegnthStringValidationWithLenghShowing(int length, String errorText)
: super((n) => n.length != length, errorText);
@override
String check(String val) {
var length = val.length;
var errorMassage = this.errorMassage.replaceAll("[]", length.toString());
return test(val) ? errorMassage : null;
}
}

@ -1,30 +0,0 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/logic/models/config.dart';
import 'package:selfprivacy/logic/models/user.dart';
part 'initializing_state.dart';
class InitializingCubit extends Cubit<InitializingState> {
InitializingCubit() : super(InitialInitializingState());
void setHetznerKey(String key) {
var newCofig = state.appConfig.copyWith(hatzner: key);
emit(InitializingState(newCofig));
}
void setCloudFlare(String cloudFlareKey) {
var newCofig = state.appConfig.copyWith(cloudFlare: cloudFlareKey);
emit(InitializingState(newCofig));
}
void setDomain(String domain) {
var newCofig = state.appConfig.copyWith(domain: domain);
emit(InitializingState(newCofig));
}
void setRootUser(User rootUser) {
var newCofig = state.appConfig.copyWith(rootUser: rootUser);
emit(InitializingState(newCofig));
}
}

@ -1,26 +0,0 @@
part of 'initializing_cubit.dart';
class InitializingState extends Equatable {
const InitializingState(this.appConfig);
final AppConfig appConfig;
@override
List<Object> get props => [appConfig];
bool get isHatznerFilled => appConfig.hatzner != null;
bool get isCloudFlareFilled => appConfig.cloudFlare != null;
bool get isDomainFilled => appConfig.domain != null;
bool get isUserFilled => appConfig.rootUser != null;
bool get isFullyInitilized => _fulfilementList.every((el) => el);
int get progress => _fulfilementList.where((el) => el).length;
List<bool> get _fulfilementList =>
[isHatznerFilled, isCloudFlareFilled, isDomainFilled, isUserFilled];
}
class InitialInitializingState extends InitializingState {
InitialInitializingState() : super(AppConfig.empty());
}

@ -1,13 +1,12 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:selfprivacy/logic/models/user.dart';
import 'package:selfprivacy/utils/password_generator.dart';
export 'package:provider/provider.dart';
part 'users_state.dart';
class UsersCubit extends Cubit<UsersState> {
UsersCubit() : super(UsersState(initMockUsers));
UsersCubit() : super(UsersState([]));
void add(User user) {
var users = state.users;
@ -24,20 +23,20 @@ class UsersCubit extends Cubit<UsersState> {
}
}
final initMockUsers = <User>[
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()),
];
// final initMockUsers = <User>[
// User(login: 'Heartbreaking.Goose', password: genPass()),
// User(login: 'Alma.lawson', password: genPass()),
// User(login: 'Bee.gees', password: genPass()),
// User(login: 'Bim.jennings', password: genPass()),