Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 354 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 608 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 906 B |
Before Width: | Height: | Size: 992 B After Width: | Height: | Size: 525 B |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 907 B |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 608 B |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 2.2 KiB |
@ -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'; |
||||
} |
@ -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)); |
||||
} |
||||
} |
||||
|
@ -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(); |
||||
} |
||||
} |
@ -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()); |
||||
} |