mirror of
https://git.selfprivacy.org/kherel/selfprivacy.org.app.git
synced 2025-01-09 17:39:42 +00:00
Merge pull request 'Use the new NixOS infect' (#415) from nixos-23.11 into master
Reviewed-on: https://git.selfprivacy.org/SelfPrivacy/selfprivacy.org.app/pulls/415
This commit is contained in:
commit
985db9a479
|
@ -624,6 +624,10 @@
|
||||||
"use_staging_acme_description": "Applies when setting up a new server.",
|
"use_staging_acme_description": "Applies when setting up a new server.",
|
||||||
"ignore_tls": "Do not verify TLS certificates",
|
"ignore_tls": "Do not verify TLS certificates",
|
||||||
"ignore_tls_description": "App will not verify TLS certificates when connecting to the server.",
|
"ignore_tls_description": "App will not verify TLS certificates when connecting to the server.",
|
||||||
|
"allow_ssh_key_at_setup": "Allow setting a root SSH key during setup",
|
||||||
|
"allow_ssh_key_at_setup_description": "A button to add a key will appear on the confirmation screen.",
|
||||||
|
"add_root_ssh_key": "Add a root SSH key",
|
||||||
|
"root_ssh_key_added": "Root SSH key set and will be applied",
|
||||||
"routing": "App routing",
|
"routing": "App routing",
|
||||||
"reset_onboarding": "Reset onboarding switch",
|
"reset_onboarding": "Reset onboarding switch",
|
||||||
"reset_onboarding_description": "Reset onboarding switch to show onboarding screen again",
|
"reset_onboarding_description": "Reset onboarding switch to show onboarding screen again",
|
||||||
|
|
|
@ -44,7 +44,7 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get rootAddress => 'https://api.digitalocean.com/v2';
|
String get rootAddress => 'https://api.digitalocean.com/v2';
|
||||||
String get infectProviderName => 'digitalocean';
|
String get infectProviderName => 'DIGITALOCEAN';
|
||||||
|
|
||||||
Future<GenericResult<List>> getServers() async {
|
Future<GenericResult<List>> getServers() async {
|
||||||
List servers = [];
|
List servers = [];
|
||||||
|
@ -77,6 +77,7 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
required final String domainName,
|
required final String domainName,
|
||||||
required final String hostName,
|
required final String hostName,
|
||||||
required final String serverType,
|
required final String serverType,
|
||||||
|
required final String? customSshKey,
|
||||||
}) async {
|
}) async {
|
||||||
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
||||||
|
|
||||||
|
@ -90,10 +91,12 @@ class DigitalOceanApi extends RestApiMap {
|
||||||
'image': 'ubuntu-20-04-x64',
|
'image': 'ubuntu-20-04-x64',
|
||||||
'user_data': '#cloud-config\n'
|
'user_data': '#cloud-config\n'
|
||||||
'runcmd:\n'
|
'runcmd:\n'
|
||||||
'- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/providers/digital-ocean/nixos-infect | '
|
'- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | '
|
||||||
"PROVIDER=$infectProviderName DNS_PROVIDER_TYPE=$dnsProviderType STAGING_ACME='$stagingAcme' DOMAIN='$domainName' "
|
"API_TOKEN=$serverApiToken CONFIG_URL='https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-template/archive/master.tar.gz' "
|
||||||
"LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' CF_TOKEN=$dnsApiToken DB_PASSWORD=$databasePassword "
|
"DNS_PROVIDER_TOKEN=$dnsApiToken DNS_PROVIDER_TYPE=$dnsProviderType DOMAIN='$domainName' ENCODED_PASSWORD='$base64Password' "
|
||||||
'API_TOKEN=$serverApiToken HOSTNAME=$hostName bash 2>&1 | tee /tmp/infect.log',
|
"HOSTNAME=$hostName LUSER='${rootUser.login}' NIX_VERSION=2.18.1 PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' "
|
||||||
|
"${customSshKey != null ? "SSH_AUTHORIZED_KEY='$customSshKey'" : ""} "
|
||||||
|
'bash 2>&1 | tee /root/nixos-infect.log',
|
||||||
'region': region!,
|
'region': region!,
|
||||||
};
|
};
|
||||||
print('Decoded data: $data');
|
print('Decoded data: $data');
|
||||||
|
|
|
@ -45,7 +45,7 @@ class HetznerApi extends RestApiMap {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get rootAddress => 'https://api.hetzner.cloud/v1';
|
String get rootAddress => 'https://api.hetzner.cloud/v1';
|
||||||
String get infectProviderName => 'hetzner';
|
String get infectProviderName => 'HETZNER';
|
||||||
|
|
||||||
Future<GenericResult<List<HetznerServerInfo>>> getServers() async {
|
Future<GenericResult<List<HetznerServerInfo>>> getServers() async {
|
||||||
List<HetznerServerInfo> servers = [];
|
List<HetznerServerInfo> servers = [];
|
||||||
|
@ -83,6 +83,7 @@ class HetznerApi extends RestApiMap {
|
||||||
required final String hostName,
|
required final String hostName,
|
||||||
required final int volumeId,
|
required final int volumeId,
|
||||||
required final String serverType,
|
required final String serverType,
|
||||||
|
required final String? customSshKey,
|
||||||
}) async {
|
}) async {
|
||||||
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
final String stagingAcme = TlsOptions.stagingAcme ? 'true' : 'false';
|
||||||
Response? serverCreateResponse;
|
Response? serverCreateResponse;
|
||||||
|
@ -101,11 +102,12 @@ class HetznerApi extends RestApiMap {
|
||||||
'networks': [],
|
'networks': [],
|
||||||
'user_data': '#cloud-config\n'
|
'user_data': '#cloud-config\n'
|
||||||
'runcmd:\n'
|
'runcmd:\n'
|
||||||
'- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/providers/hetzner/nixos-infect | '
|
'- curl https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-infect/raw/branch/master/nixos-infect | '
|
||||||
"STAGING_ACME='$stagingAcme' PROVIDER=$infectProviderName DNS_PROVIDER_TYPE=$dnsProviderType "
|
"API_TOKEN=$serverApiToken CONFIG_URL='https://git.selfprivacy.org/SelfPrivacy/selfprivacy-nixos-template/archive/master.tar.gz' "
|
||||||
"NIX_CHANNEL=nixos-21.05 DOMAIN='$domainName' LUSER='${rootUser.login}' ENCODED_PASSWORD='$base64Password' "
|
"DNS_PROVIDER_TOKEN=$dnsApiToken DNS_PROVIDER_TYPE=$dnsProviderType DOMAIN='$domainName' ENCODED_PASSWORD='$base64Password' "
|
||||||
'CF_TOKEN=$dnsApiToken DB_PASSWORD=$databasePassword API_TOKEN=$serverApiToken HOSTNAME=$hostName bash 2>&1 | '
|
"HOSTNAME=$hostName LUSER='${rootUser.login}' NIX_VERSION=2.18.1 PROVIDER=$infectProviderName STAGING_ACME='$stagingAcme' "
|
||||||
'tee /tmp/infect.log',
|
"${customSshKey != null ? "SSH_AUTHORIZED_KEY='$customSshKey'" : ""} "
|
||||||
|
'bash 2>&1 | tee /root/nixos-infect.log',
|
||||||
'labels': {},
|
'labels': {},
|
||||||
'automount': true,
|
'automount': true,
|
||||||
'location': region!,
|
'location': region!,
|
||||||
|
|
|
@ -13,4 +13,6 @@ class TlsOptions {
|
||||||
///
|
///
|
||||||
/// Doesn't matter if 'statingAcme' is set to 'true'
|
/// Doesn't matter if 'statingAcme' is set to 'true'
|
||||||
static bool verifyCertificate = false;
|
static bool verifyCertificate = false;
|
||||||
|
|
||||||
|
static bool allowCustomSshKeyDuringSetup = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/client_jobs/client_jobs_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/models/job.dart';
|
import 'package:selfprivacy/logic/models/job.dart';
|
||||||
import 'package:selfprivacy/logic/models/hive/user.dart';
|
import 'package:selfprivacy/logic/models/hive/user.dart';
|
||||||
|
|
||||||
|
@ -50,3 +51,39 @@ class SshFormCubit extends FormCubit {
|
||||||
final JobsCubit jobsCubit;
|
final JobsCubit jobsCubit;
|
||||||
final User user;
|
final User user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class JoblessSshFormCubit extends FormCubit {
|
||||||
|
JoblessSshFormCubit(
|
||||||
|
this.serverInstallationCubit,
|
||||||
|
) {
|
||||||
|
final RegExp keyRegExp = RegExp(
|
||||||
|
r'^(ecdsa-sha2-nistp256 AAAAE2VjZH|ssh-rsa AAAAB3NzaC1yc2|ssh-ed25519 AAAAC3NzaC1lZDI1NTE5)[0-9A-Za-z+/]+[=]{0,3}( .*)?$',
|
||||||
|
);
|
||||||
|
|
||||||
|
key = FieldCubit(
|
||||||
|
initalValue: '',
|
||||||
|
validations: [
|
||||||
|
RequiredStringValidation('validations.required'.tr()),
|
||||||
|
ValidationModel<String>(
|
||||||
|
(final String s) {
|
||||||
|
print(s);
|
||||||
|
print(keyRegExp.hasMatch(s));
|
||||||
|
return !keyRegExp.hasMatch(s);
|
||||||
|
},
|
||||||
|
'validations.invalid_format_ssh'.tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
super.addFields([key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<void> onSubmit() {
|
||||||
|
serverInstallationCubit.setCustomSshKey(key.state.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
|
||||||
|
late FieldCubit<String> key;
|
||||||
|
}
|
||||||
|
|
|
@ -283,6 +283,10 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
runDelayed(startServerIfDnsIsOkay, const Duration(seconds: 30), null);
|
runDelayed(startServerIfDnsIsOkay, const Duration(seconds: 30), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCustomSshKey(final String key) async {
|
||||||
|
emit((state as ServerInstallationNotFinished).copyWith(customSshKey: key));
|
||||||
|
}
|
||||||
|
|
||||||
void createServerAndSetDnsRecords() async {
|
void createServerAndSetDnsRecords() async {
|
||||||
emit((state as ServerInstallationNotFinished).copyWith(isLoading: true));
|
emit((state as ServerInstallationNotFinished).copyWith(isLoading: true));
|
||||||
|
|
||||||
|
@ -295,6 +299,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
errorCallback: clearAppConfig,
|
errorCallback: clearAppConfig,
|
||||||
successCallback: onCreateServerSuccess,
|
successCallback: onCreateServerSuccess,
|
||||||
storageSize: initialStorage,
|
storageSize: initialStorage,
|
||||||
|
customSshKey: (state as ServerInstallationNotFinished).customSshKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result =
|
final result =
|
||||||
|
@ -851,6 +856,7 @@ class ServerInstallationCubit extends Cubit<ServerInstallationState> {
|
||||||
dnsApiToken: state.dnsApiToken,
|
dnsApiToken: state.dnsApiToken,
|
||||||
backblazeCredential: state.backblazeCredential,
|
backblazeCredential: state.backblazeCredential,
|
||||||
rootUser: state.rootUser,
|
rootUser: state.rootUser,
|
||||||
|
customSshKey: null,
|
||||||
serverDetails: null,
|
serverDetails: null,
|
||||||
isServerStarted: false,
|
isServerStarted: false,
|
||||||
isServerResetedFirstTime: false,
|
isServerResetedFirstTime: false,
|
||||||
|
|
|
@ -150,6 +150,7 @@ class ServerInstallationRepository {
|
||||||
box.get(BNames.isServerResetedSecondTime, defaultValue: false),
|
box.get(BNames.isServerResetedSecondTime, defaultValue: false),
|
||||||
isLoading: box.get(BNames.isLoading, defaultValue: false),
|
isLoading: box.get(BNames.isLoading, defaultValue: false),
|
||||||
dnsMatches: null,
|
dnsMatches: null,
|
||||||
|
customSshKey: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ class TimerState extends ServerInstallationNotFinished {
|
||||||
isServerResetedSecondTime: dataState.isServerResetedSecondTime,
|
isServerResetedSecondTime: dataState.isServerResetedSecondTime,
|
||||||
dnsMatches: dataState.dnsMatches,
|
dnsMatches: dataState.dnsMatches,
|
||||||
installationDialoguePopUp: dataState.installationDialoguePopUp,
|
installationDialoguePopUp: dataState.installationDialoguePopUp,
|
||||||
|
customSshKey: dataState.customSshKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
final ServerInstallationNotFinished dataState;
|
final ServerInstallationNotFinished dataState;
|
||||||
|
@ -135,6 +136,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
required super.isServerResetedSecondTime,
|
required super.isServerResetedSecondTime,
|
||||||
required this.isLoading,
|
required this.isLoading,
|
||||||
required this.dnsMatches,
|
required this.dnsMatches,
|
||||||
|
required this.customSshKey,
|
||||||
super.providerApiToken,
|
super.providerApiToken,
|
||||||
super.serverTypeIdentificator,
|
super.serverTypeIdentificator,
|
||||||
super.dnsApiToken,
|
super.dnsApiToken,
|
||||||
|
@ -146,6 +148,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
});
|
});
|
||||||
final bool isLoading;
|
final bool isLoading;
|
||||||
final Map<String, DnsRecordStatus>? dnsMatches;
|
final Map<String, DnsRecordStatus>? dnsMatches;
|
||||||
|
final String? customSshKey;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
|
@ -160,6 +163,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
isServerResetedFirstTime,
|
isServerResetedFirstTime,
|
||||||
isLoading,
|
isLoading,
|
||||||
dnsMatches,
|
dnsMatches,
|
||||||
|
customSshKey,
|
||||||
installationDialoguePopUp,
|
installationDialoguePopUp,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -177,6 +181,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
final bool? isLoading,
|
final bool? isLoading,
|
||||||
final Map<String, DnsRecordStatus>? dnsMatches,
|
final Map<String, DnsRecordStatus>? dnsMatches,
|
||||||
final CallbackDialogueBranching? installationDialoguePopUp,
|
final CallbackDialogueBranching? installationDialoguePopUp,
|
||||||
|
final String? customSshKey,
|
||||||
}) =>
|
}) =>
|
||||||
ServerInstallationNotFinished(
|
ServerInstallationNotFinished(
|
||||||
providerApiToken: providerApiToken ?? this.providerApiToken,
|
providerApiToken: providerApiToken ?? this.providerApiToken,
|
||||||
|
@ -196,6 +201,7 @@ class ServerInstallationNotFinished extends ServerInstallationState {
|
||||||
dnsMatches: dnsMatches ?? this.dnsMatches,
|
dnsMatches: dnsMatches ?? this.dnsMatches,
|
||||||
installationDialoguePopUp:
|
installationDialoguePopUp:
|
||||||
installationDialoguePopUp ?? this.installationDialoguePopUp,
|
installationDialoguePopUp ?? this.installationDialoguePopUp,
|
||||||
|
customSshKey: customSshKey ?? this.customSshKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
ServerInstallationFinished finish() => ServerInstallationFinished(
|
ServerInstallationFinished finish() => ServerInstallationFinished(
|
||||||
|
@ -229,6 +235,7 @@ class ServerInstallationEmpty extends ServerInstallationNotFinished {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
dnsMatches: null,
|
dnsMatches: null,
|
||||||
installationDialoguePopUp: null,
|
installationDialoguePopUp: null,
|
||||||
|
customSshKey: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ class LaunchInstallationData {
|
||||||
required this.errorCallback,
|
required this.errorCallback,
|
||||||
required this.successCallback,
|
required this.successCallback,
|
||||||
required this.storageSize,
|
required this.storageSize,
|
||||||
|
required this.customSshKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
final User rootUser;
|
final User rootUser;
|
||||||
|
@ -23,4 +24,5 @@ class LaunchInstallationData {
|
||||||
final Function() errorCallback;
|
final Function() errorCallback;
|
||||||
final Function(ServerHostingDetails details) successCallback;
|
final Function(ServerHostingDetails details) successCallback;
|
||||||
final DiskSize storageSize;
|
final DiskSize storageSize;
|
||||||
|
final String? customSshKey;
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,6 +228,7 @@ class DigitalOceanServerProvider extends ServerProvider {
|
||||||
),
|
),
|
||||||
databasePassword: StringGenerators.dbPassword(),
|
databasePassword: StringGenerators.dbPassword(),
|
||||||
serverApiToken: serverApiToken,
|
serverApiToken: serverApiToken,
|
||||||
|
customSshKey: installationData.customSshKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!serverResult.success || serverResult.data == null) {
|
if (!serverResult.success || serverResult.data == null) {
|
||||||
|
|
|
@ -211,6 +211,7 @@ class HetznerServerProvider extends ServerProvider {
|
||||||
),
|
),
|
||||||
databasePassword: StringGenerators.dbPassword(),
|
databasePassword: StringGenerators.dbPassword(),
|
||||||
serverApiToken: serverApiToken,
|
serverApiToken: serverApiToken,
|
||||||
|
customSshKey: installationData.customSshKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!serverResult.success || serverResult.data == null) {
|
if (!serverResult.success || serverResult.data == null) {
|
||||||
|
|
|
@ -39,8 +39,14 @@ class BrandButton {
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
tapTargetSize: MaterialTapTargetSize.padded,
|
tapTargetSize: MaterialTapTargetSize.padded,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
),
|
),
|
||||||
child: child ?? Text(text ?? ''),
|
child: child ??
|
||||||
|
Text(
|
||||||
|
text ?? '',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,16 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
|
||||||
() => TlsOptions.verifyCertificate = value,
|
() => TlsOptions.verifyCertificate = value,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SwitchListTile(
|
||||||
|
title: Text('developer_settings.allow_ssh_key_at_setup'.tr()),
|
||||||
|
subtitle: Text(
|
||||||
|
'developer_settings.allow_ssh_key_at_setup_description'.tr(),
|
||||||
|
),
|
||||||
|
value: TlsOptions.allowCustomSshKeyDuringSetup,
|
||||||
|
onChanged: (final bool value) => setState(
|
||||||
|
() => TlsOptions.allowCustomSshKeyDuringSetup = value,
|
||||||
|
),
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|
|
@ -2,7 +2,9 @@ import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:cubit_form/cubit_form.dart';
|
import 'package:cubit_form/cubit_form.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:selfprivacy/logic/api_maps/tls_options.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/server_provider_form_cubit.dart';
|
||||||
|
import 'package:selfprivacy/logic/cubit/forms/user/ssh_form_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/server_installation/server_installation_cubit.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
import 'package:selfprivacy/logic/cubit/forms/factories/field_cubit_factory.dart';
|
||||||
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart';
|
import 'package:selfprivacy/logic/cubit/forms/setup/initializing/backblaze_form_cubit.dart';
|
||||||
|
@ -420,6 +422,9 @@ class InitializingPage extends StatelessWidget {
|
||||||
Widget _stepServer(final ServerInstallationCubit appConfigCubit) {
|
Widget _stepServer(final ServerInstallationCubit appConfigCubit) {
|
||||||
final bool isLoading =
|
final bool isLoading =
|
||||||
(appConfigCubit.state as ServerInstallationNotFinished).isLoading;
|
(appConfigCubit.state as ServerInstallationNotFinished).isLoading;
|
||||||
|
final bool hasSshKey =
|
||||||
|
(appConfigCubit.state as ServerInstallationNotFinished).customSshKey !=
|
||||||
|
null;
|
||||||
return Builder(
|
return Builder(
|
||||||
builder: (final context) => ResponsiveLayoutWithInfobox(
|
builder: (final context) => ResponsiveLayoutWithInfobox(
|
||||||
topChild: Column(
|
topChild: Column(
|
||||||
|
@ -436,12 +441,40 @@ class InitializingPage extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
primaryColumn: BrandButton.filled(
|
primaryColumn: Column(
|
||||||
onPressed:
|
children: [
|
||||||
isLoading ? null : appConfigCubit.createServerAndSetDnsRecords,
|
BrandButton.filled(
|
||||||
text: isLoading
|
onPressed: isLoading
|
||||||
? 'basis.loading'.tr()
|
? null
|
||||||
: 'initializing.create_server'.tr(),
|
: appConfigCubit.createServerAndSetDnsRecords,
|
||||||
|
text: isLoading
|
||||||
|
? 'basis.loading'.tr()
|
||||||
|
: 'initializing.create_server'.tr(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
if (TlsOptions.allowCustomSshKeyDuringSetup)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text('developer_settings.title'.tr()),
|
||||||
|
BrandOutlinedButton(
|
||||||
|
title: hasSshKey
|
||||||
|
? 'developer_settings.root_ssh_key_added'.tr()
|
||||||
|
: 'developer_settings.add_root_ssh_key'.tr(),
|
||||||
|
onPressed: () {
|
||||||
|
showModalBottomSheet<String?>(
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
useRootNavigator: true,
|
||||||
|
builder: (final BuildContext context) => Padding(
|
||||||
|
padding: MediaQuery.of(context).viewInsets,
|
||||||
|
child: AddSshKey(appConfigCubit),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -540,3 +573,62 @@ class InitializingPage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AddSshKey extends StatelessWidget {
|
||||||
|
const AddSshKey(this.serverInstallationCubit, {super.key});
|
||||||
|
|
||||||
|
final ServerInstallationCubit serverInstallationCubit;
|
||||||
|
@override
|
||||||
|
Widget build(final BuildContext context) => BlocProvider(
|
||||||
|
create: (final context) => JoblessSshFormCubit(serverInstallationCubit),
|
||||||
|
child: Builder(
|
||||||
|
builder: (final context) {
|
||||||
|
final formCubitState = context.watch<JoblessSshFormCubit>().state;
|
||||||
|
|
||||||
|
return BlocListener<JoblessSshFormCubit, FormCubitState>(
|
||||||
|
listener: (final context, final state) {
|
||||||
|
if (state.isSubmitted) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 14),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IntrinsicHeight(
|
||||||
|
child: CubitFormTextField(
|
||||||
|
autofocus: true,
|
||||||
|
formFieldCubit:
|
||||||
|
context.read<JoblessSshFormCubit>().key,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'ssh.input_label'.tr(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
BrandButton.rised(
|
||||||
|
onPressed: formCubitState.isSubmitting
|
||||||
|
? null
|
||||||
|
: () => context
|
||||||
|
.read<JoblessSshFormCubit>()
|
||||||
|
.trySubmit(),
|
||||||
|
text: 'ssh.create'.tr(),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue