From f54aa46303654aac9b0daa8eca85d3434dbfeae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=9Al=C4=99zak?= Date: Tue, 17 Mar 2026 12:31:49 +0100 Subject: [PATCH 1/5] requested changes for migration wizard steps --- .../defguard_core/src/handlers/wireguard.rs | 40 ++++++ crates/defguard_core/src/lib.rs | 8 +- crates/defguard_core/src/openapi.rs | 1 + .../defguard_setup/src/handlers/migration.rs | 61 +------- crates/defguard_setup/src/migration.rs | 6 +- web/messages/en/edge_wizard.json | 7 +- web/messages/en/initial_wizard.json | 6 +- web/messages/en/migration_wizard.json | 25 ++-- web/package.json | 6 +- web/pnpm-lock.yaml | 135 +++++++++--------- .../steps/SetupEdgeComponentStep.tsx | 3 + .../MigrationWizardPage.tsx | 24 ++-- .../steps/MigrationWizardCAStep.tsx | 7 +- .../MigrationWizardEdgeComponentStep.tsx | 10 ++ ...igrationWizardGeneralConfigurationStep.tsx | 93 +++--------- .../store/useMigrationWizardStore.tsx | 8 +- .../steps/SetupCertificateAuthorityStep.tsx | 2 + .../initial/steps/SetupEdgeComponentStep.tsx | 3 + web/src/shared/api/api.ts | 5 +- web/src/shared/api/types.ts | 13 +- web/src/shared/defguard-ui | 2 +- web/src/shared/query.ts | 6 + 22 files changed, 226 insertions(+), 245 deletions(-) diff --git a/crates/defguard_core/src/handlers/wireguard.rs b/crates/defguard_core/src/handlers/wireguard.rs index 8a1e48b9e9..3484bd3360 100644 --- a/crates/defguard_core/src/handlers/wireguard.rs +++ b/crates/defguard_core/src/handlers/wireguard.rs @@ -53,6 +53,11 @@ pub(crate) struct WireguardNetworkInfo { has_devices: bool, } +#[derive(Serialize, ToSchema)] +pub(crate) struct LocationsCount { + count: usize, +} + #[derive(Deserialize, Serialize, ToSchema)] pub struct WireguardNetworkData { pub name: String, @@ -493,6 +498,41 @@ pub async fn list_networks(_role: AdminRole, State(appstate): State) - Ok(ApiResponse::json(network_info, StatusCode::OK)) } +/// Number of all networks +/// +/// Retrieve count of all networks. +/// +/// # Returns +/// - `LocationsCount` object +/// +/// - `WebError` if error occurs +#[utoipa::path( + get, + path = "/api/v1/network/count", + responses( + (status = 200, description = "Count of all networks", body = LocationsCount), + (status = 401, description = "Unauthorized to count networks.", body = ApiResponse, example = json!({"msg": "Session is required"})), + (status = 403, description = "You don't have permission to count networks.", body = ApiResponse, example = json!({"msg": "access denied"})), + (status = 500, description = "Unable to count networks.", body = ApiResponse, example = json!({"msg": "Internal server error"})) + ), + security( + ("cookie" = []), + ("api_token" = []) + ) +)] +pub async fn count_networks(_role: AdminRole, State(appstate): State) -> ApiResult { + debug!("Counting WireGuard networks"); + let count = sqlx::query_scalar!("SELECT COUNT(*) FROM wireguard_network") + .fetch_one(&appstate.pool) + .await?; + let count = count.unwrap_or(0).try_into().map_err(|error| { + error!("Failed to convert wireguard_network count to usize: {error}"); + WebError::Http(StatusCode::INTERNAL_SERVER_ERROR) + })?; + + Ok(ApiResponse::json(LocationsCount { count }, StatusCode::OK)) +} + /// Details of network /// /// Retrieve details about network with `network_id`. diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index ba963d2067..08ebec1b9e 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -163,9 +163,10 @@ use crate::{ add_webhook, change_enabled, change_webhook, delete_webhook, get_webhook, list_webhooks, }, wireguard::{ - add_device, add_user_devices, create_network, delete_device, delete_network, - download_config, gateway_status, get_device, import_network, list_devices, - list_networks, list_user_devices, modify_device, modify_network, network_details, + add_device, add_user_devices, count_networks, create_network, delete_device, + delete_network, download_config, gateway_status, get_device, import_network, + list_devices, list_networks, list_user_devices, modify_device, modify_network, + network_details, }, worker::{create_job, create_worker_token, job_status, list_workers, remove_worker}, }, @@ -537,6 +538,7 @@ pub fn build_webapp( post(start_network_device_setup_for_device), ) .route("/network", post(create_network).get(list_networks)) + .route("/network/count", get(count_networks)) .route("/network/display", get(get_locations_display)) .route("/network/import", post(import_network)) .route("/network/stats", get(locations_overview_stats)) diff --git a/crates/defguard_core/src/openapi.rs b/crates/defguard_core/src/openapi.rs index 5c3483856a..4340b7caa9 100644 --- a/crates/defguard_core/src/openapi.rs +++ b/crates/defguard_core/src/openapi.rs @@ -71,6 +71,7 @@ use super::{ network::modify_network, network::delete_network, network::list_networks, + network::count_networks, network::network_details, // /license license::license_check, diff --git a/crates/defguard_setup/src/handlers/migration.rs b/crates/defguard_setup/src/handlers/migration.rs index 220a5f815d..67443fbdc5 100644 --- a/crates/defguard_setup/src/handlers/migration.rs +++ b/crates/defguard_setup/src/handlers/migration.rs @@ -2,8 +2,7 @@ use std::sync::{Arc, Mutex}; use axum::{Extension, Json}; use defguard_common::db::models::{ - ActiveWizard, Settings, Wizard, group::Group, migration_wizard::MigrationWizardState, - settings::update_current_settings, + ActiveWizard, Wizard, migration_wizard::MigrationWizardState, }; use defguard_core::{ auth::AdminOrSetupRole, @@ -15,7 +14,7 @@ use serde::{Deserialize, Serialize}; use serde_json::json; use sqlx::PgPool; use tokio::sync::oneshot; -use tracing::{debug, info}; +use tracing::info; pub async fn get_migration_state( _: AdminOrSetupRole, @@ -38,66 +37,10 @@ pub async fn update_migration_state( #[derive(Deserialize, Serialize, Debug)] pub struct GeneralConfig { defguard_url: String, - default_admin_group_name: String, - default_authentication: u32, default_mfa_code_lifetime: u32, public_proxy_url: String, } -pub async fn set_general_config( - _: AdminOrSetupRole, - Extension(pool): Extension, - Json(general_config): Json, -) -> ApiResult { - info!("Applying initial general configuration settings"); - debug!( - "General configuration received: defguard_url={}, default_admin_group_name={}, default_authentication={}, default_mfa_code_lifetime={}, public_proxy_url={}", - general_config.defguard_url, - general_config.default_admin_group_name, - general_config.default_authentication, - general_config.default_mfa_code_lifetime, - general_config.public_proxy_url, - ); - let default_admin_group_name = general_config.default_admin_group_name.clone(); - let mut settings = Settings::get_current_settings(); - settings.public_proxy_url = general_config.public_proxy_url; - settings.defguard_url = general_config.defguard_url; - settings.default_admin_group_name = general_config.default_admin_group_name; - settings.authentication_period_days = general_config - .default_authentication - .try_into() - .map_err(|err| { - WebError::BadRequest(format!("Invalid authentication period days: {err}")) - })?; - settings.mfa_code_timeout_seconds = general_config - .default_mfa_code_lifetime - .try_into() - .map_err(|err| WebError::BadRequest(format!("Invalid MFA code timeout seconds: {err}")))?; - update_current_settings(&pool, settings).await?; - debug!("Settings persisted"); - - if let Some(mut group) = Group::find_by_name(&pool, &default_admin_group_name).await? { - debug!( - "Admin group {} found, marking as admin", - default_admin_group_name - ); - group.is_admin = true; - group.save(&pool).await?; - } else { - debug!( - "Admin group {} not found, creating", - default_admin_group_name - ); - let mut group = Group::new(&default_admin_group_name); - group.is_admin = true; - group.save(&pool).await?; - } - - info!("Initial general configuration applied"); - - Ok(ApiResponse::with_status(StatusCode::OK)) -} - pub async fn finish_setup( _: AdminOrSetupRole, Extension(pool): Extension, diff --git a/crates/defguard_setup/src/migration.rs b/crates/defguard_setup/src/migration.rs index ec98119da0..acdc86d0f6 100644 --- a/crates/defguard_setup/src/migration.rs +++ b/crates/defguard_setup/src/migration.rs @@ -29,7 +29,7 @@ use defguard_core::{ resource_display::get_locations_display, session_info::get_session_info, settings::{get_settings, get_settings_essentials, patch_settings}, - wireguard::list_networks, + wireguard::{count_networks, list_networks}, }, health_check, version::IncompatibleComponents, @@ -45,7 +45,7 @@ use tracing::{info, instrument}; use crate::handlers::{ initial_wizard::{create_ca, get_ca, upload_ca}, - migration::{finish_setup, get_migration_state, set_general_config, update_migration_state}, + migration::{finish_setup, get_migration_state, update_migration_state}, }; /// FIXME: This is a workaround which enables us to reuse the same API handlers @@ -118,6 +118,7 @@ pub fn build_migration_webapp( .route("/auth/email/verify", post(email_mfa_code)) .route("/auth/recovery", post(recovery_code)) .route("/network", get(list_networks)) + .route("/network/count", get(count_networks)) .route("/network/display", get(get_locations_display)) .route( "/network/{network_id}/gateways/setup", @@ -130,7 +131,6 @@ pub fn build_migration_webapp( "/state", get(get_migration_state).put(update_migration_state), ) - .route("/general_config", post(set_general_config)) .route("/ca", post(create_ca).get(get_ca)) .route("/ca/upload", post(upload_ca)) .route("/finish", post(finish_setup)), diff --git a/web/messages/en/edge_wizard.json b/web/messages/en/edge_wizard.json index 2b4d81c99e..5ff7deb51d 100644 --- a/web/messages/en/edge_wizard.json +++ b/web/messages/en/edge_wizard.json @@ -21,10 +21,13 @@ "edge_setup_step_edge_adoption_description": "Review the system's checks and see if any issues need attention before deployment.", "edge_setup_step_confirmation_label": "Confirmation", "edge_setup_step_confirmation_description": "Your configuration was successful. You're all set.", - "edge_setup_component_label_common_name": "Common Name", + "edge_setup_component_label_common_name": "Edge Name", + "edge_setup_component_error_common_name_help": "This name will be visible in the Edge Components list and is used to identify a deployed instance, as there may be multiple instances for High Availability (HA).", "edge_setup_component_label_ip_or_domain": "IP or Domain", + "edge_setup_component_label_ip_or_domain_help": "Enter the IP or domain name of the server where the Edge Component is deployed. Core will then connect to it and perform the adoption automatically.", "edge_setup_component_label_grpc_port": "gRPC Port", - "edge_setup_component_error_common_name_required": "Common Name is required", + "edge_setup_component_label_grpc_port_help": "Specify the gRPC TCP port for Edge Component communication. If unchanged, leave the default value. Ensure this port is open and accessible to the Core instance through any server or network firewalls.", + "edge_setup_component_error_common_name_required": "Edge Name is required", "edge_setup_component_error_ip_or_domain_required": "IP or Domain is required", "edge_setup_component_error_grpc_port_required": "gRPC Port is required", "edge_setup_component_error_grpc_port_max": "gRPC Port must be less than 65536", diff --git a/web/messages/en/initial_wizard.json b/web/messages/en/initial_wizard.json index f9165635f1..4712430442 100644 --- a/web/messages/en/initial_wizard.json +++ b/web/messages/en/initial_wizard.json @@ -145,9 +145,11 @@ "initial_setup_ca_error_upload_failed": "Failed to upload CA. Please ensure the certificate file is valid and try again.", "initial_setup_ca_option_create_title": "Create a certificate authority & configure all Defguard components", "initial_setup_ca_option_create_description": "By choosing this option, Defguard will create its own certificate authority and automatically configure all components to use its certificates — no manual setup required.", - "initial_setup_ca_label_common_name": "Common Name", + "initial_setup_ca_label_common_name": "Certificate Authority Name", + "initial_setup_ca_helper_common_name": "Can be any name you wish.", "initial_setup_ca_placeholder_common_name": "Defguard Certificate Authority", - "initial_setup_ca_label_email": "Email", + "initial_setup_ca_label_email": "Certificate Authority Email", + "initial_setup_ca_helper_email": "Each certificate authority (or any certificate) has a contact email field. Can be your email or general IT email.", "initial_setup_ca_placeholder_email": "email@example.com", "initial_setup_ca_label_validity": "Validity Period", "initial_setup_ca_option_use_own_title": "Use your own certificate authority", diff --git a/web/messages/en/migration_wizard.json b/web/messages/en/migration_wizard.json index 9712b45e5a..0f35768dfd 100644 --- a/web/messages/en/migration_wizard.json +++ b/web/messages/en/migration_wizard.json @@ -5,11 +5,11 @@ "migration_wizard_step_general_config_label": "General Configuration", "migration_wizard_step_general_config_description": "Manage core details and connection parameters for your VPN location.", "migration_wizard_step_certificate_authority_label": "Certificate Authority", - "migration_wizard_step_certificate_authority_description": "Securing component communication", + "migration_wizard_step_certificate_authority_description": "We have incorporated a Certificate Authority (CA) management into Defguard to simplify component deployment and management.", "migration_wizard_step_certificate_authority_summary_label": "Certificate Authority Summary", "migration_wizard_step_certificate_authority_summary_description": "Securing component communication", "migration_wizard_step_edge_component_label": "Edge Component", - "migration_wizard_step_edge_component_description": "Set up your VPN proxy quickly and ensure secure, optimized traffic flow for your users.", + "migration_wizard_step_edge_component_description": "Starting with Defguard 2.0, Proxy has been renamed to Edge, and support for multiple instances (high availability) has been introduced. ", "migration_wizard_step_edge_adoption_label": "Edge Component Adoption", "migration_wizard_step_edge_adoption_description": "Review the system's checks and see if any issues need attention before deployment.", "migration_wizard_step_confirmation_label": "Confirmation", @@ -27,19 +27,20 @@ "migration_wizard_ca_validity_one_year": "1 year", "migration_wizard_ca_validity_years": "{years} years", "migration_wizard_general_config_error_invalid_url": "Invalid URL", - "migration_wizard_general_config_error_defguard_url_required": "Defguard URL is required", - "migration_wizard_general_config_error_defguard_url_invalid_host": "Defguard URL must use a hostname, not an IP address", + "migration_wizard_general_config_error_defguard_url_required": "Defguard Private URL is required", + "migration_wizard_general_config_error_defguard_url_invalid_host": "Defguard Private URL must use a hostname, not an IP address", "migration_wizard_general_config_error_admin_group_required": "Default admin group name is required", "migration_wizard_general_config_error_auth_period_min": "Authentication period must be at least 1 day", "migration_wizard_general_config_error_mfa_timeout_min": "MFA code timeout must be at least 60 seconds", - "migration_wizard_general_config_label_defguard_url": "Defguard URL", + "migration_wizard_general_config_label_defguard_url": "Defguard Private URL", "migration_wizard_general_config_label_admin_group": "Default Admin Group Name", "migration_wizard_general_config_label_auth_period": "Default Authentication Period (days)", "migration_wizard_general_config_label_mfa_timeout": "Default MFA Code Timeout (seconds)", - "migration_wizard_general_config_label_public_proxy_url": "Public Edge component URL", - "migration_wizard_general_config_error_public_proxy_url_invalid": "Public Proxy URL must be a valid URL", - "migration_wizard_general_config_error_public_proxy_url_required": "Public Proxy URL is required", - "migration_wizard_ca_error_common_name_required": "Common name is required", + "migration_wizard_general_config_label_public_proxy_url": "Defguard Public URL", + "migration_wizard_general_config_label_help_public_proxy_url": "", + "migration_wizard_general_config_error_public_proxy_url_invalid": "Defguard Public URL must be a valid URL", + "migration_wizard_general_config_error_public_proxy_url_required": "Defguard Public URL is required", + "migration_wizard_ca_error_common_name_required": "Certificate Authority Name is required", "migration_wizard_ca_error_email_invalid": "Invalid email address", "migration_wizard_ca_error_email_required": "Email is required", "migration_wizard_ca_error_validity_min": "Validity period must be at least 1 year", @@ -48,9 +49,11 @@ "migration_wizard_ca_error_upload_failed": "Failed to upload CA. Please ensure the certificate file is valid and try again.", "migration_wizard_ca_option_create_title": "Create a certificate authority & configure all Defguard components", "migration_wizard_ca_option_create_description": "By choosing this option, Defguard will create its own certificate authority and automatically configure all components to use its certificates — no manual setup required.", - "migration_wizard_ca_label_common_name": "Common Name", + "migration_wizard_ca_label_common_name": "Certificate Authority Name", + "migration_wizard_ca_helper_common_name": "Can be any name you wish.", "migration_wizard_ca_placeholder_common_name": "Defguard Certificate Authority", - "migration_wizard_ca_label_email": "Email", + "migration_wizard_ca_label_email": "Certificate Authority Email", + "migration_wizard_ca_helper_email": "Each certificate authority (or any certificate) has a contact email field. Can be your email or general IT email.", "migration_wizard_ca_placeholder_email": "email@example.com", "migration_wizard_ca_label_validity": "Validity Period", "migration_wizard_ca_generated_title": "Certificate Authority Generated", diff --git a/web/package.json b/web/package.json index 6e6c384bd4..34e6e8fcba 100644 --- a/web/package.json +++ b/web/package.json @@ -22,7 +22,7 @@ "@stablelib/x25519": "^2.0.1", "@tanstack/react-form": "^1.28.5", "@tanstack/react-query": "^5.90.21", - "@tanstack/react-router": "^1.167.3", + "@tanstack/react-router": "^1.167.4", "@tanstack/react-table": "^8.21.3", "@tanstack/react-virtual": "^3.13.23", "@uidotdev/usehooks": "^2.4.1", @@ -34,7 +34,7 @@ "humanize-duration": "^3.33.2", "ipaddr.js": "^2.3.0", "lodash-es": "^4.17.23", - "motion": "^12.36.0", + "motion": "^12.38.0", "qrcode.react": "^4.2.0", "qs": "^6.15.0", "radashi": "^12.7.2", @@ -57,7 +57,7 @@ "@tanstack/react-devtools": "^0.10.0", "@tanstack/react-query-devtools": "^5.91.3", "@tanstack/react-router-devtools": "^1.166.9", - "@tanstack/router-plugin": "^1.166.12", + "@tanstack/router-plugin": "^1.166.13", "@types/byte-size": "^8.1.2", "@types/humanize-duration": "^3.27.4", "@types/lodash-es": "^4.17.12", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index fbec0df2f3..8246963e53 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -36,8 +36,8 @@ importers: specifier: ^5.90.21 version: 5.90.21(react@19.2.4) '@tanstack/react-router': - specifier: ^1.167.3 - version: 1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: ^1.167.4 + version: 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -72,8 +72,8 @@ importers: specifier: ^4.17.23 version: 4.17.23 motion: - specifier: ^12.36.0 - version: 12.36.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: ^12.38.0 + version: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.4) @@ -131,10 +131,10 @@ importers: version: 5.91.3(@tanstack/react-query@5.90.21(react@19.2.4))(react@19.2.4) '@tanstack/react-router-devtools': specifier: ^1.166.9 - version: 1.166.9(@tanstack/react-router@1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.3)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.166.9(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.4)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/router-plugin': - specifier: ^1.166.12 - version: 1.166.12(@tanstack/react-router@1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.4)(sass@1.98.0)(tsx@4.21.0)) + specifier: ^1.166.13 + version: 1.166.13(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.4)(sass@1.98.0)(tsx@4.21.0)) '@types/byte-size': specifier: ^8.1.2 version: 8.1.2 @@ -251,12 +251,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} hasBin: true @@ -1175,8 +1175,8 @@ packages: '@tanstack/router-core': optional: true - '@tanstack/react-router@1.167.3': - resolution: {integrity: sha512-1qbSy4r+O7IBdmPLlcKsjB041Gq2MMnIEAYSGIjaMZIL4duUIQnOWLw4jTfjKil/IJz/9rO5JcvrbxOG5UTSdg==} + '@tanstack/react-router@1.167.4': + resolution: {integrity: sha512-VpbZh382zX3WF4+X2Z+EUyd8eJhJyjg9C6ByYwrVZiWbhgbMK4+zQQIG2+lCAlIlDi7SV8fDcGL09NA8Z2kpGQ==} engines: {node: '>=20.19'} peerDependencies: react: '>=18.0.0 || >=19.0.0' @@ -1201,9 +1201,10 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-core@1.167.3': - resolution: {integrity: sha512-M/CxrTGKk1fsySJjd+Pzpbi3YLDz+cJSutDjSTMy12owWlOgHV/I6kzR0UxyaBlHraM6XgMHNA0XdgsS1fa4Nw==} + '@tanstack/router-core@1.167.4': + resolution: {integrity: sha512-Gk5V9Zr5JFJ4SbLyCheQLJ3MnXddccENPA+DJRz+9g3QxtN8DJB8w8KCUCgDeYlWp4LvmO4nX3fy3tupqVP2Pw==} engines: {node: '>=20.19'} + hasBin: true '@tanstack/router-devtools-core@1.166.9': resolution: {integrity: sha512-PNlA7GmOUX9wY7LUG709Pk3Lg33dfHBztQwzjzrOiOsuf4ggp2R6bwarF8nYGNjG79z/MaB5PN+5yvkCVk8jGw==} @@ -1215,16 +1216,17 @@ packages: csstype: optional: true - '@tanstack/router-generator@1.166.11': - resolution: {integrity: sha512-Q/49wxURbft1oNOvo/eVAWZq/lNLK3nBGlavqhLToAYXY6LCzfMtRlE/y3XPHzYC9pZc09u5jvBR1k1E4hyGDQ==} + '@tanstack/router-generator@1.166.12': + resolution: {integrity: sha512-2HdxSTbCkbU9JeYogKVigIlXoLtIJE1x5rbEov+ZLTPjGCO9kicNQuljqg9Js+u2/ahtWewNrE5u1QCAyxmpIg==} engines: {node: '>=20.19'} - '@tanstack/router-plugin@1.166.12': - resolution: {integrity: sha512-PYsnN6goK6zBaVo63UVKjofv69+HHMKRQXymwN55JYKguNnNR8OZ6E12icPb0Olc5uIpPiGz1YI2+rbpmNKGHA==} + '@tanstack/router-plugin@1.166.13': + resolution: {integrity: sha512-xG3ND3AlMe6DN9PihJAYUbQJevqJvVdzN1QpZbfU1/jkHurL97ynP2yXfmMTh8Qgi1K+SWRko4bi7iZlYP9SUw==} engines: {node: '>=20.19'} + hasBin: true peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.167.3 + '@tanstack/react-router': ^1.167.4 vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' vite-plugin-solid: ^2.11.10 webpack: '>=5.92.0' @@ -1254,9 +1256,10 @@ packages: '@tanstack/virtual-core@3.13.23': resolution: {integrity: sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg==} - '@tanstack/virtual-file-routes@1.161.6': - resolution: {integrity: sha512-EGWs9yvJA821pUkwkiZLQW89CzUumHyJy8NKq229BubyoWXfDw1oWnTJYSS/hhbLiwP9+KpopjeF5wWwnCCyeQ==} + '@tanstack/virtual-file-routes@1.161.7': + resolution: {integrity: sha512-olW33+Cn+bsCsZKPwEGhlkqS6w3M2slFv11JIobdnCFKMLG97oAI2kWKdx5/zsywTL8flpnoIgaZZPlQTFYhdQ==} engines: {node: '>=20.19'} + hasBin: true '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -1471,8 +1474,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001779: - resolution: {integrity: sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==} + caniuse-lite@1.0.30001780: + resolution: {integrity: sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -1775,8 +1778,8 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - framer-motion@12.36.0: - resolution: {integrity: sha512-4PqYHAT7gev0ke0wos+PyrcFxI0HScjm3asgU8nSYa8YzJFuwgIvdj3/s3ZaxLq0bUSboIn19A2WS/MHwLCvfw==} + framer-motion@12.38.0: + resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2250,14 +2253,14 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - motion-dom@12.36.0: - resolution: {integrity: sha512-Ep1pq8P88rGJ75om8lTCA13zqd7ywPGwCqwuWwin6BKc0hMLkVfcS6qKlRqEo2+t0DwoUcgGJfXwaiFn4AOcQA==} + motion-dom@12.38.0: + resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} motion-utils@12.36.0: resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} - motion@12.36.0: - resolution: {integrity: sha512-5BMQuktYUX8aEByKWYx5tR4X3G08H2OMgp46wTxZ4o7CDDstyy4A0fe9RLNMjZiwvntCWGDvs16sC87/emz4Yw==} + motion@12.38.0: + resolution: {integrity: sha512-uYfXzeHlgThchzwz5Te47dlv5JOUC7OB4rjJ/7XTUgtBZD8CchMN8qEJ4ZVsUmTyYA44zjV0fBwsiktRuFnn+w==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2967,8 +2970,8 @@ snapshots: '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 @@ -2983,7 +2986,7 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -3023,12 +3026,12 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.6': + '@babel/helpers@7.29.2': dependencies: '@babel/template': 7.28.6 '@babel/types': 7.29.0 - '@babel/parser@7.29.0': + '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 @@ -3045,7 +3048,7 @@ snapshots: '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@babel/traverse@7.29.0': @@ -3053,7 +3056,7 @@ snapshots: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -3682,7 +3685,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 '@tanstack/devtools-client': 0.0.6 @@ -3758,22 +3761,22 @@ snapshots: '@tanstack/query-core': 5.90.20 react: 19.2.4 - '@tanstack/react-router-devtools@1.166.9(@tanstack/react-router@1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.3)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-router-devtools@1.166.9(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.4)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@tanstack/react-router': 1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-devtools-core': 1.166.9(@tanstack/router-core@1.167.3)(csstype@3.2.3) + '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/router-devtools-core': 1.166.9(@tanstack/router-core@1.167.4)(csstype@3.2.3) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) optionalDependencies: - '@tanstack/router-core': 1.167.3 + '@tanstack/router-core': 1.167.4 transitivePeerDependencies: - csstype - '@tanstack/react-router@1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/history': 1.161.6 '@tanstack/react-store': 0.9.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-core': 1.167.3 + '@tanstack/router-core': 1.167.4 isbot: 5.1.36 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -3799,7 +3802,7 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@tanstack/router-core@1.167.3': + '@tanstack/router-core@1.167.4': dependencies: '@tanstack/history': 1.161.6 '@tanstack/store': 0.9.2 @@ -3809,20 +3812,20 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-devtools-core@1.166.9(@tanstack/router-core@1.167.3)(csstype@3.2.3)': + '@tanstack/router-devtools-core@1.166.9(@tanstack/router-core@1.167.4)(csstype@3.2.3)': dependencies: - '@tanstack/router-core': 1.167.3 + '@tanstack/router-core': 1.167.4 clsx: 2.1.1 goober: 2.1.18(csstype@3.2.3) tiny-invariant: 1.3.3 optionalDependencies: csstype: 3.2.3 - '@tanstack/router-generator@1.166.11': + '@tanstack/router-generator@1.166.12': dependencies: - '@tanstack/router-core': 1.167.3 + '@tanstack/router-core': 1.167.4 '@tanstack/router-utils': 1.161.6 - '@tanstack/virtual-file-routes': 1.161.6 + '@tanstack/virtual-file-routes': 1.161.7 prettier: 3.8.1 recast: 0.23.11 source-map: 0.7.6 @@ -3831,7 +3834,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.166.12(@tanstack/react-router@1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.4)(sass@1.98.0)(tsx@4.21.0))': + '@tanstack/router-plugin@1.166.13(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.4)(sass@1.98.0)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) @@ -3839,15 +3842,15 @@ snapshots: '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 - '@tanstack/router-core': 1.167.3 - '@tanstack/router-generator': 1.166.11 + '@tanstack/router-core': 1.167.4 + '@tanstack/router-generator': 1.166.12 '@tanstack/router-utils': 1.161.6 - '@tanstack/virtual-file-routes': 1.161.6 + '@tanstack/virtual-file-routes': 1.161.7 chokidar: 3.6.0 unplugin: 2.3.11 zod: 3.25.76 optionalDependencies: - '@tanstack/react-router': 1.167.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.4)(sass@1.98.0)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -3856,7 +3859,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 ansis: 4.2.0 babel-dead-code-elimination: 1.0.12 @@ -3872,7 +3875,7 @@ snapshots: '@tanstack/virtual-core@3.13.23': {} - '@tanstack/virtual-file-routes@1.161.6': {} + '@tanstack/virtual-file-routes@1.161.7': {} '@tybys/wasm-util@0.10.1': dependencies: @@ -4006,7 +4009,7 @@ snapshots: autoprefixer@10.4.27(postcss@8.5.8): dependencies: browserslist: 4.28.1 - caniuse-lite: 1.0.30001779 + caniuse-lite: 1.0.30001780 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.8 @@ -4023,7 +4026,7 @@ snapshots: babel-dead-code-elimination@1.0.12: dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 transitivePeerDependencies: @@ -4042,7 +4045,7 @@ snapshots: browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.10.8 - caniuse-lite: 1.0.30001779 + caniuse-lite: 1.0.30001780 electron-to-chromium: 1.5.313 node-releases: 2.0.36 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -4069,7 +4072,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001779: {} + caniuse-lite@1.0.30001780: {} ccount@2.0.1: {} @@ -4338,9 +4341,9 @@ snapshots: fraction.js@5.3.4: {} - framer-motion@12.36.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + framer-motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - motion-dom: 12.36.0 + motion-dom: 12.38.0 motion-utils: 12.36.0 tslib: 2.8.1 optionalDependencies: @@ -4909,15 +4912,15 @@ snapshots: dependencies: mime-db: 1.52.0 - motion-dom@12.36.0: + motion-dom@12.38.0: dependencies: motion-utils: 12.36.0 motion-utils@12.36.0: {} - motion@12.36.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + motion@12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: - framer-motion: 12.36.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + framer-motion: 12.38.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4) tslib: 2.8.1 optionalDependencies: react: 19.2.4 diff --git a/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx b/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx index 1854fa3af5..b6049b3bd8 100644 --- a/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx +++ b/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx @@ -92,6 +92,7 @@ export const SetupEdgeComponentStep = () => { )} @@ -102,6 +103,7 @@ export const SetupEdgeComponentStep = () => { )} @@ -112,6 +114,7 @@ export const SetupEdgeComponentStep = () => { )} diff --git a/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx b/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx index 30e6f66dd4..148b1b8e63 100644 --- a/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx +++ b/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx @@ -8,6 +8,7 @@ import type { } from '../../shared/components/wizard/types'; import { WizardPage } from '../../shared/components/wizard/WizardPage/WizardPage'; import { + getLocationsCountQueryOptions, getMigrationStateQueryOptions, getSettingsQueryOptions, } from '../../shared/query'; @@ -22,21 +23,26 @@ import { MigrationWizardStart } from './steps/MigrationWizardStart'; import { useMigrationWizardStore } from './store/useMigrationWizardStore'; import { MigrationWizardStep, type MigrationWizardStepValue } from './types'; -const welcomePageConfig: WizardWelcomePageConfig = { - title: 'Welcome to Defguard Migration Wizard.', - subtitle: `We've detected your previous version 1.X so email.`, - content: , - docsText: `We'll guide you through the process step by step. For full details, see the migration guide following the link below.`, -} as const; - type ConfigurableSteps = Exclude; export const MigrationWizardPage = () => { + const { data: locationCount } = useSuspenseQuery(getLocationsCountQueryOptions); const { data: wizardState } = useSuspenseQuery(getMigrationStateQueryOptions); const { data: settings } = useSuspenseQuery(getSettingsQueryOptions); const activeStep = useMigrationWizardStore((s) => s.current_step); + const welcomePageConfig = useMemo( + (): WizardWelcomePageConfig => + ({ + title: 'Welcome to Defguard Migration Wizard.', + subtitle: `We've detected your pervious version with ${locationCount} number of locations.`, + content: , + docsText: `We'll guide you through the process step by step. For full details, see the migration guide following the link below.`, + }) as const, + [locationCount], + ); + const stepsConfig = useMemo( (): Record => ({ general: { @@ -111,10 +117,8 @@ export const MigrationWizardPage = () => { if (settings) { useMigrationWizardStore.setState({ defguard_url: settings.defguard_url, - default_admin_group_name: settings.default_admin_group_name, - default_authentication_period_days: settings.authentication_period_days, - default_mfa_code_timeout_seconds: settings.mfa_code_timeout_seconds, public_proxy_url: settings.public_proxy_url, + ip_or_domain: settings.public_proxy_url, }); } }, [settings]); diff --git a/web/src/pages/MigrationWizardPage/steps/MigrationWizardCAStep.tsx b/web/src/pages/MigrationWizardPage/steps/MigrationWizardCAStep.tsx index 37b260a6b1..e6f2133fdb 100644 --- a/web/src/pages/MigrationWizardPage/steps/MigrationWizardCAStep.tsx +++ b/web/src/pages/MigrationWizardPage/steps/MigrationWizardCAStep.tsx @@ -4,6 +4,7 @@ import z from 'zod'; import { useShallow } from 'zustand/react/shallow'; import { m } from '../../../paraglide/messages'; import api from '../../../shared/api/api'; +import type { User } from '../../../shared/api/types'; import { Controls } from '../../../shared/components/Controls/Controls'; import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; import { Button } from '../../../shared/defguard-ui/components/Button/Button'; @@ -14,6 +15,7 @@ import { Snackbar } from '../../../shared/defguard-ui/providers/snackbar/snackba import { ThemeSpacing } from '../../../shared/defguard-ui/types'; import { useAppForm } from '../../../shared/form'; import { formChangeLogic } from '../../../shared/formLogic'; +import { useAuth } from '../../../shared/hooks/useAuth'; import { useMigrationWizardStore } from '../store/useMigrationWizardStore'; type ValidityValue = 1 | 2 | 3 | 5 | 10; @@ -39,11 +41,12 @@ type CreateCAStoreValues = { }; export const MigrationWizardCAStep = () => { + const currentUser = useAuth((s) => s.user as User); const createCAdefaultValues = useMigrationWizardStore( useShallow( (s): CreateCAFormFields => ({ ca_common_name: s.ca_common_name, - ca_email: s.ca_email, + ca_email: s.ca_email.length ? s.ca_email : currentUser.email, ca_validity_period_years: s.ca_validity_period_years, }), ), @@ -114,6 +117,7 @@ export const MigrationWizardCAStep = () => { @@ -124,6 +128,7 @@ export const MigrationWizardCAStep = () => { )} diff --git a/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx b/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx index d4e7f23bb7..0d793a94f3 100644 --- a/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx +++ b/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx @@ -5,6 +5,7 @@ import { m } from '../../../paraglide/messages'; import { Controls } from '../../../shared/components/Controls/Controls'; import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; import { Button } from '../../../shared/defguard-ui/components/Button/Button'; +import { InfoBanner } from '../../../shared/defguard-ui/components/InfoBanner/InfoBanner'; import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../shared/defguard-ui/types'; import { useAppForm } from '../../../shared/form'; @@ -66,6 +67,12 @@ export const MigrationWizardEdgeComponentStep = () => { return ( + +
{ e.stopPropagation(); @@ -79,6 +86,7 @@ export const MigrationWizardEdgeComponentStep = () => { )} @@ -89,6 +97,7 @@ export const MigrationWizardEdgeComponentStep = () => { )} @@ -99,6 +108,7 @@ export const MigrationWizardEdgeComponentStep = () => { )} diff --git a/web/src/pages/MigrationWizardPage/steps/MigrationWizardGeneralConfigurationStep.tsx b/web/src/pages/MigrationWizardPage/steps/MigrationWizardGeneralConfigurationStep.tsx index 31f2032a47..f7e9a4281b 100644 --- a/web/src/pages/MigrationWizardPage/steps/MigrationWizardGeneralConfigurationStep.tsx +++ b/web/src/pages/MigrationWizardPage/steps/MigrationWizardGeneralConfigurationStep.tsx @@ -5,8 +5,10 @@ import { useShallow } from 'zustand/react/shallow'; import { m } from '../../../paraglide/messages'; import api from '../../../shared/api/api'; import { Controls } from '../../../shared/components/Controls/Controls'; +import { DescriptionBlock } from '../../../shared/components/DescriptionBlock/DescriptionBlock'; import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; import { Button } from '../../../shared/defguard-ui/components/Button/Button'; +import { Divider } from '../../../shared/defguard-ui/components/Divider/Divider'; import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; import { ThemeSpacing } from '../../../shared/defguard-ui/types'; import { useAppForm } from '../../../shared/form'; @@ -14,64 +16,40 @@ import { formChangeLogic } from '../../../shared/formLogic'; import { isValidDefguardUrl } from '../../../shared/utils/defguardUrl'; import { useMigrationWizardStore } from '../store/useMigrationWizardStore'; -type FormFields = StoreValues; - -type StoreValues = { - defguard_url: string; - default_admin_group_name: string; - default_authentication: number; - default_mfa_code_lifetime: number; - public_proxy_url: string; -}; - export const MigrationWizardGeneralConfigurationStep = () => { const { mutateAsync } = useMutation({ - mutationFn: api.migration.setGeneralConfig, + mutationFn: api.settings.patchSettings, meta: { invalidate: [['settings'], ['migration', 'state']], }, }); - const defaultValues = useMigrationWizardStore( - useShallow( - (s): FormFields => ({ - defguard_url: s.defguard_url, - default_admin_group_name: s.default_admin_group_name, - default_authentication: s.default_authentication_period_days, - default_mfa_code_lifetime: s.default_mfa_code_timeout_seconds, - public_proxy_url: s.public_proxy_url, - }), - ), - ); - const formSchema = useMemo( () => z.object({ defguard_url: z - .string({ - error: m.migration_wizard_general_config_error_defguard_url_required(), - }) - .min(1, m.migration_wizard_general_config_error_defguard_url_required()) .url(m.migration_wizard_general_config_error_invalid_url()) + .min(1, m.migration_wizard_general_config_error_defguard_url_required()) .refine( isValidDefguardUrl, m.migration_wizard_general_config_error_defguard_url_invalid_host(), ), - default_admin_group_name: z - .string() - .min(1, m.migration_wizard_general_config_error_admin_group_required()), - default_authentication: z - .number() - .min(1, m.migration_wizard_general_config_error_auth_period_min()), - default_mfa_code_lifetime: z - .number() - .min(60, m.migration_wizard_general_config_error_mfa_timeout_min()), public_proxy_url: z .url(m.migration_wizard_general_config_error_public_proxy_url_invalid()) .min(1, m.migration_wizard_general_config_error_public_proxy_url_required()), }), [], ); + type FormFields = z.infer; + + const defaultValues = useMigrationWizardStore( + useShallow( + (s): FormFields => ({ + defguard_url: s.defguard_url, + public_proxy_url: s.public_proxy_url, + }), + ), + ); const form = useAppForm({ defaultValues, @@ -84,9 +62,6 @@ export const MigrationWizardGeneralConfigurationStep = () => { await mutateAsync(value); useMigrationWizardStore.setState({ defguard_url: value.defguard_url, - default_admin_group_name: value.default_admin_group_name, - default_authentication_period_days: value.default_authentication, - default_mfa_code_timeout_seconds: value.default_mfa_code_lifetime, public_proxy_url: value.public_proxy_url, }); useMigrationWizardStore.getState().next(); @@ -103,6 +78,10 @@ export const MigrationWizardGeneralConfigurationStep = () => { }} > + +

{`This URL will be used to access and control Defguard. It should not be exposed to the Internet only to the internal or VPN network. You can learn more about our security approach in the video below.`}

+
+ {(field) => ( { /> )} - - - {(field) => ( - - )} - - - - {(field) => ( - - )} - - - - {(field) => ( - - )} - - + + +

{`This URL will be used to access and control Defguard. It should not be exposed to the Internet only to the internal or VPN network. You can learn more about our security approach in the video below.`}

+
+ {(field) => ( { @@ -195,6 +196,7 @@ export const SetupCertificateAuthorityStep = () => { )} diff --git a/web/src/pages/SetupPage/initial/steps/SetupEdgeComponentStep.tsx b/web/src/pages/SetupPage/initial/steps/SetupEdgeComponentStep.tsx index b42aee34f3..10ffae620a 100644 --- a/web/src/pages/SetupPage/initial/steps/SetupEdgeComponentStep.tsx +++ b/web/src/pages/SetupPage/initial/steps/SetupEdgeComponentStep.tsx @@ -86,6 +86,7 @@ export const SetupEdgeComponentStep = () => { )} @@ -96,6 +97,7 @@ export const SetupEdgeComponentStep = () => { )} @@ -106,6 +108,7 @@ export const SetupEdgeComponentStep = () => { )} diff --git a/web/src/shared/api/api.ts b/web/src/shared/api/api.ts index ca94310fb4..238c7b11d1 100644 --- a/web/src/shared/api/api.ts +++ b/web/src/shared/api/api.ts @@ -34,6 +34,7 @@ import type { AvailableLocationIpResponse, ChangeAccountActiveRequest, ChangeWebhookStateRequest, + CountResponse, CreateActivityLogStreamRequest, CreateAdminRequest, CreateCARequest, @@ -74,7 +75,6 @@ import type { LoginResponse, LoginResponseBasic, MfaCompleteResponse, - MigrationGeneralConfigRequest, MigrationWizardApiState, NetworkDevice, NetworkLocation, @@ -326,6 +326,7 @@ const api = { }), }, location: { + getCount: () => client.get(`/network/count`), getLocationsDisplay: () => client.get(`/network/display`), deleteLocation: (locationId: number) => client.delete(`/network/${locationId}`), getLocationsSummary: (from?: number) => @@ -533,8 +534,6 @@ const api = { updateMigrationState: (data: MigrationWizardApiState) => client.put(`/migration/state`, data), }, - setGeneralConfig: (data: MigrationGeneralConfigRequest) => - client.post(`/migration/general_config`, data), }, checkLicense: (data: { license: string }) => client.post('/license/check', data), diff --git a/web/src/shared/api/types.ts b/web/src/shared/api/types.ts index a0c9c13a23..4a74897b19 100644 --- a/web/src/shared/api/types.ts +++ b/web/src/shared/api/types.ts @@ -72,10 +72,11 @@ export interface SetGeneralConfigRequest { admin_username: string; } -export type MigrationGeneralConfigRequest = Omit< - SetGeneralConfigRequest, - 'admin_username' ->; +export interface MigrationGeneralConfigRequest { + defguard_url: string; + default_mfa_code_lifetime: number; + public_proxy_url: string; +} export interface SetAutoAdoptionUrlSettingsRequest { defguard_url: string; @@ -1103,6 +1104,10 @@ export interface AclCount { pending: number; } +export interface CountResponse { + count: number; +} + export interface AclDestination { id: number; parent_id: number | null; diff --git a/web/src/shared/defguard-ui b/web/src/shared/defguard-ui index df533f0bdb..345b718ab2 160000 --- a/web/src/shared/defguard-ui +++ b/web/src/shared/defguard-ui @@ -1 +1 @@ -Subproject commit df533f0bdb8a9dcf4a8dc7ab636a316e39f249c4 +Subproject commit 345b718ab2e066213689611d4c8fe90af943368f diff --git a/web/src/shared/query.ts b/web/src/shared/query.ts index cdeef2aada..4ede8e0dbd 100644 --- a/web/src/shared/query.ts +++ b/web/src/shared/query.ts @@ -16,6 +16,12 @@ export const getEnterpriseSettingsQueryOptions = queryOptions({ select: (resp) => resp.data, }); +export const getLocationsCountQueryOptions = queryOptions({ + queryFn: api.location.getCount, + queryKey: ['network', 'count'], + select: (resp) => resp.data.count, +}); + export const getLocationQueryOptions = (id: number) => queryOptions({ queryFn: () => api.location.getLocation(id), From fdd6bfff2610d47346afd9bb2541bf7f4d1f0cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=9Al=C4=99zak?= Date: Tue, 17 Mar 2026 12:41:51 +0100 Subject: [PATCH 2/5] fill the translation file for migration wizard --- web/messages/en/migration_wizard.json | 14 ++++++++++++++ .../MigrationWizardPage/MigrationWizardPage.tsx | 6 +++--- .../MigrationWizardConfirmationStep.tsx | 4 ++-- .../steps/MigrationWizardEdgeComponentStep.tsx | 2 +- .../MigrationWizardGeneralConfigurationStep.tsx | 8 ++++---- .../steps/MigrationWizardStart.tsx | 11 ++++------- .../store/useMigrationWizardStore.tsx | 3 ++- 7 files changed, 30 insertions(+), 18 deletions(-) diff --git a/web/messages/en/migration_wizard.json b/web/messages/en/migration_wizard.json index 0f35768dfd..4e4d9209ce 100644 --- a/web/messages/en/migration_wizard.json +++ b/web/messages/en/migration_wizard.json @@ -2,6 +2,13 @@ "$schema": "https://inlang.com/schema/inlang-message-format", "migration_wizard_title": "Migration Wizard", "migration_wizard_subtitle": "This wizard will guide you through migration-related configuration of your Defguard instance.", + "migration_wizard_welcome_title": "Welcome to Defguard Migration Wizard.", + "migration_wizard_welcome_subtitle": "We've detected your previous version with {count} locations.", + "migration_wizard_welcome_docs_text": "We'll guide you through the process step by step. For full details, see the migration guide following the link below.", + "migration_wizard_start_warning": "IMPORTANT: Until you finish this migration process your VPN locations will not work.", + "migration_wizard_start_explain_1": "We will first automatically upgrade the Core instance (what you see now), followed by the public communication component, Edge (called Proxy prior to 2.0).", + "migration_wizard_start_explain_2": "Next, each VPN location must be upgraded. This will likely require manual changes to your internal network (firewall rules), as the Core ↔ Gateway communication has changed: the Core now initiates the connection to the Gateway (in 1.x Gateway connected to Core).", + "migration_wizard_start_button": "Start migration process", "migration_wizard_step_general_config_label": "General Configuration", "migration_wizard_step_general_config_description": "Manage core details and connection parameters for your VPN location.", "migration_wizard_step_certificate_authority_label": "Certificate Authority", @@ -14,6 +21,11 @@ "migration_wizard_step_edge_adoption_description": "Review the system's checks and see if any issues need attention before deployment.", "migration_wizard_step_confirmation_label": "Confirmation", "migration_wizard_step_confirmation_description": "Your configuration was successful. You're all set.", + "migration_wizard_general_config_private_url_title": "Private URL", + "migration_wizard_general_config_private_url_description": "This URL will be used to access and control Defguard. It should not be exposed to the Internet only to the internal or VPN network. You can learn more about our security approach in the video below.", + "migration_wizard_general_config_public_url_title": "Public URL", + "migration_wizard_general_config_public_url_description": "This URL will be used to access and control Defguard. It should not be exposed to the Internet only to the internal or VPN network. You can learn more about our security approach in the video below.", + "migration_wizard_edge_component_info": "We've detected your current Proxy setup. Please upgrade it to the latest 2.0 Edge component so it can be adopted automatically and managed by Defguard.", "migration_wizard_confirmation_title": "Initial system migration are complete.", "migration_wizard_confirmation_subtitle": "You've completed the first stage of the migration. Defguard is almost ready to go.", "migration_wizard_confirmation_locations_info": "You currently have {count} VPN locations configured. These locations must be upgraded.", @@ -24,6 +36,8 @@ "migration_wizard_confirmation_prepare_network_title": "Prepare your network", "migration_wizard_confirmation_prepare_network_subtitle": "Please prepare all required network and firewall changes before starting the migration. Once ready, we'll begin adopting the upgraded gateway components for each VPN location.", "migration_wizard_confirmation_checkbox_label": "I have changed all my gateways firewall rules and network setup", + "migration_wizard_finish_success_snackbar": "Migration finished", + "migration_wizard_finish_error_snackbar": "Finishing migration failed", "migration_wizard_ca_validity_one_year": "1 year", "migration_wizard_ca_validity_years": "{years} years", "migration_wizard_general_config_error_invalid_url": "Invalid URL", diff --git a/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx b/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx index 148b1b8e63..5d692eedf5 100644 --- a/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx +++ b/web/src/pages/MigrationWizardPage/MigrationWizardPage.tsx @@ -35,10 +35,10 @@ export const MigrationWizardPage = () => { const welcomePageConfig = useMemo( (): WizardWelcomePageConfig => ({ - title: 'Welcome to Defguard Migration Wizard.', - subtitle: `We've detected your pervious version with ${locationCount} number of locations.`, + title: m.migration_wizard_welcome_title(), + subtitle: m.migration_wizard_welcome_subtitle({ count: locationCount }), content: , - docsText: `We'll guide you through the process step by step. For full details, see the migration guide following the link below.`, + docsText: m.migration_wizard_welcome_docs_text(), }) as const, [locationCount], ); diff --git a/web/src/pages/MigrationWizardPage/steps/MigrationWizardConfirmationStep/MigrationWizardConfirmationStep.tsx b/web/src/pages/MigrationWizardPage/steps/MigrationWizardConfirmationStep/MigrationWizardConfirmationStep.tsx index 9f83de1724..4071f688f7 100644 --- a/web/src/pages/MigrationWizardPage/steps/MigrationWizardConfirmationStep/MigrationWizardConfirmationStep.tsx +++ b/web/src/pages/MigrationWizardPage/steps/MigrationWizardConfirmationStep/MigrationWizardConfirmationStep.tsx @@ -52,14 +52,14 @@ export const MigrationWizardConfirmationStep = () => { const { mutate: finish, isPending: finishPending } = useMutation({ mutationFn: migrationWizardFinishPromise, onSuccess: async () => { - Snackbar.default(`Migration finished`); + Snackbar.default(m.migration_wizard_finish_success_snackbar()); await navigate({ to: '/vpn-overview', replace: true }); setTimeout(() => { useMigrationWizardStore.getState().resetState(); }, 2500); }, onError: (e) => { - Snackbar.error(`Finishing migration failed`); + Snackbar.error(m.migration_wizard_finish_error_snackbar()); console.error(e); }, meta: { diff --git a/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx b/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx index 0d793a94f3..9f12c0270e 100644 --- a/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx +++ b/web/src/pages/MigrationWizardPage/steps/MigrationWizardEdgeComponentStep.tsx @@ -70,7 +70,7 @@ export const MigrationWizardEdgeComponentStep = () => { { }} > - -

{`This URL will be used to access and control Defguard. It should not be exposed to the Internet only to the internal or VPN network. You can learn more about our security approach in the video below.`}

+ +

{m.migration_wizard_general_config_private_url_description()}

@@ -92,8 +92,8 @@ export const MigrationWizardGeneralConfigurationStep = () => { )} - -

{`This URL will be used to access and control Defguard. It should not be exposed to the Internet only to the internal or VPN network. You can learn more about our security approach in the video below.`}

+ +

{m.migration_wizard_general_config_public_url_description()}

diff --git a/web/src/pages/MigrationWizardPage/steps/MigrationWizardStart.tsx b/web/src/pages/MigrationWizardPage/steps/MigrationWizardStart.tsx index b66b831550..183eea2bd6 100644 --- a/web/src/pages/MigrationWizardPage/steps/MigrationWizardStart.tsx +++ b/web/src/pages/MigrationWizardPage/steps/MigrationWizardStart.tsx @@ -1,3 +1,4 @@ +import { m } from '../../../paraglide/messages'; import { Controls } from '../../../shared/components/Controls/Controls'; import { Button } from '../../../shared/defguard-ui/components/Button/Button'; import { IconKind } from '../../../shared/defguard-ui/components/Icon'; @@ -14,20 +15,20 @@ export const MigrationWizardStart = () => {
${explain2}`} + content={`${m.migration_wizard_start_explain_1()}

${m.migration_wizard_start_explain_2()}`} />