Skip to content

Commit 473505a

Browse files
committed
fix wizard empty state
1 parent ae715f8 commit 473505a

16 files changed

Lines changed: 113 additions & 48 deletions

File tree

crates/defguard_common/src/db/models/initial_setup_wizard.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ pub enum InitialSetupStep {
1414
// Adoption is not present, since the proxy is saved
1515
// only after completing adoption step.
1616
EdgeComponent,
17+
InternalUrlSettings,
18+
InternalUrlSslConfig,
19+
ExternalUrlSettings,
20+
ExternalUrlSslConfig,
1721
Confirmation,
1822
Finished,
1923
}

crates/defguard_core/src/handlers/component_setup.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,13 +609,13 @@ pub async fn setup_proxy_tls_stream(
609609
Ok(wizard) => {
610610
if !wizard.completed {
611611
let state = InitialSetupState {
612-
step: InitialSetupStep::Confirmation,
612+
step: InitialSetupStep::InternalUrlSettings,
613613
};
614614
if let Err(err) = state.save(&pool).await {
615615
yield Ok(flow.error(&format!("Failed to update setup step in wizard: {err}")));
616616
return;
617617
}
618-
debug!("Initial setup step advanced to 'Confirmation'");
618+
debug!("Initial setup step advanced to 'InternalUrlSettings'");
619619
}
620620
}
621621
Err(err) => {

crates/defguard_proxy_manager/src/handler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ impl ProxyHandler {
121121
}
122122
}
123123

124+
#[allow(clippy::too_many_arguments)]
124125
pub(super) fn from_proxy(
125126
proxy: &Proxy<Id>,
126127
pool: PgPool,

crates/defguard_setup/src/handlers/auto_wizard.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use defguard_common::{
77
db::models::{
88
Certificates, WireguardNetwork,
99
certificates::{CoreCertSource, ProxyCertSource},
10+
initial_setup_wizard::InitialSetupStep,
1011
settings::update_current_settings,
1112
setup_auto_adoption::{AutoAdoptionWizardState, AutoAdoptionWizardStep},
1213
wireguard::LocationMfaMode,
@@ -26,6 +27,8 @@ use serde_json::json;
2627
use sqlx::{PgPool, query_scalar};
2728
use tracing::{debug, info};
2829

30+
use crate::handlers::initial_wizard::advance_initial_wizard_to_step;
31+
2932
pub(crate) async fn is_auto_wizard_active(pool: &PgPool) -> Result<bool, WebError> {
3033
let wizard = Wizard::get(pool).await?;
3134
Ok(wizard.active_wizard == ActiveWizard::AutoAdoption)
@@ -185,6 +188,7 @@ pub async fn set_internal_url_settings(
185188
};
186189

187190
advance_auto_wizard_to_step(&pool, AutoAdoptionWizardStep::InternalUrlSslConfig).await?;
191+
advance_initial_wizard_to_step(&pool, InitialSetupStep::InternalUrlSslConfig).await?;
188192

189193
info!("Auto-adoption wizard internal URL settings applied");
190194

@@ -216,10 +220,11 @@ pub async fn get_internal_ssl_info(
216220
}
217221

218222
/// SSL configuration type for the external (proxy) web server.
219-
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
223+
#[derive(Default, Deserialize, Serialize, Debug, Clone, PartialEq)]
220224
#[serde(rename_all = "snake_case")]
221225
pub enum ExternalSslType {
222226
/// No SSL - plain HTTP, user manages reverse proxy / SSL termination themselves.
227+
#[default]
223228
None,
224229
/// Obtain certificate via ACME / Let's Encrypt.
225230
LetsEncrypt,
@@ -238,12 +243,6 @@ pub struct ExternalUrlSettingsConfig {
238243
key_pem: Option<String>,
239244
}
240245

241-
impl Default for ExternalSslType {
242-
fn default() -> Self {
243-
Self::None
244-
}
245-
}
246-
247246
/// Updates external proxy URL settings (step 4).
248247
pub async fn set_external_url_settings(
249248
_: AdminOrSetupRole,
@@ -360,6 +359,7 @@ pub async fn set_external_url_settings(
360359
};
361360

362361
advance_auto_wizard_to_step(&pool, AutoAdoptionWizardStep::ExternalUrlSslConfig).await?;
362+
advance_initial_wizard_to_step(&pool, InitialSetupStep::ExternalUrlSslConfig).await?;
363363

364364
info!("Auto-adoption wizard external URL settings applied");
365365
Ok(ApiResponse::new(

crates/defguard_setup/src/handlers/initial_wizard.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,18 @@ use tracing::{debug, info};
3737

3838
use crate::handlers::auto_wizard::{advance_auto_wizard_to_step, is_auto_wizard_active};
3939

40-
async fn advance_initial_wizard_to_step(
40+
pub(crate) async fn advance_initial_wizard_to_step(
4141
pool: &PgPool,
4242
step: InitialSetupStep,
4343
) -> Result<(), WebError> {
4444
let wizard = Wizard::get(pool).await?;
4545

46+
// Don't try to advance if the initial wizard is not active
47+
if wizard.active_wizard != ActiveWizard::Initial {
48+
debug!("Not advancing initial wizard step as initial wizard is not active");
49+
return Ok(());
50+
}
51+
4652
// Don't try to advance if setup is already completed
4753
if wizard.completed {
4854
debug!("Not advancing setup step as initial setup is already completed");
@@ -329,7 +335,7 @@ pub async fn set_general_config(
329335

330336
info!("Initial general configuration applied");
331337

332-
advance_initial_wizard_to_step(&pool, InitialSetupStep::Ca).await?;
338+
advance_initial_wizard_to_step(&pool, InitialSetupStep::InternalUrlSettings).await?;
333339

334340
Ok(ApiResponse::with_status(StatusCode::CREATED))
335341
}

crates/defguard_setup/tests/auto_wizard_url_settings.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn generate_test_cert_pem(common_name: &str) -> (String, String) {
5656
let dn = vec![(DnType::CommonName, common_name)];
5757
let csr = Csr::new(&key_pair, &san, dn).unwrap();
5858
let cert = ca.sign_csr(&csr).unwrap();
59-
let cert_pem = der_to_pem(&cert.der().to_vec(), PemLabel::Certificate).unwrap();
59+
let cert_pem = der_to_pem(cert.der(), PemLabel::Certificate).unwrap();
6060
let key_pem = der_to_pem(key_pair.serialize_der().as_slice(), PemLabel::PrivateKey).unwrap();
6161
(cert_pem, key_pem)
6262
}

web/src/pages/MigrationWizardPage/steps/MigrationWizardExternalUrlSslConfigStep.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ export const MigrationWizardExternalUrlSslConfigStep = () => {
6161

6262
const [acmeState, setAcmeState] = useState<AcmeStepState>(defaultAcmeState);
6363

64+
// If ssl_type is not set (e.g. fresh browser session), redirect back so the
65+
// user can re-submit the settings step and repopulate the store.
66+
// biome-ignore lint/correctness/useExhaustiveDependencies: only run on mount
67+
useEffect(() => {
68+
if (sslType === null) {
69+
useMigrationWizardStore.getState().back();
70+
}
71+
}, []);
72+
6473
const { data: sslInfoData } = useQuery({
6574
queryKey: ['external_ssl_info'],
6675
queryFn: () => api.initial_setup.getExternalSslInfo(),
@@ -301,7 +310,7 @@ export const MigrationWizardExternalUrlSslConfigStep = () => {
301310
text={m.controls_back()}
302311
variant="outlined"
303312
onClick={() => useMigrationWizardStore.getState().back()}
304-
disabled={isLetsEncryptProcessing || acmeState.isComplete}
313+
disabled={isLetsEncryptProcessing}
305314
/>
306315
<div className="right">
307316
<Button

web/src/pages/MigrationWizardPage/steps/MigrationWizardInternalUrlSslConfigStep.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useQuery } from '@tanstack/react-query';
2+
import { useEffect } from 'react';
23
import { m } from '../../../paraglide/messages';
34
import api from '../../../shared/api/api';
45
import { Controls } from '../../../shared/components/Controls/Controls';
@@ -16,6 +17,15 @@ export const MigrationWizardInternalUrlSslConfigStep = () => {
1617
const sslType = useMigrationWizardStore((s) => s.internal_ssl_type);
1718
const certInfo = useMigrationWizardStore((s) => s.internal_ssl_cert_info);
1819

20+
// If ssl_type is not set (e.g. fresh browser session), redirect back so the
21+
// user can re-submit the settings step and repopulate the store.
22+
// biome-ignore lint/correctness/useExhaustiveDependencies: only run on mount
23+
useEffect(() => {
24+
if (sslType === null) {
25+
useMigrationWizardStore.getState().back();
26+
}
27+
}, []);
28+
1929
const { data: sslInfoData } = useQuery({
2030
queryKey: ['internal_ssl_info'],
2131
queryFn: () => api.initial_setup.getInternalSslInfo(),

web/src/pages/MigrationWizardPage/store/useMigrationWizardStore.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ interface StoreValues extends MigrationWizardApiState {
3030
authentication_period_days: number;
3131
mfa_code_timeout_seconds: number;
3232
// internal URL SSL configuration
33-
internal_ssl_type: InternalSslType;
33+
internal_ssl_type: InternalSslType | null;
3434
internal_ssl_cert_info: CertInfo | null;
3535
// external URL SSL configuration
36-
external_ssl_type: ExternalSslType;
36+
external_ssl_type: ExternalSslType | null;
3737
external_ssl_cert_info: CertInfo | null;
3838
// ca
3939
ca_common_name: string;
@@ -65,9 +65,9 @@ const defaults: StoreValues = {
6565
default_admin_group_name: 'admin',
6666
authentication_period_days: 30,
6767
mfa_code_timeout_seconds: 300,
68-
internal_ssl_type: 'none',
68+
internal_ssl_type: null,
6969
internal_ssl_cert_info: null,
70-
external_ssl_type: 'none',
70+
external_ssl_type: null,
7171
external_ssl_cert_info: null,
7272
ca_common_name: m.migration_wizard_ca_placeholder_common_name(),
7373
ca_email: '',

web/src/pages/SetupPage/autoAdoption/steps/AutoAdoptionExternalUrlSslConfigStep.tsx

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useQuery } from '@tanstack/react-query';
2-
import { useCallback, useEffect, useState } from 'react';
2+
import { useCallback, useEffect } from 'react';
33
import { m } from '../../../../paraglide/messages';
44
import api from '../../../../shared/api/api';
55
import { Controls } from '../../../../shared/components/Controls/Controls';
@@ -61,7 +61,14 @@ export const AutoAdoptionExternalUrlSslConfigStep = () => {
6161
const sslType = useAutoAdoptionSetupWizardStore((s) => s.external_ssl_type);
6262
const certInfo = useAutoAdoptionSetupWizardStore((s) => s.external_ssl_cert_info);
6363

64-
const [acmeState, setAcmeState] = useState<AcmeStepState>(defaultAcmeState);
64+
// If ssl_type is not set (e.g. fresh browser session), redirect back so the
65+
// user can re-submit the settings step and repopulate the store.
66+
// biome-ignore lint/correctness/useExhaustiveDependencies: only run on mount
67+
useEffect(() => {
68+
if (sslType === null) {
69+
setActiveStep(AutoAdoptionSetupStep.ExternalUrlSettings);
70+
}
71+
}, []);
6572

6673
const { data: sslInfoData } = useQuery({
6774
queryKey: ['external_ssl_info'],
@@ -99,33 +106,24 @@ export const AutoAdoptionExternalUrlSslConfigStep = () => {
99106
};
100107
}, []);
101108

102-
const stepDone = useCallback(
103-
(stepId: AcmeStepId): boolean => {
104-
const stepIndex = ACME_STEP_IDS.indexOf(stepId);
105-
const currentIndex = acmeState.currentStep
106-
? ACME_STEP_IDS.indexOf(acmeState.currentStep)
107-
: -1;
108-
return stepIndex < currentIndex || acmeState.isComplete;
109-
},
110-
[acmeState.isComplete, acmeState.currentStep],
111-
);
109+
const stepDone = useCallback((stepId: AcmeStepId): boolean => {
110+
const stepIndex = ACME_STEP_IDS.indexOf(stepId);
111+
const currentIndex = acmeState.currentStep
112+
? ACME_STEP_IDS.indexOf(acmeState.currentStep)
113+
: -1;
114+
return stepIndex < currentIndex || acmeState.isComplete;
115+
}, []);
112116

113-
const stepLoading = useCallback(
114-
(stepId: AcmeStepId): boolean => {
115-
return acmeState.isProcessing && acmeState.currentStep === stepId;
116-
},
117-
[acmeState.isProcessing, acmeState.currentStep],
118-
);
117+
const stepLoading = useCallback((stepId: AcmeStepId): boolean => {
118+
return acmeState.isProcessing && acmeState.currentStep === stepId;
119+
}, []);
119120

120-
const stepError = useCallback(
121-
(stepId: AcmeStepId): string | null => {
122-
if (acmeState.errorMessage && acmeState.currentStep === stepId) {
123-
return acmeState.errorMessage;
124-
}
125-
return null;
126-
},
127-
[acmeState.errorMessage, acmeState.currentStep],
128-
);
121+
const stepError = useCallback((stepId: AcmeStepId): string | null => {
122+
if (acmeState.errorMessage && acmeState.currentStep === stepId) {
123+
return acmeState.errorMessage;
124+
}
125+
return null;
126+
}, []);
129127

130128
const handleDownloadCaCert = () => {
131129
if (!sslInfoData?.ca_cert_pem) return;
@@ -303,7 +301,7 @@ export const AutoAdoptionExternalUrlSslConfigStep = () => {
303301
text={m.initial_setup_controls_back()}
304302
variant="outlined"
305303
onClick={() => setActiveStep(AutoAdoptionSetupStep.ExternalUrlSettings)}
306-
disabled={isLetsEncryptProcessing || acmeState.isComplete}
304+
disabled={isLetsEncryptProcessing}
307305
/>
308306
<div className="right">
309307
<Button

0 commit comments

Comments
 (0)