From 652089d6b9caaef050bab1d0ac051b8df60a2d80 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Tue, 3 Feb 2026 15:28:15 +0100 Subject: [PATCH 1/4] fix issues with connecting to proxy after setup --- crates/defguard/src/main.rs | 22 +++++----------------- web/src/routeTree.gen.ts | 32 ++++++++++++++++++-------------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/crates/defguard/src/main.rs b/crates/defguard/src/main.rs index 4e5c40380f..562c05c9ec 100644 --- a/crates/defguard/src/main.rs +++ b/crates/defguard/src/main.rs @@ -9,10 +9,7 @@ use defguard_common::{ config::{Command, DefGuardConfig, SERVER_CONFIG}, db::{ init_db, - models::{ - Settings, - settings::{initialize_current_settings, update_current_settings}, - }, + models::{Settings, settings::initialize_current_settings}, }, messages::peer_stats_update::PeerStatsUpdate, types::proxy::ProxyControlMessage, @@ -111,8 +108,10 @@ async fn main() -> Result<(), anyhow::Error> { if let Err(err) = run_setup_web_server(pool.clone(), config.http_bind_address, config.http_port).await { - error!("Setup web server exited with error: {err}"); + anyhow::bail!("Setup web server exited with error: {err}"); } + + settings = Settings::get_current_settings(); } config.initialize_post_settings(); @@ -144,18 +143,7 @@ async fn main() -> Result<(), anyhow::Error> { let incompatible_components: Arc> = Arc::default(); if settings.ca_cert_der.is_none() || settings.ca_key_der.is_none() { - info!( - "No gRPC TLS certificate or key found in settings, generating self-signed certificate for gRPC server." - ); - - let ca = defguard_certs::CertificateAuthority::new("Defguard", "", 10)?; - - let (cert_der, key_der) = (ca.cert_der().to_vec(), ca.key_pair_der().to_vec()); - - settings.ca_cert_der = Some(cert_der); - settings.ca_key_der = Some(key_der); - - update_current_settings(&pool, settings).await?; + anyhow::bail!("CA certificate or key were not found in settings, despite completing setup.") } // read grpc TLS cert and key diff --git a/web/src/routeTree.gen.ts b/web/src/routeTree.gen.ts index f1d8040d34..e22adc19ec 100644 --- a/web/src/routeTree.gen.ts +++ b/web/src/routeTree.gen.ts @@ -327,6 +327,7 @@ const AuthorizedDefaultEdgeEdgeIdEditRoute = export interface FileRoutesByFullPath { '/404': typeof R404Route + '/': typeof AuthorizedDefaultRouteWithChildren '/auth': typeof AuthRouteWithChildren '/consent': typeof ConsentRoute '/playground': typeof PlaygroundRoute @@ -366,15 +367,16 @@ export interface FileRoutesByFullPath { '/settings/smtp': typeof AuthorizedDefaultSettingsSmtpRoute '/user/$username': typeof AuthorizedDefaultUserUsernameRoute '/vpn-overview/$locationId': typeof AuthorizedDefaultVpnOverviewLocationIdRoute - '/edge': typeof AuthorizedDefaultEdgeIndexRoute - '/locations': typeof AuthorizedDefaultLocationsIndexRoute - '/settings': typeof AuthorizedDefaultSettingsIndexRoute - '/vpn-overview': typeof AuthorizedDefaultVpnOverviewIndexRoute + '/edge/': typeof AuthorizedDefaultEdgeIndexRoute + '/locations/': typeof AuthorizedDefaultLocationsIndexRoute + '/settings/': typeof AuthorizedDefaultSettingsIndexRoute + '/vpn-overview/': typeof AuthorizedDefaultVpnOverviewIndexRoute '/edge/$edgeId/edit': typeof AuthorizedDefaultEdgeEdgeIdEditRoute '/locations/$locationId/edit': typeof AuthorizedDefaultLocationsLocationIdEditRoute } export interface FileRoutesByTo { '/404': typeof R404Route + '/': typeof AuthorizedDefaultRouteWithChildren '/consent': typeof ConsentRoute '/playground': typeof PlaygroundRoute '/snackbar': typeof SnackbarRoute @@ -475,6 +477,7 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/404' + | '/' | '/auth' | '/consent' | '/playground' @@ -514,15 +517,16 @@ export interface FileRouteTypes { | '/settings/smtp' | '/user/$username' | '/vpn-overview/$locationId' - | '/edge' - | '/locations' - | '/settings' - | '/vpn-overview' + | '/edge/' + | '/locations/' + | '/settings/' + | '/vpn-overview/' | '/edge/$edgeId/edit' | '/locations/$locationId/edit' fileRoutesByTo: FileRoutesByTo to: | '/404' + | '/' | '/consent' | '/playground' | '/snackbar' @@ -662,7 +666,7 @@ declare module '@tanstack/react-router' { '/_authorized': { id: '/_authorized' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof AuthorizedRouteImport parentRoute: typeof rootRouteImport } @@ -718,7 +722,7 @@ declare module '@tanstack/react-router' { '/_authorized/_default': { id: '/_authorized/_default' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof AuthorizedDefaultRouteImport parentRoute: typeof AuthorizedRoute } @@ -823,28 +827,28 @@ declare module '@tanstack/react-router' { '/_authorized/_default/vpn-overview/': { id: '/_authorized/_default/vpn-overview/' path: '/vpn-overview' - fullPath: '/vpn-overview' + fullPath: '/vpn-overview/' preLoaderRoute: typeof AuthorizedDefaultVpnOverviewIndexRouteImport parentRoute: typeof AuthorizedDefaultRoute } '/_authorized/_default/settings/': { id: '/_authorized/_default/settings/' path: '/settings' - fullPath: '/settings' + fullPath: '/settings/' preLoaderRoute: typeof AuthorizedDefaultSettingsIndexRouteImport parentRoute: typeof AuthorizedDefaultRoute } '/_authorized/_default/locations/': { id: '/_authorized/_default/locations/' path: '/locations' - fullPath: '/locations' + fullPath: '/locations/' preLoaderRoute: typeof AuthorizedDefaultLocationsIndexRouteImport parentRoute: typeof AuthorizedDefaultRoute } '/_authorized/_default/edge/': { id: '/_authorized/_default/edge/' path: '/edge' - fullPath: '/edge' + fullPath: '/edge/' preLoaderRoute: typeof AuthorizedDefaultEdgeIndexRouteImport parentRoute: typeof AuthorizedDefaultRoute } From 9cd5599d9720a01f2d167fb3250421a97f82b7e8 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:39:38 +0100 Subject: [PATCH 2/4] auto login, redirect to location setup, adaptation -> adoption, tests --- Cargo.lock | 2 + crates/defguard_common/src/config.rs | 1 + crates/defguard_common/src/db/models/proxy.rs | 4 +- .../defguard_common/src/db/models/settings.rs | 15 +- crates/defguard_core/src/grpc/mod.rs | 9 +- crates/defguard_core/src/handlers/auth.rs | 2 +- .../src/handlers/component_setup.rs | 1 - crates/defguard_core/src/handlers/mod.rs | 4 +- .../src/handlers/network_devices.rs | 12 +- crates/defguard_core/src/handlers/user.rs | 23 +- crates/defguard_core/src/headers.rs | 2 +- .../tests/integration/api/proxy.rs | 7 +- crates/defguard_proxy_manager/src/lib.rs | 4 +- .../src/password_reset.rs | 14 +- crates/defguard_setup/Cargo.toml | 9 + crates/defguard_setup/src/handlers.rs | 42 +- crates/defguard_setup/tests/initial_setup.rs | 470 ++++++++++++++++++ ...0.0]_public_proxy_url_in_settings.down.sql | 2 + ...2.0.0]_public_proxy_url_in_settings.up.sql | 2 + web/messages/en/edge_wizard.json | 30 +- web/messages/en/gateway_wizard.json | 28 +- web/messages/en/initial_wizard.json | 9 +- web/src/pages/EdgeSetupPage/EdgeSetupPage.tsx | 12 +- ...tionStep.tsx => SetupEdgeAdoptionStep.tsx} | 81 ++- .../steps/SetupEdgeComponentStep.tsx | 16 +- web/src/pages/EdgeSetupPage/types.ts | 4 +- .../EdgeSetupPage/useEdgeWizardStore.tsx | 30 +- .../GatewaySetupPage/GatewaySetupPage.tsx | 12 +- .../steps/SetupGatewayAdaptationStep.tsx | 85 ++-- .../steps/SetupGatewayComponentStep.tsx | 2 +- web/src/pages/GatewaySetupPage/types.ts | 2 +- .../useGatewayWizardStore.tsx | 24 +- web/src/pages/SetupPage/SetupPage.tsx | 13 +- .../SetupPage/steps/SetupConfirmationStep.tsx | 45 +- ...tionStep.tsx => SetupEdgeAdoptionStep.tsx} | 99 ++-- .../steps/SetupEdgeComponentStep.tsx | 17 +- .../steps/SetupGeneralConfigStep.tsx | 16 + web/src/pages/SetupPage/steps/style.scss | 6 + web/src/pages/SetupPage/types.ts | 2 +- .../pages/SetupPage/useSetupWizardStore.tsx | 28 +- web/src/shared/api/types.ts | 1 + .../wizard/WizardPage/WizardPage.tsx | 4 +- .../components/wizard/WizardTop/WizardTop.tsx | 4 +- 43 files changed, 862 insertions(+), 333 deletions(-) create mode 100644 crates/defguard_setup/tests/initial_setup.rs create mode 100644 migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql create mode 100644 migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql rename web/src/pages/EdgeSetupPage/steps/{SetupEdgeAdaptationStep.tsx => SetupEdgeAdoptionStep.tsx} (62%) rename web/src/pages/SetupPage/steps/{SetupEdgeAdaptationStep.tsx => SetupEdgeAdoptionStep.tsx} (57%) diff --git a/Cargo.lock b/Cargo.lock index 3f5b47a941..f9384f7d7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1396,6 +1396,8 @@ version = "0.0.0" dependencies = [ "anyhow", "axum", + "axum-client-ip", + "axum-extra", "chrono", "defguard_certs", "defguard_common", diff --git a/crates/defguard_common/src/config.rs b/crates/defguard_common/src/config.rs index 75347fe015..5b7090e081 100644 --- a/crates/defguard_common/src/config.rs +++ b/crates/defguard_common/src/config.rs @@ -112,6 +112,7 @@ pub struct DefGuardConfig { pub stats_purge_threshold: Duration, #[arg(long, env = "DEFGUARD_ENROLLMENT_URL", value_parser = Url::parse, default_value = "http://localhost:8080")] + #[deprecated(since = "2.0.0", note = "Use Settings.public_proxy_url instead")] pub enrollment_url: Url, #[arg(long, env = "DEFGUARD_ENROLLMENT_TOKEN_TIMEOUT", default_value = "24h")] diff --git a/crates/defguard_common/src/db/models/proxy.rs b/crates/defguard_common/src/db/models/proxy.rs index 5b20797702..854b093e57 100644 --- a/crates/defguard_common/src/db/models/proxy.rs +++ b/crates/defguard_common/src/db/models/proxy.rs @@ -14,7 +14,6 @@ pub struct Proxy { pub name: String, pub address: String, pub port: i32, - pub public_address: String, pub connected_at: Option, pub disconnected_at: Option, pub version: Option, @@ -35,13 +34,12 @@ impl fmt::Display for Proxy { } impl Proxy { - pub fn new>(name: S, address: S, port: i32, public_address: S) -> Self { + pub fn new>(name: S, address: S, port: i32) -> Self { Self { id: NoId, name: name.into(), address: address.into(), port, - public_address: public_address.into(), connected_at: None, disconnected_at: None, has_certificate: false, diff --git a/crates/defguard_common/src/db/models/settings.rs b/crates/defguard_common/src/db/models/settings.rs index edaab31866..0463f492da 100644 --- a/crates/defguard_common/src/db/models/settings.rs +++ b/crates/defguard_common/src/db/models/settings.rs @@ -149,11 +149,13 @@ pub struct Settings { pub ca_key_der: Option>, pub ca_cert_der: Option>, pub ca_expiry: Option, + // Initial setup, general settings pub initial_setup_completed: bool, pub defguard_url: String, pub default_admin_group_name: String, pub authentication_period_days: i32, pub mfa_code_timeout_seconds: i32, + pub public_proxy_url: String, } // Implement manually to avoid exposing the license key. @@ -271,7 +273,8 @@ impl Settings { ldap_user_rdn_attr, ldap_sync_groups, \ openid_username_handling \"openid_username_handling: OpenIdUsernameHandling\", \ ca_key_der, ca_cert_der, ca_expiry, initial_setup_completed, \ - defguard_url, default_admin_group_name, authentication_period_days, mfa_code_timeout_seconds \ + defguard_url, default_admin_group_name, authentication_period_days, mfa_code_timeout_seconds, \ + public_proxy_url \ FROM \"settings\" WHERE id = 1", ) .fetch_optional(executor) @@ -356,7 +359,8 @@ impl Settings { defguard_url = $53, \ default_admin_group_name = $54, \ authentication_period_days = $55, \ - mfa_code_timeout_seconds = $56 \ + mfa_code_timeout_seconds = $56, \ + public_proxy_url = $57 \ WHERE id = 1", self.openid_enabled, self.wireguard_enabled, @@ -413,7 +417,8 @@ impl Settings { self.defguard_url, self.default_admin_group_name, self.authentication_period_days, - self.mfa_code_timeout_seconds + self.mfa_code_timeout_seconds, + self.public_proxy_url ) .execute(executor) .await?; @@ -493,6 +498,10 @@ impl Settings { pub fn authentication_timeout(&self) -> Duration { Duration::from_secs(self.authentication_period_days as u64 * 24 * 3600) } + + pub fn proxy_public_url(&self) -> Result { + Url::parse(&self.public_proxy_url) + } } #[derive(Serialize)] diff --git a/crates/defguard_core/src/grpc/mod.rs b/crates/defguard_core/src/grpc/mod.rs index 4e17811cd0..b582e18b55 100644 --- a/crates/defguard_core/src/grpc/mod.rs +++ b/crates/defguard_core/src/grpc/mod.rs @@ -176,17 +176,16 @@ impl InstanceInfo { enterprise_settings: &EnterpriseSettings, openid_provider: Option>, ) -> Result { - let config = server_config(); let openid_display_name = openid_provider .as_ref() .map(|provider| provider.display_name.clone()) .unwrap_or_default(); let url = Settings::url()?; - Ok(InstanceInfo { + Ok(Self { id: settings.uuid, - name: settings.instance_name, - url: url.clone(), - proxy_url: config.enrollment_url.clone(), + name: settings.instance_name.clone(), + url, + proxy_url: settings.proxy_public_url()?, username: username.into(), client_traffic_policy: enterprise_settings.client_traffic_policy, enterprise_enabled: is_business_license_active(), diff --git a/crates/defguard_core/src/handlers/auth.rs b/crates/defguard_core/src/handlers/auth.rs index e81d13aef8..ac1ba97774 100644 --- a/crates/defguard_core/src/handlers/auth.rs +++ b/crates/defguard_core/src/handlers/auth.rs @@ -54,7 +54,7 @@ use crate::{ /// Common functionality for `authenticate()` and `auth_callback()`. /// Returns either `AuthResponse` or `MFAInfo`. -pub(crate) async fn create_session( +pub async fn create_session( pool: &PgPool, mail_tx: &UnboundedSender, ip_address: IpAddr, diff --git a/crates/defguard_core/src/handlers/component_setup.rs b/crates/defguard_core/src/handlers/component_setup.rs index e004c700c1..641e8291b0 100644 --- a/crates/defguard_core/src/handlers/component_setup.rs +++ b/crates/defguard_core/src/handlers/component_setup.rs @@ -532,7 +532,6 @@ pub async fn setup_proxy_tls_stream( &request.common_name, &request.ip_or_domain, i32::from(request.grpc_port), - &request.ip_or_domain, ); proxy.has_certificate = true; diff --git a/crates/defguard_core/src/handlers/mod.rs b/crates/defguard_core/src/handlers/mod.rs index cc2223ec6d..bb5ff434ba 100644 --- a/crates/defguard_core/src/handlers/mod.rs +++ b/crates/defguard_core/src/handlers/mod.rs @@ -29,7 +29,7 @@ use crate::{ pub(crate) mod activity_log; pub(crate) mod app_info; -pub(crate) mod auth; +pub mod auth; pub mod component_setup; pub(crate) mod forward_auth; pub(crate) mod group; @@ -49,7 +49,7 @@ pub mod wireguard; pub mod worker; pub(crate) mod yubikey; -pub(crate) static SESSION_COOKIE_NAME: &str = "defguard_session"; +pub static SESSION_COOKIE_NAME: &str = "defguard_session"; pub(crate) static SIGN_IN_COOKIE_NAME: &str = "defguard_sign_in"; pub(crate) const SIGN_IN_COOKIE_MAX_AGE: time::Duration = time::Duration::minutes(10); pub(crate) const DEFAULT_API_PAGE_SIZE: u32 = 50; diff --git a/crates/defguard_core/src/handlers/network_devices.rs b/crates/defguard_core/src/handlers/network_devices.rs index 59ddf4bca4..155d304029 100644 --- a/crates/defguard_core/src/handlers/network_devices.rs +++ b/crates/defguard_core/src/handlers/network_devices.rs @@ -13,7 +13,7 @@ use defguard_common::{ db::{ Id, models::{ - Device, DeviceConfig, DeviceType, User, WireguardNetwork, + Device, DeviceConfig, DeviceType, Settings, User, WireguardNetwork, device::{DeviceInfo, WireguardNetworkDevice}, wireguard::NetworkAddressError, }, @@ -450,13 +450,14 @@ pub(crate) async fn start_network_device_setup( device: NetworkDeviceInfo::from_device(device, &mut transaction).await?, }; let config = server_config(); + let settings = Settings::get_current_settings(); let configuration_token = start_desktop_configuration( &user, &mut transaction, &user, None, config.enrollment_token_timeout.as_secs(), - config.enrollment_url.clone(), + settings.proxy_public_url()?.clone(), false, appstate.mail_tx.clone(), Some(result.device.id), @@ -473,7 +474,7 @@ pub(crate) async fn start_network_device_setup( transaction.commit().await?; Ok(ApiResponse::new( - json!({"enrollment_token": configuration_token, "enrollment_url": config.enrollment_url.to_string()}), + json!({"enrollment_token": configuration_token, "enrollment_url": settings.proxy_public_url()?.to_string()}), StatusCode::CREATED, )) } @@ -516,13 +517,14 @@ pub(crate) async fn start_network_device_setup_for_device( )) })?; let config = server_config(); + let settings = Settings::get_current_settings(); let configuration_token = start_desktop_configuration( &user, &mut transaction, &user, None, config.enrollment_token_timeout.as_secs(), - config.enrollment_url.clone(), + settings.proxy_public_url()?, false, appstate.mail_tx.clone(), Some(device.id), @@ -538,7 +540,7 @@ pub(crate) async fn start_network_device_setup_for_device( Ok(ApiResponse::new( json!({ "enrollment_token": configuration_token, - "enrollment_url": config.enrollment_url.to_string() + "enrollment_url": settings.proxy_public_url()?.to_string() }), StatusCode::CREATED, )) diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index 5007397b6d..99f77a3706 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -8,7 +8,7 @@ use defguard_common::{ db::{ Id, models::{ - BiometricAuth, OAuth2AuthorizedApp, User, WebAuthn, device::UserDevice, + BiometricAuth, OAuth2AuthorizedApp, Settings, User, WebAuthn, device::UserDevice, user::SecurityKey, }, }, @@ -463,13 +463,16 @@ pub async fn start_enrollment( None => config.enrollment_token_timeout.as_secs(), }; + let settings: Settings = Settings::get_current_settings(); + let public_proxy_url = settings.proxy_public_url()?; + let enrollment_token = start_user_enrollment( &mut user, &mut transaction, &session.user, data.email, token_expiration_time_seconds, - config.enrollment_url.clone(), + public_proxy_url.clone(), data.send_enrollment_notification, appstate.mail_tx.clone(), ) @@ -486,7 +489,7 @@ pub async fn start_enrollment( debug!( "Enrollment token {}, enrollment url {}", enrollment_token, - config.enrollment_url.to_string() + public_proxy_url.to_string() ); appstate.emit_event(ApiEvent { context, @@ -494,7 +497,7 @@ pub async fn start_enrollment( })?; Ok(ApiResponse::new( - json!({"enrollment_token": enrollment_token, "enrollment_url": config.enrollment_url.to_string()}), + json!({"enrollment_token": enrollment_token, "enrollment_url": public_proxy_url.to_string()}), StatusCode::CREATED, )) } @@ -566,13 +569,15 @@ pub async fn start_remote_desktop_configuration( session.user.username ); let config = server_config(); + let settings = Settings::get_current_settings(); + let public_proxy_url = settings.proxy_public_url()?; let desktop_configuration_token = start_desktop_configuration( &user, &mut transaction, &session.user, Some(email), config.enrollment_token_timeout.as_secs(), - config.enrollment_url.clone(), + public_proxy_url.clone(), data.send_enrollment_notification, appstate.mail_tx.clone(), None, @@ -590,7 +595,7 @@ pub async fn start_remote_desktop_configuration( debug!( "Desktop configuration token {}, desktop configuration url {}", desktop_configuration_token, - config.enrollment_url.to_string() + public_proxy_url.to_string() ); appstate.emit_event(ApiEvent { context, @@ -598,7 +603,7 @@ pub async fn start_remote_desktop_configuration( })?; Ok(ApiResponse::new( - json!({"enrollment_token": desktop_configuration_token, "enrollment_url": config.enrollment_url.to_string()}), + json!({"enrollment_token": desktop_configuration_token, "enrollment_url": public_proxy_url.to_string()}), StatusCode::CREATED, )) } @@ -1098,12 +1103,14 @@ pub async fn reset_password( Some(PASSWORD_RESET_TOKEN_TYPE.to_string()), ); enrollment.save(&mut *transaction).await?; + let settings = Settings::get_current_settings(); + let public_proxy_url = settings.proxy_public_url()?; let mail = Mail { to: user.email.clone(), subject: EMAIL_PASSWORD_RESET_START_SUBJECT.into(), content: templates::email_password_reset_mail( - config.enrollment_url.clone(), + public_proxy_url, enrollment.id.clone().as_str(), None, None, diff --git a/crates/defguard_core/src/headers.rs b/crates/defguard_core/src/headers.rs index c15f2cda40..f6627b5434 100644 --- a/crates/defguard_core/src/headers.rs +++ b/crates/defguard_core/src/headers.rs @@ -20,7 +20,7 @@ pub(crate) const CONTENT_SECURITY_POLICY_HEADER_NAME: HeaderName = pub(crate) const CONTENT_SECURITY_POLICY_HEADER_VALUE: HeaderValue = HeaderValue::from_static("frame-ancestors 'none';"); -pub(crate) static USER_AGENT_PARSER: LazyLock = LazyLock::new(|| { +pub static USER_AGENT_PARSER: LazyLock = LazyLock::new(|| { let regexes = include_bytes!("../user_agent_header_regexes.yaml"); UserAgentParser::from_bytes(regexes).expect("Parser creation failed") }); diff --git a/crates/defguard_core/tests/integration/api/proxy.rs b/crates/defguard_core/tests/integration/api/proxy.rs index 0cead7ec97..886c39999c 100644 --- a/crates/defguard_core/tests/integration/api/proxy.rs +++ b/crates/defguard_core/tests/integration/api/proxy.rs @@ -17,7 +17,7 @@ async fn test_proxy_details(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(response.status(), StatusCode::OK); // Create new proxy. - let proxy = Proxy::new("test", "localhost", 50051, "public.net") + let proxy = Proxy::new("test", "localhost", 50051) .save(&pool) .await .unwrap(); @@ -46,7 +46,7 @@ async fn test_proxy_update(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(response.status(), StatusCode::OK); // Create new proxy. - let mut proxy = Proxy::new("test", "localhost", 50051, "public.net") + let mut proxy = Proxy::new("test", "localhost", 50051) .save(&pool) .await .unwrap(); @@ -72,7 +72,6 @@ async fn test_proxy_update(_: PgPoolOptions, options: PgConnectOptions) { let proxy_before_mods = proxy.clone(); proxy.address = "otherhost".to_string(); proxy.port = 50052; - proxy.public_address = "otherpublichost.net".to_string(); let response = client .put(format!("/api/v1/proxy/{}", proxy.id)) .json(&proxy) @@ -95,7 +94,7 @@ async fn test_delete_proxy(_: PgPoolOptions, options: PgConnectOptions) { assert_eq!(response.status(), StatusCode::OK); // Create new proxy. - let proxy = Proxy::new("test", "localhost", 50051, "public.net") + let proxy = Proxy::new("test", "localhost", 50051) .save(&pool) .await .unwrap(); diff --git a/crates/defguard_proxy_manager/src/lib.rs b/crates/defguard_proxy_manager/src/lib.rs index 13547b1ca3..e9d86cd4db 100644 --- a/crates/defguard_proxy_manager/src/lib.rs +++ b/crates/defguard_proxy_manager/src/lib.rs @@ -917,10 +917,12 @@ impl ProxyServer { "Saved desktop configuration token. Responding to \ proxy with the token." ); + let settings = Settings::get_current_settings(); + let public_proxy_url = settings.proxy_public_url()?; Some(core_response::Payload::AuthCallback( AuthCallbackResponse { - url: config.enrollment_url.clone().into(), + url: public_proxy_url.into(), token: desktop_configuration.id, }, )) diff --git a/crates/defguard_proxy_manager/src/password_reset.rs b/crates/defguard_proxy_manager/src/password_reset.rs index 208b3e5260..41adbf6011 100644 --- a/crates/defguard_proxy_manager/src/password_reset.rs +++ b/crates/defguard_proxy_manager/src/password_reset.rs @@ -1,4 +1,7 @@ -use defguard_common::{config::server_config, db::models::User}; +use defguard_common::{ + config::server_config, + db::models::{Settings, User}, +}; use defguard_core::{ db::models::enrollment::{PASSWORD_RESET_TOKEN_TYPE, Token}, enterprise::ldap::utils::ldap_change_password, @@ -150,10 +153,17 @@ impl PasswordResetServer { error!("Failed to commit transaction"); Status::internal("unexpected error") })?; + + let settings = Settings::get_current_settings(); + let public_proxy_url = settings.proxy_public_url().map_err(|err| { + error!("Failed to get public proxy URL: {err}"); + Status::internal("unexpected error") + })?; + send_password_reset_email( &user, &self.mail_tx, - config.enrollment_url.clone(), + public_proxy_url, &enrollment.id, Some(&ip_address), Some(&device_info), diff --git a/crates/defguard_setup/Cargo.toml b/crates/defguard_setup/Cargo.toml index 65c9026dec..e68fc756ab 100644 --- a/crates/defguard_setup/Cargo.toml +++ b/crates/defguard_setup/Cargo.toml @@ -23,3 +23,12 @@ tracing.workspace = true serde.workspace = true chrono.workspace = true defguard_version.workspace = true +axum-extra.workspace = true +axum-client-ip.workspace = true + +[dev-dependencies] +reqwest = { version = "0.12", features = [ + "cookies", + "json", + "rustls-tls", +], default-features = false } diff --git a/crates/defguard_setup/src/handlers.rs b/crates/defguard_setup/src/handlers.rs index 0b7e145d7f..c8525516d9 100644 --- a/crates/defguard_setup/src/handlers.rs +++ b/crates/defguard_setup/src/handlers.rs @@ -1,14 +1,24 @@ use std::sync::{Arc, Mutex}; use axum::{Extension, Json}; +use axum_client_ip::InsecureClientIp; +use axum_extra::{ + TypedHeader, + extract::{ + CookieJar, + cookie::{Cookie, SameSite}, + }, + headers::UserAgent, +}; use defguard_certs::{der_to_pem, parse_certificate_info, parse_pem_certificate}; use defguard_common::db::models::{ - Settings, User, group::Group, settings::update_current_settings, + Session, SessionState, Settings, User, group::Group, settings::update_current_settings, }; use defguard_core::{ auth::AdminOrSetupRole, error::WebError, - handlers::{ApiResponse, ApiResult}, + handlers::{ApiResponse, ApiResult, SESSION_COOKIE_NAME}, + headers::get_device_info, }; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; @@ -27,14 +37,17 @@ pub struct CreateAdmin { } pub async fn create_admin( + cookies: CookieJar, + user_agent: TypedHeader, + InsecureClientIp(insecure_ip): InsecureClientIp, Extension(pool): Extension, Json(admin): Json, -) -> ApiResult { +) -> Result<(CookieJar, ApiResponse), WebError> { info!( "Creating initial admin user {} ({})", admin.username, admin.email ); - User::new( + let user = User::new( admin.username, Some(admin.password.as_str()), admin.last_name, @@ -45,9 +58,26 @@ pub async fn create_admin( .save(&pool) .await?; + let device_info = get_device_info(user_agent.as_str()); + + Session::delete_expired(&pool).await?; + let session = Session::new( + user.id, + SessionState::PasswordVerified, + insecure_ip.to_string(), + Some(device_info), + ); + session.save(&pool).await?; + + let auth_cookie = Cookie::build((SESSION_COOKIE_NAME, session.id.clone())) + .path("/") + .http_only(true) + .same_site(SameSite::Lax); + let cookies = cookies.add(auth_cookie); + info!("Initial admin user created"); - Ok(ApiResponse::with_status(StatusCode::CREATED)) + Ok((cookies, ApiResponse::with_status(StatusCode::CREATED))) } #[derive(Deserialize, Serialize, Debug)] @@ -56,6 +86,7 @@ pub struct GeneralConfig { default_admin_group_name: String, default_authentication: u32, default_mfa_code_lifetime: u32, + public_proxy_url: String, admin_username: String, } @@ -86,6 +117,7 @@ pub async fn set_general_config( .default_mfa_code_lifetime .try_into() .map_err(|err| WebError::BadRequest(format!("Invalid MFA code timeout seconds: {err}")))?; + settings.public_proxy_url = general_config.public_proxy_url; update_current_settings(&pool, settings).await?; debug!("Settings persisted"); diff --git a/crates/defguard_setup/tests/initial_setup.rs b/crates/defguard_setup/tests/initial_setup.rs new file mode 100644 index 0000000000..2514a088af --- /dev/null +++ b/crates/defguard_setup/tests/initial_setup.rs @@ -0,0 +1,470 @@ +use std::{ + net::{IpAddr, Ipv4Addr, SocketAddr}, + sync::Arc, +}; + +use axum::serve; +use defguard_certs::{CertificateAuthority, PemLabel, der_to_pem}; +use defguard_common::{ + VERSION, + db::{ + models::{Session, Settings, User, group::Group, settings::initialize_current_settings}, + setup_pool, + }, +}; +use defguard_setup::setup::build_setup_webapp; +use reqwest::{ + Client, StatusCode, + cookie::Jar, + header::{HeaderMap, HeaderValue, USER_AGENT}, +}; +use semver::Version; +use serde_json::json; +use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; +use tokio::{ + net::TcpListener, + sync::{Notify, oneshot}, + task::JoinHandle, +}; + +const SESSION_COOKIE_NAME: &str = "defguard_session"; + +struct TestClient { + client: Client, + _jar: Arc, + port: u16, + _task: JoinHandle<()>, +} + +impl TestClient { + fn new(app: axum::Router, listener: TcpListener) -> Self { + let port = listener.local_addr().unwrap().port(); + let task = tokio::spawn(async move { + let server = serve( + listener, + app.into_make_service_with_connect_info::(), + ); + server.await.expect("server error"); + }); + + let jar = Arc::new(Jar::default()); + let mut headers = HeaderMap::new(); + headers.insert(USER_AGENT, HeaderValue::from_static("test/0.0")); + + let client = Client::builder() + .default_headers(headers) + .cookie_provider(jar.clone()) + .build() + .expect("Failed to build reqwest client"); + + Self { + client, + _jar: jar, + port, + _task: task, + } + } + + fn base_url(&self) -> String { + format!("http://localhost:{}", self.port) + } + + fn get(&self, path: &str) -> reqwest::RequestBuilder { + self.client.get(format!("{}{}", self.base_url(), path)) + } + + fn post(&self, path: &str) -> reqwest::RequestBuilder { + self.client.post(format!("{}{}", self.base_url(), path)) + } +} + +async fn make_setup_test_client(pool: sqlx::PgPool) -> (TestClient, oneshot::Receiver<()>) { + let (setup_shutdown_tx, setup_shutdown_rx) = oneshot::channel::<()>(); + let app = build_setup_webapp( + pool, + Version::parse(VERSION).expect("Invalid version"), + setup_shutdown_tx, + ); + let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); + let listener = TcpListener::bind(addr) + .await + .expect("Could not bind ephemeral socket"); + + (TestClient::new(app, listener), setup_shutdown_rx) +} + +#[sqlx::test] +async fn test_create_admin(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + initialize_current_settings(&pool) + .await + .expect("Failed to initialize settings"); + + let (client, _shutdown_rx) = make_setup_test_client(pool.clone()).await; + + let payload = json!({ + "first_name": "Admin", + "last_name": "Admin", + "username": "admin1", + "email": "admin1@example.com", + "password": "Passw0rd!" + }); + + let response = client + .post("/api/v1/initial_setup/admin") + .json(&payload) + .send() + .await + .expect("Failed to create admin user"); + assert_eq!(response.status(), StatusCode::CREATED); + + let session_cookie = response + .cookies() + .find(|cookie| cookie.name() == SESSION_COOKIE_NAME) + .expect("Session cookie not set"); + + let user = User::find_by_username(&pool, "admin1") + .await + .expect("Failed to fetch user") + .expect("Admin user not created"); + assert_eq!(user.email, "admin1@example.com"); + + let session = Session::find_by_id(&pool, session_cookie.value()) + .await + .expect("Failed to fetch session") + .expect("Session not created"); + assert_eq!(session.user_id, user.id); +} + +#[sqlx::test] +async fn test_set_general_config(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + initialize_current_settings(&pool) + .await + .expect("Failed to initialize settings"); + + let _admin = User::new( + "admin1", + Some("Passw0rd!"), + "Admin", + "Admin", + "admin1@example.com", + None, + ) + .save(&pool) + .await + .expect("Failed to create admin user"); + + let (client, _shutdown_rx) = make_setup_test_client(pool.clone()).await; + + let payload = json!({ + "defguard_url": "https://example.com", + "default_admin_group_name": "admins", + "default_authentication": 14, + "default_mfa_code_lifetime": 120, + "admin_username": "admin1", + "public_proxy_url": "https://proxy.example.com" + }); + + let response = client + .post("/api/v1/initial_setup/general_config") + .json(&payload) + .send() + .await + .expect("Failed to set general config"); + assert_eq!(response.status(), StatusCode::CREATED); + + let settings = Settings::get(&pool) + .await + .expect("Failed to fetch settings") + .expect("Settings not found"); + assert_eq!(settings.defguard_url, "https://example.com"); + assert_eq!(settings.default_admin_group_name, "admins"); + assert_eq!(settings.authentication_period_days, 14); + assert_eq!(settings.mfa_code_timeout_seconds, 120); + + let group = Group::find_by_name(&pool, "admins") + .await + .expect("Failed to fetch group") + .expect("Admin group not created"); + assert!(group.is_admin); + + let admin = User::find_by_username(&pool, "admin1") + .await + .expect("Failed to fetch admin") + .expect("Admin user missing"); + let groups = admin + .member_of_names(&pool) + .await + .expect("Failed to fetch group membership"); + assert!(groups.contains(&"admins".to_string())); +} + +#[sqlx::test] +async fn test_create_ca(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + initialize_current_settings(&pool) + .await + .expect("Failed to initialize settings"); + + let (client, _shutdown_rx) = make_setup_test_client(pool.clone()).await; + + let payload = json!({ + "common_name": "Test CA", + "email": "ca@example.com", + "validity_period_years": 1 + }); + + let response = client + .post("/api/v1/initial_setup/ca") + .json(&payload) + .send() + .await + .expect("Failed to create CA"); + assert_eq!(response.status(), StatusCode::CREATED); + + let settings = Settings::get(&pool) + .await + .expect("Failed to fetch settings") + .expect("Settings not found"); + assert!(settings.ca_cert_der.is_some()); + assert!(settings.ca_key_der.is_some()); + assert!(settings.ca_expiry.is_some()); +} + +#[sqlx::test] +async fn test_upload_ca(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + initialize_current_settings(&pool) + .await + .expect("Failed to initialize settings"); + + let (client, _shutdown_rx) = make_setup_test_client(pool.clone()).await; + + let ca = CertificateAuthority::new("CA", "ca@example.com", 365).expect("Failed to create CA"); + let cert_pem = + der_to_pem(ca.cert_der(), PemLabel::Certificate).expect("Failed to convert cert to PEM"); + + let response = client + .post("/api/v1/initial_setup/ca/upload") + .json(&json!({ "cert_file": cert_pem })) + .send() + .await + .expect("Failed to upload CA"); + assert_eq!(response.status(), StatusCode::CREATED); + + let settings = Settings::get(&pool) + .await + .expect("Failed to fetch settings") + .expect("Settings not found"); + assert!(settings.ca_cert_der.is_some()); + assert!(settings.ca_key_der.is_none()); + assert!(settings.ca_expiry.is_some()); +} + +#[sqlx::test] +async fn test_get_ca(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + initialize_current_settings(&pool) + .await + .expect("Failed to initialize settings"); + + let (client, _shutdown_rx) = make_setup_test_client(pool.clone()).await; + + let payload = json!({ + "common_name": "CA", + "email": "ca@example.com", + "validity_period_years": 1 + }); + let response = client + .post("/api/v1/initial_setup/ca") + .json(&payload) + .send() + .await + .expect("Failed to create CA"); + assert_eq!(response.status(), StatusCode::CREATED); + + let response = client + .get("/api/v1/initial_setup/ca") + .send() + .await + .expect("Failed to fetch CA"); + assert_eq!(response.status(), StatusCode::OK); + + let body: serde_json::Value = response.json().await.expect("Failed to parse CA response"); + assert_eq!(body["subject_common_name"], "CA"); + let pem = body["ca_cert_pem"].as_str().expect("Missing ca_cert_pem"); + assert!(pem.contains("BEGIN CERTIFICATE")); +} + +#[sqlx::test] +async fn test_finish_setup(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + initialize_current_settings(&pool) + .await + .expect("Failed to initialize settings"); + + let (client, shutdown_rx) = make_setup_test_client(pool.clone()).await; + + let response = client + .post("/api/v1/initial_setup/finish") + .send() + .await + .expect("Failed to finish setup"); + assert_eq!(response.status(), StatusCode::OK); + + let settings = Settings::get(&pool) + .await + .expect("Failed to fetch settings") + .expect("Settings not found"); + assert!(settings.initial_setup_completed); + + let shutdown_signal = + tokio::time::timeout(std::time::Duration::from_secs(1), shutdown_rx).await; + assert!(matches!(shutdown_signal, Ok(Ok(())))); +} + +#[sqlx::test] +async fn test_setup_flow(_: PgPoolOptions, options: PgConnectOptions) { + let pool = setup_pool(options).await; + initialize_current_settings(&pool) + .await + .expect("Failed to initialize settings"); + + let (setup_shutdown_tx, setup_shutdown_rx) = oneshot::channel::<()>(); + let shutdown_notify = Arc::new(Notify::new()); + let shutdown_notify_server = shutdown_notify.clone(); + + let app = build_setup_webapp( + pool.clone(), + Version::parse(VERSION).expect("Invalid version"), + setup_shutdown_tx, + ); + + let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0); + let listener = TcpListener::bind(addr) + .await + .expect("Could not bind ephemeral socket"); + let port = listener.local_addr().unwrap().port(); + + let server_task = tokio::spawn(async move { + let server = serve( + listener, + app.into_make_service_with_connect_info::(), + ) + .with_graceful_shutdown(async move { + let _ = setup_shutdown_rx.await; + shutdown_notify_server.notify_one(); + }); + server.await.expect("server error"); + }); + + let jar = Arc::new(Jar::default()); + let mut headers = HeaderMap::new(); + headers.insert(USER_AGENT, HeaderValue::from_static("test/0.0")); + let client = Client::builder() + .default_headers(headers) + .cookie_provider(jar) + .build() + .expect("Failed to build reqwest client"); + let base_url = format!("http://localhost:{port}"); + + let response = client + .post(format!("{base_url}/api/v1/initial_setup/admin")) + .json(&json!({ + "first_name": "Admin", + "last_name": "Admin", + "username": "admin1", + "email": "admin1@example.com", + "password": "Passw0rd!" + })) + .send() + .await + .expect("Failed to create admin user"); + assert_eq!(response.status(), StatusCode::CREATED); + let session_cookie_value = response + .cookies() + .find(|cookie| cookie.name() == SESSION_COOKIE_NAME) + .expect("Session cookie not set") + .value() + .to_string(); + + let response = client + .post(format!("{base_url}/api/v1/initial_setup/general_config")) + .json(&json!({ + "defguard_url": "https://example.com", + "default_admin_group_name": "admins", + "default_authentication": 14, + "default_mfa_code_lifetime": 120, + "public_proxy_url": "https://proxy.example.com", + "admin_username": "admin1" + })) + .send() + .await + .expect("Failed to set general config"); + assert_eq!(response.status(), StatusCode::CREATED); + + let response = client + .post(format!("{base_url}/api/v1/initial_setup/ca")) + .json(&json!({ + "common_name": "CA", + "email": "ca@example.com", + "validity_period_years": 1 + })) + .send() + .await + .expect("Failed to create CA"); + assert_eq!(response.status(), StatusCode::CREATED); + + let response = client + .post(format!("{base_url}/api/v1/initial_setup/finish")) + .send() + .await + .expect("Failed to finish setup"); + assert_eq!(response.status(), StatusCode::OK); + + let settings = Settings::get(&pool) + .await + .expect("Failed to fetch settings") + .expect("Settings not found"); + assert!(settings.initial_setup_completed); + assert_eq!(settings.defguard_url, "https://example.com"); + assert_eq!(settings.default_admin_group_name, "admins"); + assert_eq!(settings.authentication_period_days, 14); + assert_eq!(settings.mfa_code_timeout_seconds, 120); + assert!(settings.ca_cert_der.is_some()); + assert!(settings.ca_key_der.is_some()); + assert!(settings.ca_expiry.is_some()); + + let admin_group = Group::find_by_name(&pool, "admins") + .await + .expect("Failed to fetch admin group") + .expect("Admin group not created"); + assert!(admin_group.is_admin); + + let admin_user = User::find_by_username(&pool, "admin1") + .await + .expect("Failed to fetch admin user") + .expect("Admin user not found"); + let groups = admin_user + .member_of_names(&pool) + .await + .expect("Failed to fetch group membership"); + assert!(groups.contains(&"admins".to_string())); + + let session = Session::find_by_id(&pool, &session_cookie_value) + .await + .expect("Failed to fetch session") + .expect("Session not created"); + assert_eq!(session.user_id, admin_user.id); + + let shutdown_signal = tokio::time::timeout( + std::time::Duration::from_secs(1), + shutdown_notify.notified(), + ) + .await; + assert!(shutdown_signal.is_ok()); + + let server_result = tokio::time::timeout(std::time::Duration::from_secs(1), server_task).await; + assert!(matches!(server_result, Ok(Ok(())))); +} diff --git a/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql b/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql new file mode 100644 index 0000000000..0c72a349a5 --- /dev/null +++ b/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE proxy ADD COLUMN public_address TEXT NOT NULL; +ALTER TABLE settings DROP COLUMN public_proxy_url; diff --git a/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql b/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql new file mode 100644 index 0000000000..7463094e94 --- /dev/null +++ b/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE proxy DROP COLUMN public_address; +ALTER TABLE settings ADD COLUMN public_proxy_url TEXT NOT NULL DEFAULT 'http://localhost:8080'; diff --git a/web/messages/en/edge_wizard.json b/web/messages/en/edge_wizard.json index 411c240de2..4dd90b4037 100644 --- a/web/messages/en/edge_wizard.json +++ b/web/messages/en/edge_wizard.json @@ -17,31 +17,29 @@ "edge_setup_controls_configure": "Configure Edge component", "edge_setup_step_edge_component_label": "Configure edge component", "edge_setup_step_edge_component_description": "Set up your VPN proxy quickly and ensure secure, optimized traffic flow for your users.", - "edge_setup_step_edge_adaptation_label": "Edge Component Adaptation", - "edge_setup_step_edge_adaptation_description": "Review the system's checks and see if any issues need attention before deployment.", + "edge_setup_step_edge_adoption_label": "Edge Component Adoption", + "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_ip_or_domain": "IP or Domain", "edge_setup_component_label_grpc_port": "gRPC Port", - "edge_setup_component_label_public_domain": "Public Domain", "edge_setup_component_error_common_name_required": "Common 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", - "edge_setup_component_error_public_domain_required": "Public Domain is required", "edge_setup_component_controls_back": "Back", "edge_setup_component_controls_submit": "Adopt Edge component", - "edge_setup_adaptation_checking_configuration": "Checking provided Edge component configuration", - "edge_setup_adaptation_checking_availability": "Checking if Edge is available at: {ip_or_domain}:{grpc_port}", - "edge_setup_adaptation_checking_version": "Checking Edge proxy version", - "edge_setup_adaptation_checking_version_with_value": "Checking Edge proxy version: {proxyVersion}", - "edge_setup_adaptation_obtaining_csr": "Obtaining Certificate Signing Request", - "edge_setup_adaptation_signing_certificate": "Signing Certificate", - "edge_setup_adaptation_configuring_tls": "Configuring TLS communication", - "edge_setup_adaptation_error_default": "An error occurred during setup.", - "edge_setup_adaptation_error_log_title": "Error log", - "edge_setup_adaptation_controls_retry": "Retry", - "edge_setup_adaptation_controls_back": "Back", - "edge_setup_adaptation_controls_continue": "Continue" + "edge_setup_adoption_checking_configuration": "Checking provided Edge component configuration", + "edge_setup_adoption_checking_availability": "Checking if Edge is available at: {ip_or_domain}:{grpc_port}", + "edge_setup_adoption_checking_version": "Checking Edge proxy version", + "edge_setup_adoption_checking_version_with_value": "Checking Edge proxy version: {proxyVersion}", + "edge_setup_adoption_obtaining_csr": "Obtaining Certificate Signing Request", + "edge_setup_adoption_signing_certificate": "Signing Certificate", + "edge_setup_adoption_configuring_tls": "Configuring TLS communication", + "edge_setup_adoption_error_default": "An error occurred during setup.", + "edge_setup_adoption_error_log_title": "Error log", + "edge_setup_adoption_controls_retry": "Retry", + "edge_setup_adoption_controls_back": "Back", + "edge_setup_adoption_controls_continue": "Continue" } diff --git a/web/messages/en/gateway_wizard.json b/web/messages/en/gateway_wizard.json index 7816574338..82484b172f 100644 --- a/web/messages/en/gateway_wizard.json +++ b/web/messages/en/gateway_wizard.json @@ -17,8 +17,8 @@ "gateway_setup_controls_configure": "Configure gateway", "gateway_setup_step_gateway_component_label": "Configure Gateway component", "gateway_setup_step_gateway_component_description": "Set up your VPN proxy quickly and ensure secure, optimized traffic flow for your users.", - "gateway_setup_step_gateway_adaptation_label": "Gateway Adoption", - "gateway_setup_step_gateway_adaptation_description": "Review the system's checks and see if any issues need attention before deployment.", + "gateway_setup_step_gateway_adoption_label": "Gateway Adoption", + "gateway_setup_step_gateway_adoption_description": "Review the system's checks and see if any issues need attention before deployment.", "gateway_setup_step_confirmation_label": "Configuration confirmation", "gateway_setup_step_confirmation_description": "Your configuration was successful. You're all set.", "gateway_setup_component_label_common_name": "Common Name", @@ -30,16 +30,16 @@ "gateway_setup_component_error_grpc_port_max": "gRPC Port must be less than 65536", "gateway_setup_component_controls_back": "Back", "gateway_setup_component_controls_submit": "Adopt Gateway component", - "gateway_setup_adaptation_checking_configuration": "Checking provided Gateway component configuration", - "gateway_setup_adaptation_checking_availability": "Checking if Gateway is available at: {ip_or_domain}:{grpc_port}", - "gateway_setup_adaptation_checking_version": "Checking Gateway version", - "gateway_setup_adaptation_checking_version_with_value": "Checking Gateway version: {gatewayVersion}", - "gateway_setup_adaptation_obtaining_csr": "Obtaining Certificate Signing Request", - "gateway_setup_adaptation_signing_certificate": "Signing Certificate", - "gateway_setup_adaptation_configuring_tls": "Configuring TLS communication", - "gateway_setup_adaptation_error_default": "An error occurred during setup.", - "gateway_setup_adaptation_error_log_title": "Error log", - "gateway_setup_adaptation_controls_retry": "Retry", - "gateway_setup_adaptation_controls_back": "Back", - "gateway_setup_adaptation_controls_continue": "Continue" + "gateway_setup_adoption_checking_configuration": "Checking provided Gateway component configuration", + "gateway_setup_adoption_checking_availability": "Checking if Gateway is available at: {ip_or_domain}:{grpc_port}", + "gateway_setup_adoption_checking_version": "Checking Gateway version", + "gateway_setup_adoption_checking_version_with_value": "Checking Gateway version: {gatewayVersion}", + "gateway_setup_adoption_obtaining_csr": "Obtaining Certificate Signing Request", + "gateway_setup_adoption_signing_certificate": "Signing Certificate", + "gateway_setup_adoption_configuring_tls": "Configuring TLS communication", + "gateway_setup_adoption_error_default": "An error occurred during setup.", + "gateway_setup_adoption_error_log_title": "Error log", + "gateway_setup_adoption_controls_retry": "Retry", + "gateway_setup_adoption_controls_back": "Back", + "gateway_setup_adoption_controls_continue": "Continue" } diff --git a/web/messages/en/initial_wizard.json b/web/messages/en/initial_wizard.json index be7b5f3598..367e048f88 100644 --- a/web/messages/en/initial_wizard.json +++ b/web/messages/en/initial_wizard.json @@ -16,8 +16,8 @@ "initial_setup_step_certificate_authority_summary_description": "Securing component communication", "initial_setup_step_edge_component_label": "Edge Component", "initial_setup_step_edge_component_description": "Set up your VPN proxy quickly and ensure secure, optimized traffic flow for your users.", - "initial_setup_step_edge_adaptation_label": "Edge Component Adaptation", - "initial_setup_step_edge_adaptation_description": "Review the system’s checks and see if any issues need attention before deployment.", + "initial_setup_step_edge_adoption_label": "Edge Component Adoption", + "initial_setup_step_edge_adoption_description": "Review the system’s checks and see if any issues need attention before deployment.", "initial_setup_step_confirmation_label": "Confirmation", "initial_setup_step_confirmation_description": "Your configuration was successful. You’re all set.", @@ -55,7 +55,10 @@ "initial_setup_general_config_label_admin_group": "Default Admin Group Name", "initial_setup_general_config_label_auth_period": "Default Authentication Period (days)", "initial_setup_general_config_label_mfa_timeout": "Default MFA Code Timeout (seconds)", + "initial_setup_general_config_label_public_proxy_url": "Public Edge component URL", "initial_setup_general_config_error_save_failed": "Failed to create admin user. Please try again.", + "initial_setup_general_config_error_public_proxy_url_invalid": "Public Proxy URL must be a valid URL", + "initial_setup_general_config_error_public_proxy_url_required": "Public Proxy URL is required", "initial_setup_ca_validity_one_year": "1 year", "initial_setup_ca_validity_years": "{years} years", @@ -95,6 +98,8 @@ "initial_setup_confirmation_action_subtitle": "To organize users, manage access, track users activity and device monitoring.", "initial_setup_confirmation_action_time": "Around 3 minutes", "initial_setup_confirmation_footer": "Once you create your first location, the only step left will be to connect a gateway — and the system will be fully ready to use. This usually takes about n 10–15 minutes, depending on the complexity of your VPN configuration.", + "initial_setup_confirmation_cancel": "I'll do this later", + "initial_setup_confirmation_submit": "Create location now", "initial_setup_controls_back": "Back", "initial_setup_controls_continue": "Continue", diff --git a/web/src/pages/EdgeSetupPage/EdgeSetupPage.tsx b/web/src/pages/EdgeSetupPage/EdgeSetupPage.tsx index d8a8f7c48e..4b76693f54 100644 --- a/web/src/pages/EdgeSetupPage/EdgeSetupPage.tsx +++ b/web/src/pages/EdgeSetupPage/EdgeSetupPage.tsx @@ -13,7 +13,7 @@ import { ThemeSpacing } from '../../shared/defguard-ui/types'; import deployImage from './assets/deploy.svg'; import welcomeImage from './assets/welcome_image.svg'; import { SetupConfirmationStep } from './steps/SetupConfirmationStep'; -import { SetupEdgeAdaptationStep } from './steps/SetupEdgeAdaptationStep'; +import { SetupEdgeAdoptionStep } from './steps/SetupEdgeAdoptionStep'; import { SetupEdgeComponentStep } from './steps/SetupEdgeComponentStep'; import { EdgeSetupStep, type EdgeSetupStepValue } from './types'; import { useEdgeWizardStore } from './useEdgeWizardStore'; @@ -32,11 +32,11 @@ export const EdgeSetupPage = () => { label: m.edge_setup_step_edge_component_label(), description: m.edge_setup_step_edge_component_description(), }, - edgeAdaptation: { - id: EdgeSetupStep.EdgeAdaptation, + edgeAdoption: { + id: EdgeSetupStep.EdgeAdoption, order: 2, - label: m.edge_setup_step_edge_adaptation_label(), - description: m.edge_setup_step_edge_adaptation_description(), + label: m.edge_setup_step_edge_adoption_label(), + description: m.edge_setup_step_edge_adoption_description(), }, confirmation: { id: EdgeSetupStep.Confirmation, @@ -51,7 +51,7 @@ export const EdgeSetupPage = () => { const stepsComponents = useMemo( (): Record => ({ edgeComponent: , - edgeAdaptation: , + edgeAdoption: , confirmation: , }), [], diff --git a/web/src/pages/EdgeSetupPage/steps/SetupEdgeAdaptationStep.tsx b/web/src/pages/EdgeSetupPage/steps/SetupEdgeAdoptionStep.tsx similarity index 62% rename from web/src/pages/EdgeSetupPage/steps/SetupEdgeAdaptationStep.tsx rename to web/src/pages/EdgeSetupPage/steps/SetupEdgeAdoptionStep.tsx index 82c9b2186e..66d998f133 100644 --- a/web/src/pages/EdgeSetupPage/steps/SetupEdgeAdaptationStep.tsx +++ b/web/src/pages/EdgeSetupPage/steps/SetupEdgeAdoptionStep.tsx @@ -13,27 +13,27 @@ import { EdgeSetupStep } from '../types'; import { useEdgeWizardStore } from '../useEdgeWizardStore'; import type { SetupEvent, SetupStep, SetupStepId } from './types'; -export const SetupEdgeAdaptationStep = () => { +export const SetupEdgeAdoptionStep = () => { const setActiveStep = useEdgeWizardStore((s) => s.setActiveStep); const edgeComponentWizardStore = useEdgeWizardStore((s) => s); - const edgeAdaptationState = useEdgeWizardStore((s) => s.edgeAdaptationState); - const setEdgeAdaptationState = useEdgeWizardStore((s) => s.setEdgeAdaptationState); - const resetEdgeAdaptationState = useEdgeWizardStore((s) => s.resetEdgeAdaptationState); + const edgeAdoptionState = useEdgeWizardStore((s) => s.edgeAdoptionState); + const setEdgeAdoptionState = useEdgeWizardStore((s) => s.setEdgeAdoptionState); + const resetEdgeAdoptionState = useEdgeWizardStore((s) => s.resetEdgeAdoptionState); const handleEvent = useCallback( (event: SetupEvent) => { - setEdgeAdaptationState({ + setEdgeAdoptionState({ currentStep: event.step, isComplete: event.step === 'Done', isProcessing: event.step !== 'Done' && !event.error, proxyVersion: event.version ?? null, errorMessage: event.error - ? event.message || m.edge_setup_adaptation_error_default() + ? event.message || m.edge_setup_adoption_error_default() : null, proxyLogs: event.logs && event.logs.length > 0 ? [...event.logs] : [], }); }, - [setEdgeAdaptationState], + [setEdgeAdoptionState], ); const sse = useSSEController( @@ -49,7 +49,7 @@ export const SetupEdgeAdaptationStep = () => { ); const handleBack = () => { - useEdgeWizardStore.getState().resetEdgeAdaptationState(); + useEdgeWizardStore.getState().resetEdgeAdoptionState(); setActiveStep(EdgeSetupStep.EdgeComponent); }; @@ -61,75 +61,70 @@ export const SetupEdgeAdaptationStep = () => { () => [ { id: 'CheckingConfiguration', - title: m.edge_setup_adaptation_checking_configuration(), + title: m.edge_setup_adoption_checking_configuration(), }, { id: 'CheckingAvailability', - title: m.edge_setup_adaptation_checking_availability({ + title: m.edge_setup_adoption_checking_availability({ ip_or_domain: edgeComponentWizardStore.ip_or_domain, grpc_port: edgeComponentWizardStore.grpc_port.toString(), }), }, { id: 'CheckingVersion', - title: edgeAdaptationState.proxyVersion - ? m.edge_setup_adaptation_checking_version_with_value({ - proxyVersion: edgeAdaptationState.proxyVersion, + title: edgeAdoptionState.proxyVersion + ? m.edge_setup_adoption_checking_version_with_value({ + proxyVersion: edgeAdoptionState.proxyVersion, }) - : m.edge_setup_adaptation_checking_version(), + : m.edge_setup_adoption_checking_version(), }, { id: 'ObtainingCsr', - title: m.edge_setup_adaptation_obtaining_csr(), + title: m.edge_setup_adoption_obtaining_csr(), }, { id: 'SigningCertificate', - title: m.edge_setup_adaptation_signing_certificate(), + title: m.edge_setup_adoption_signing_certificate(), }, { id: 'ConfiguringTls', - title: m.edge_setup_adaptation_configuring_tls(), + title: m.edge_setup_adoption_configuring_tls(), }, ], - [edgeComponentWizardStore, edgeAdaptationState.proxyVersion], + [edgeComponentWizardStore, edgeAdoptionState.proxyVersion], ); const stepDone = useCallback( (stepId: SetupStepId): boolean => { const stepIndex = steps.findIndex((step) => step.id === stepId); - const currentStepIndex = edgeAdaptationState.currentStep - ? steps.findIndex((step) => step.id === edgeAdaptationState.currentStep) + const currentStepIndex = edgeAdoptionState.currentStep + ? steps.findIndex((step) => step.id === edgeAdoptionState.currentStep) : -1; - return stepIndex < currentStepIndex || edgeAdaptationState.isComplete; + return stepIndex < currentStepIndex || edgeAdoptionState.isComplete; }, - [edgeAdaptationState.isComplete, edgeAdaptationState.currentStep, steps], + [edgeAdoptionState.isComplete, edgeAdoptionState.currentStep, steps], ); const stepLoading = useCallback( (stepId: SetupStepId): boolean => { - return ( - edgeAdaptationState.isProcessing && edgeAdaptationState.currentStep === stepId - ); + return edgeAdoptionState.isProcessing && edgeAdoptionState.currentStep === stepId; }, - [edgeAdaptationState.isProcessing, edgeAdaptationState.currentStep], + [edgeAdoptionState.isProcessing, edgeAdoptionState.currentStep], ); const stepError = useCallback( (stepId: SetupStepId): string | null => { - if ( - edgeAdaptationState.errorMessage && - edgeAdaptationState.currentStep === stepId - ) { - return edgeAdaptationState.errorMessage; + if (edgeAdoptionState.errorMessage && edgeAdoptionState.currentStep === stepId) { + return edgeAdoptionState.errorMessage; } return null; }, - [edgeAdaptationState.errorMessage, edgeAdaptationState.currentStep], + [edgeAdoptionState.errorMessage, edgeAdoptionState.currentStep], ); // biome-ignore lint/correctness/useExhaustiveDependencies: only run on mount useEffect(() => { - resetEdgeAdaptationState(); + resetEdgeAdoptionState(); sse.start(); return () => { @@ -149,11 +144,11 @@ export const SetupEdgeAdaptationStep = () => { error={!!stepError(step.id)} errorMessage={stepError(step.id) || undefined} > - {edgeAdaptationState.proxyLogs.length > 0 ? ( + {edgeAdoptionState.proxyLogs.length > 0 ? ( <> @@ -162,12 +157,12 @@ export const SetupEdgeAdaptationStep = () => {
@@ -176,15 +171,15 @@ export const SetupEdgeAdaptationStep = () => { diff --git a/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx b/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx index a24762a5f9..40e3397936 100644 --- a/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx +++ b/web/src/pages/EdgeSetupPage/steps/SetupEdgeComponentStep.tsx @@ -20,7 +20,6 @@ type StoreValues = { common_name: string; ip_or_domain: string; grpc_port: number; - public_domain: string; }; export const SetupEdgeComponentStep = () => { @@ -32,7 +31,6 @@ export const SetupEdgeComponentStep = () => { common_name: s.common_name, ip_or_domain: s.ip_or_domain, grpc_port: s.grpc_port, - public_domain: s.public_domain, }), ), ); @@ -63,9 +61,6 @@ export const SetupEdgeComponentStep = () => { .number() .min(1, m.edge_setup_component_error_grpc_port_required()) .max(65535, m.edge_setup_component_error_grpc_port_max()), - public_domain: z - .string() - .min(1, m.edge_setup_component_error_public_domain_required()), }), [], ); @@ -81,7 +76,7 @@ export const SetupEdgeComponentStep = () => { useEdgeWizardStore.setState({ ...value, }); - setActiveStep(EdgeSetupStep.EdgeAdaptation); + setActiveStep(EdgeSetupStep.EdgeAdoption); }, }); @@ -125,15 +120,6 @@ export const SetupEdgeComponentStep = () => { )} - - {(field) => ( - - )} - void; setShowWelcome: (show: boolean) => void; updateValues: (values: Partial) => void; - resetEdgeAdaptationState: () => void; - setEdgeAdaptationState: (state: EdgeAdaptationState) => void; + resetEdgeAdoptionState: () => void; + setEdgeAdoptionState: (state: EdgeAdoptionState) => void; }; -const edgeAdaptationStateDefaults: EdgeAdaptationState = { +const edgeAdoptionStateDefaults: EdgeAdoptionState = { isProcessing: false, isComplete: false, currentStep: null, @@ -42,8 +37,7 @@ const defaults: StoreValues = { common_name: '', ip_or_domain: '', grpc_port: 50051, - public_domain: '', - edgeAdaptationState: edgeAdaptationStateDefaults, + edgeAdoptionState: edgeAdoptionStateDefaults, }; export const useEdgeWizardStore = create()( @@ -60,13 +54,13 @@ export const useEdgeWizardStore = create()( setActiveStep: (step) => set({ activeStep: step }), setShowWelcome: (show) => set({ showWelcome: show }), updateValues: (values) => set(values), - resetEdgeAdaptationState: () => + resetEdgeAdoptionState: () => set(() => ({ - edgeAdaptationState: { ...edgeAdaptationStateDefaults }, + edgeAdoptionState: { ...edgeAdoptionStateDefaults }, })), - setEdgeAdaptationState: (state: Partial) => + setEdgeAdoptionState: (state: Partial) => set((s) => ({ - edgeAdaptationState: { ...s.edgeAdaptationState, ...state }, + edgeAdoptionState: { ...s.edgeAdoptionState, ...state }, })), }), { @@ -79,8 +73,8 @@ export const useEdgeWizardStore = create()( 'setActiveStep', 'updateValues', 'setShowWelcome', - 'resetEdgeAdaptationState', - 'setEdgeAdaptationState', + 'resetEdgeAdoptionState', + 'setEdgeAdoptionState', ]), }, ), diff --git a/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx b/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx index 769f21c1f2..9573bdecf9 100644 --- a/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx +++ b/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx @@ -10,7 +10,7 @@ import { Divider } from '../../shared/defguard-ui/components/Divider/Divider'; import { ThemeSpacing } from '../../shared/defguard-ui/types'; import welcomeImage from './assets/welcome_image.svg'; import { SetupConfirmationStep } from './steps/SetupConfirmationStep'; -import { SetupGatewayAdaptationStep } from './steps/SetupGatewayAdaptationStep'; +import { SetupGatewayAdoptionStep } from './steps/SetupGatewayAdoptionStep'; import { SetupGatewayComponentStep } from './steps/SetupGatewayComponentStep'; import { GatewaySetupStep, type GatewaySetupStepValue } from './types'; import { useGatewayWizardStore } from './useGatewayWizardStore'; @@ -29,11 +29,11 @@ export const GatewaySetupPage = () => { label: m.gateway_setup_step_gateway_component_label(), description: m.gateway_setup_step_gateway_component_description(), }, - gatewayAdaptation: { - id: GatewaySetupStep.GatewayAdaptation, + gatewayAdoption: { + id: GatewaySetupStep.GatewayAdoption, order: 2, - label: m.gateway_setup_step_gateway_adaptation_label(), - description: m.gateway_setup_step_gateway_adaptation_description(), + label: m.gateway_setup_step_gateway_adoption_label(), + description: m.gateway_setup_step_gateway_adoption_description(), }, confirmation: { id: GatewaySetupStep.Confirmation, @@ -48,7 +48,7 @@ export const GatewaySetupPage = () => { const stepsComponents = useMemo( (): Record => ({ gatewayComponent: , - gatewayAdaptation: , + gatewayAdoption: , confirmation: , }), [], diff --git a/web/src/pages/GatewaySetupPage/steps/SetupGatewayAdaptationStep.tsx b/web/src/pages/GatewaySetupPage/steps/SetupGatewayAdaptationStep.tsx index c735b8e9eb..bf6da47da1 100644 --- a/web/src/pages/GatewaySetupPage/steps/SetupGatewayAdaptationStep.tsx +++ b/web/src/pages/GatewaySetupPage/steps/SetupGatewayAdaptationStep.tsx @@ -13,31 +13,29 @@ import { GatewaySetupStep } from '../types'; import { useGatewayWizardStore } from '../useGatewayWizardStore'; import type { SetupEvent, SetupStep, SetupStepId } from './types'; -export const SetupGatewayAdaptationStep = () => { +export const SetupGatewayAdoptionStep = () => { const setActiveStep = useGatewayWizardStore((s) => s.setActiveStep); const gatewayComponentWizardStore = useGatewayWizardStore((s) => s); - const gatewayAdaptationState = useGatewayWizardStore((s) => s.gatewayAdaptationState); - const setGatewayAdaptationState = useGatewayWizardStore( - (s) => s.setGatewayAdaptationState, - ); - const resetGatewayAdaptationState = useGatewayWizardStore( - (s) => s.resetGatewayAdaptationState, + const gatewayAdoptionState = useGatewayWizardStore((s) => s.gatewayAdoptionState); + const setGatewayAdoptionState = useGatewayWizardStore((s) => s.setGatewayAdoptionState); + const resetGatewayAdoptionState = useGatewayWizardStore( + (s) => s.resetGatewayAdoptionState, ); const handleEvent = useCallback( (event: SetupEvent) => { - setGatewayAdaptationState({ + setGatewayAdoptionState({ currentStep: event.step, isComplete: event.step === 'Done', isProcessing: event.step !== 'Done' && !event.error, gatewayVersion: event.version ?? null, errorMessage: event.error - ? event.message || m.edge_setup_adaptation_error_default() + ? event.message || m.edge_setup_adoption_error_default() : null, gatewayLogs: event.logs && event.logs.length > 0 ? [...event.logs] : [], }); }, - [setGatewayAdaptationState], + [setGatewayAdoptionState], ); const sse = useSSEController( @@ -54,7 +52,7 @@ export const SetupGatewayAdaptationStep = () => { ); const handleBack = () => { - useGatewayWizardStore.getState().resetGatewayAdaptationState(); + useGatewayWizardStore.getState().resetGatewayAdoptionState(); setActiveStep(GatewaySetupStep.GatewayComponent); }; @@ -66,76 +64,75 @@ export const SetupGatewayAdaptationStep = () => { () => [ { id: 'CheckingConfiguration', - title: m.gateway_setup_adaptation_checking_configuration(), + title: m.gateway_setup_adoption_checking_configuration(), }, { id: 'CheckingAvailability', - title: m.gateway_setup_adaptation_checking_availability({ + title: m.gateway_setup_adoption_checking_availability({ ip_or_domain: gatewayComponentWizardStore.ip_or_domain, grpc_port: String(gatewayComponentWizardStore.grpc_port), }), }, { id: 'CheckingVersion', - title: gatewayAdaptationState.gatewayVersion - ? m.gateway_setup_adaptation_checking_version_with_value({ - gatewayVersion: gatewayAdaptationState.gatewayVersion, + title: gatewayAdoptionState.gatewayVersion + ? m.gateway_setup_adoption_checking_version_with_value({ + gatewayVersion: gatewayAdoptionState.gatewayVersion, }) - : m.gateway_setup_adaptation_checking_version(), + : m.gateway_setup_adoption_checking_version(), }, { id: 'ObtainingCsr', - title: m.gateway_setup_adaptation_obtaining_csr(), + title: m.gateway_setup_adoption_obtaining_csr(), }, { id: 'SigningCertificate', - title: m.gateway_setup_adaptation_signing_certificate(), + title: m.gateway_setup_adoption_signing_certificate(), }, { id: 'ConfiguringTls', - title: m.gateway_setup_adaptation_configuring_tls(), + title: m.gateway_setup_adoption_configuring_tls(), }, ], - [gatewayComponentWizardStore, gatewayAdaptationState.gatewayVersion], + [gatewayComponentWizardStore, gatewayAdoptionState.gatewayVersion], ); const stepDone = useCallback( (stepId: SetupStepId): boolean => { const stepIndex = steps.findIndex((step) => step.id === stepId); - const currentStepIndex = gatewayAdaptationState.currentStep - ? steps.findIndex((step) => step.id === gatewayAdaptationState.currentStep) + const currentStepIndex = gatewayAdoptionState.currentStep + ? steps.findIndex((step) => step.id === gatewayAdoptionState.currentStep) : -1; - return stepIndex < currentStepIndex || gatewayAdaptationState.isComplete; + return stepIndex < currentStepIndex || gatewayAdoptionState.isComplete; }, - [gatewayAdaptationState.isComplete, gatewayAdaptationState.currentStep, steps], + [gatewayAdoptionState.isComplete, gatewayAdoptionState.currentStep, steps], ); const stepLoading = useCallback( (stepId: SetupStepId): boolean => { return ( - gatewayAdaptationState.isProcessing && - gatewayAdaptationState.currentStep === stepId + gatewayAdoptionState.isProcessing && gatewayAdoptionState.currentStep === stepId ); }, - [gatewayAdaptationState.isProcessing, gatewayAdaptationState.currentStep], + [gatewayAdoptionState.isProcessing, gatewayAdoptionState.currentStep], ); const stepError = useCallback( (stepId: SetupStepId): string | null => { if ( - gatewayAdaptationState.errorMessage && - gatewayAdaptationState.currentStep === stepId + gatewayAdoptionState.errorMessage && + gatewayAdoptionState.currentStep === stepId ) { - return gatewayAdaptationState.errorMessage; + return gatewayAdoptionState.errorMessage; } return null; }, - [gatewayAdaptationState.errorMessage, gatewayAdaptationState.currentStep], + [gatewayAdoptionState.errorMessage, gatewayAdoptionState.currentStep], ); // biome-ignore lint/correctness/useExhaustiveDependencies: only run on mount useEffect(() => { - resetGatewayAdaptationState(); + resetGatewayAdoptionState(); sse.start(); return () => { @@ -155,11 +152,11 @@ export const SetupGatewayAdaptationStep = () => { error={!!stepError(step.id)} errorMessage={stepError(step.id) || undefined} > - {gatewayAdaptationState.gatewayLogs.length > 0 ? ( + {gatewayAdoptionState.gatewayLogs.length > 0 ? ( <> @@ -168,12 +165,12 @@ export const SetupGatewayAdaptationStep = () => {
@@ -182,17 +179,15 @@ export const SetupGatewayAdaptationStep = () => { diff --git a/web/src/pages/GatewaySetupPage/steps/SetupGatewayComponentStep.tsx b/web/src/pages/GatewaySetupPage/steps/SetupGatewayComponentStep.tsx index 3674d57f01..8e876f78bd 100644 --- a/web/src/pages/GatewaySetupPage/steps/SetupGatewayComponentStep.tsx +++ b/web/src/pages/GatewaySetupPage/steps/SetupGatewayComponentStep.tsx @@ -76,7 +76,7 @@ export const SetupGatewayComponentStep = () => { useGatewayWizardStore.setState({ ...value, }); - setActiveStep(GatewaySetupStep.GatewayAdaptation); + setActiveStep(GatewaySetupStep.GatewayAdoption); }, }); diff --git a/web/src/pages/GatewaySetupPage/types.ts b/web/src/pages/GatewaySetupPage/types.ts index f4ae6bcdf0..c44493a551 100644 --- a/web/src/pages/GatewaySetupPage/types.ts +++ b/web/src/pages/GatewaySetupPage/types.ts @@ -1,6 +1,6 @@ export const GatewaySetupStep = { GatewayComponent: 'gatewayComponent', - GatewayAdaptation: 'gatewayAdaptation', + GatewayAdoption: 'gatewayAdoption', Confirmation: 'confirmation', } as const; diff --git a/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx b/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx index bac51baf1f..34af84a60b 100644 --- a/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx +++ b/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx @@ -4,7 +4,7 @@ import { createJSONStorage, persist } from 'zustand/middleware'; import type { SetupStepId } from './steps/types'; import { GatewaySetupStep, type GatewaySetupStepValue } from './types'; -type GatewayAdaptationState = { +type GatewayAdoptionState = { isProcessing: boolean; isComplete: boolean; currentStep: SetupStepId | null; @@ -20,7 +20,7 @@ type StoreValues = { ip_or_domain: string; grpc_port: number; network_id: number | null; - gatewayAdaptationState: GatewayAdaptationState; + gatewayAdoptionState: GatewayAdoptionState; }; type StoreMethods = { @@ -29,11 +29,11 @@ type StoreMethods = { setActiveStep: (step: GatewaySetupStepValue) => void; setShowWelcome: (show: boolean) => void; updateValues: (values: Partial) => void; - resetGatewayAdaptationState: () => void; - setGatewayAdaptationState: (state: GatewayAdaptationState) => void; + resetGatewayAdoptionState: () => void; + setGatewayAdoptionState: (state: GatewayAdoptionState) => void; }; -const gatewayAdaptationStateDefaults: GatewayAdaptationState = { +const gatewayAdoptionStateDefaults: GatewayAdoptionState = { isProcessing: false, isComplete: false, currentStep: null, @@ -49,7 +49,7 @@ const defaults: StoreValues = { ip_or_domain: '', grpc_port: 50066, network_id: null, - gatewayAdaptationState: gatewayAdaptationStateDefaults, + gatewayAdoptionState: gatewayAdoptionStateDefaults, }; export const useGatewayWizardStore = create()( @@ -66,13 +66,13 @@ export const useGatewayWizardStore = create()( setActiveStep: (step) => set({ activeStep: step }), setShowWelcome: (show) => set({ showWelcome: show }), updateValues: (values) => set(values), - resetGatewayAdaptationState: () => + resetGatewayAdoptionState: () => set(() => ({ - gatewayAdaptationState: { ...gatewayAdaptationStateDefaults }, + gatewayAdoptionState: { ...gatewayAdoptionStateDefaults }, })), - setGatewayAdaptationState: (state: Partial) => + setGatewayAdoptionState: (state: Partial) => set((s) => ({ - gatewayAdaptationState: { ...s.gatewayAdaptationState, ...state }, + gatewayAdoptionState: { ...s.gatewayAdoptionState, ...state }, })), }), { @@ -85,8 +85,8 @@ export const useGatewayWizardStore = create()( 'setActiveStep', 'updateValues', 'setShowWelcome', - 'resetEdgeAdaptationState', - 'setEdgeAdaptationState', + 'resetEdgeAdoptionState', + 'setEdgeAdoptionState', ]), }, ), diff --git a/web/src/pages/SetupPage/SetupPage.tsx b/web/src/pages/SetupPage/SetupPage.tsx index ec4d3a4ab6..44fd0dea48 100644 --- a/web/src/pages/SetupPage/SetupPage.tsx +++ b/web/src/pages/SetupPage/SetupPage.tsx @@ -13,7 +13,7 @@ import { SetupAdminUserStep } from './steps/SetupAdminUserStep'; import { SetupCertificateAuthorityStep } from './steps/SetupCertificateAuthorityStep'; import { SetupCertificateAuthoritySummaryStep } from './steps/SetupCertificateAuthoritySummaryStep'; import { SetupConfirmationStep } from './steps/SetupConfirmationStep'; -import { SetupEdgeAdaptationStep } from './steps/SetupEdgeAdaptationStep'; +import { SetupEdgeAdoptionStep } from './steps/SetupEdgeAdoptionStep'; import { SetupEdgeComponentStep } from './steps/SetupEdgeComponentStep'; import { SetupGeneralConfigStep } from './steps/SetupGeneralConfigStep'; import { SetupPageStep, type SetupPageStepValue } from './types'; @@ -57,11 +57,11 @@ export const SetupPage = () => { label: m.initial_setup_step_edge_component_label(), description: m.initial_setup_step_edge_component_description(), }, - edgeAdaptation: { - id: SetupPageStep.EdgeAdaptation, + edgeAdoption: { + id: SetupPageStep.EdgeAdoption, order: 6, - label: m.initial_setup_step_edge_adaptation_label(), - description: m.initial_setup_step_edge_adaptation_description(), + label: m.initial_setup_step_edge_adoption_label(), + description: m.initial_setup_step_edge_adoption_description(), }, confirmation: { id: SetupPageStep.Confirmation, @@ -80,7 +80,7 @@ export const SetupPage = () => { certificateAuthority: , certificateAuthoritySummary: , edgeComponent: , - edgeAdaptation: , + edgeAdoption: , confirmation: , }), [], @@ -112,7 +112,6 @@ export const SetupPage = () => { return ( {}} subtitle={m.initial_setup_wizard_subtitle()} title={m.initial_setup_wizard_title()} steps={stepsConfig} diff --git a/web/src/pages/SetupPage/steps/SetupConfirmationStep.tsx b/web/src/pages/SetupPage/steps/SetupConfirmationStep.tsx index 33fe41e3cb..bb8fa86c29 100644 --- a/web/src/pages/SetupPage/steps/SetupConfirmationStep.tsx +++ b/web/src/pages/SetupPage/steps/SetupConfirmationStep.tsx @@ -1,14 +1,11 @@ -import { useMutation } from '@tanstack/react-query'; import { useNavigate } from '@tanstack/react-router'; import { m } from '../../../paraglide/messages'; -import api from '../../../shared/api/api'; import { ActionCard } from '../../../shared/components/ActionCard/ActionCard'; import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; import { Divider } from '../../../shared/defguard-ui/components/Divider/Divider'; import { Icon } from '../../../shared/defguard-ui/components/Icon'; import { ModalControls } from '../../../shared/defguard-ui/components/ModalControls/ModalControls'; import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; -import { Snackbar } from '../../../shared/defguard-ui/providers/snackbar/snackbar'; import { ThemeSpacing } from '../../../shared/defguard-ui/types'; import location from '../assets/location.png'; import { useSetupWizardStore } from '../useSetupWizardStore'; @@ -16,21 +13,16 @@ import { useSetupWizardStore } from '../useSetupWizardStore'; export const SetupConfirmationStep = () => { const navigate = useNavigate(); - const { mutateAsync: finishSetup } = useMutation({ - mutationKey: ['finish-setup'], - mutationFn: api.initial_setup.finishSetup, - onError: (error) => { - console.error('Failed to finish setup:', error); - Snackbar.error(m.initial_setup_confirmation_error_finish_failed()); - }, - meta: { - invalidate: ['settings-essentials'], - }, - }); - const handleFinish = async () => { - await finishSetup(); - navigate({ to: '/auth/login', replace: true }).then(() => { + navigate({ to: '/add-location', replace: true }).then(() => { + setTimeout(() => { + useSetupWizardStore.getState().reset(); + }, 100); + }); + }; + + const handleExit = async () => { + navigate({ to: '/vpn-overview', replace: true }).then(() => { setTimeout(() => { useSetupWizardStore.getState().reset(); }, 100); @@ -60,15 +52,18 @@ export const SetupConfirmationStep = () => {

{m.initial_setup_confirmation_footer()}

+ - ); }; diff --git a/web/src/pages/SetupPage/steps/SetupEdgeAdaptationStep.tsx b/web/src/pages/SetupPage/steps/SetupEdgeAdoptionStep.tsx similarity index 57% rename from web/src/pages/SetupPage/steps/SetupEdgeAdaptationStep.tsx rename to web/src/pages/SetupPage/steps/SetupEdgeAdoptionStep.tsx index 9d2af80fbf..924bcd8621 100644 --- a/web/src/pages/SetupPage/steps/SetupEdgeAdaptationStep.tsx +++ b/web/src/pages/SetupPage/steps/SetupEdgeAdoptionStep.tsx @@ -1,5 +1,7 @@ +import { useMutation } from '@tanstack/react-query'; import { useCallback, useEffect, useMemo } from 'react'; import { m } from '../../../paraglide/messages'; +import api from '../../../shared/api/api'; import { Controls } from '../../../shared/components/Controls/Controls'; import { LoadingStep } from '../../../shared/components/LoadingStep/LoadingStep'; import { WizardCard } from '../../../shared/components/wizard/WizardCard/WizardCard'; @@ -7,35 +9,48 @@ import { Button } from '../../../shared/defguard-ui/components/Button/Button'; import { CodeCard } from '../../../shared/defguard-ui/components/CodeCard/CodeCard'; import { ModalControls } from '../../../shared/defguard-ui/components/ModalControls/ModalControls'; import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { Snackbar } from '../../../shared/defguard-ui/providers/snackbar/snackbar'; import { ThemeSpacing } from '../../../shared/defguard-ui/types'; import { useSSEController } from '../../../shared/hooks/useSSEController'; import type { SetupEvent, SetupStep, SetupStepId } from '../../EdgeSetupPage/steps/types'; import { SetupPageStep } from '../types'; import { useSetupWizardStore } from '../useSetupWizardStore'; -export const SetupEdgeAdaptationStep = () => { +export const SetupEdgeAdoptionStep = () => { const setActiveStep = useSetupWizardStore((s) => s.setActiveStep); const setupWizardStore = useSetupWizardStore((s) => s); - const edgeAdaptationState = useSetupWizardStore((s) => s.edgeAdaptationState); - const setEdgeAdaptationState = useSetupWizardStore((s) => s.setEdgeAdaptationState); - const resetEdgeAdaptationState = useSetupWizardStore((s) => s.resetEdgeAdaptationState); + const edgeAdoptionState = useSetupWizardStore((s) => s.edgeAdoptionState); + const setEdgeAdoptionState = useSetupWizardStore((s) => s.setEdgeAdoptionState); + const resetEdgeAdoptionState = useSetupWizardStore((s) => s.resetEdgeAdoptionState); const handleEvent = useCallback( (event: SetupEvent) => { - setEdgeAdaptationState({ + setEdgeAdoptionState({ currentStep: event.step, isComplete: event.step === 'Done', isProcessing: event.step !== 'Done' && !event.error, proxyVersion: event.version ?? null, errorMessage: event.error - ? event.message || m.edge_setup_adaptation_error_default() + ? event.message || m.edge_setup_adoption_error_default() : null, proxyLogs: event.logs && event.logs.length > 0 ? [...event.logs] : [], }); }, - [setEdgeAdaptationState], + [setEdgeAdoptionState], ); + const { mutateAsync: finishSetup } = useMutation({ + mutationKey: ['finish-setup'], + mutationFn: api.initial_setup.finishSetup, + onError: (error) => { + console.error('Failed to finish setup:', error); + Snackbar.error(m.initial_setup_confirmation_error_finish_failed()); + }, + meta: { + invalidate: ['settings-essentials'], + }, + }); + const sse = useSSEController( '/api/v1/proxy/setup/stream', { @@ -49,11 +64,12 @@ export const SetupEdgeAdaptationStep = () => { ); const handleBack = () => { - useSetupWizardStore.getState().resetEdgeAdaptationState(); + useSetupWizardStore.getState().resetEdgeAdoptionState(); setActiveStep(SetupPageStep.EdgeComponent); }; - const handleNext = () => { + const handleNext = async () => { + await finishSetup(); setActiveStep(SetupPageStep.Confirmation); }; @@ -61,75 +77,70 @@ export const SetupEdgeAdaptationStep = () => { () => [ { id: 'CheckingConfiguration', - title: m.edge_setup_adaptation_checking_configuration(), + title: m.edge_setup_adoption_checking_configuration(), }, { id: 'CheckingAvailability', - title: m.edge_setup_adaptation_checking_availability({ + title: m.edge_setup_adoption_checking_availability({ ip_or_domain: setupWizardStore.ip_or_domain, grpc_port: setupWizardStore.grpc_port.toString(), }), }, { id: 'CheckingVersion', - title: edgeAdaptationState.proxyVersion - ? m.edge_setup_adaptation_checking_version_with_value({ - proxyVersion: edgeAdaptationState.proxyVersion, + title: edgeAdoptionState.proxyVersion + ? m.edge_setup_adoption_checking_version_with_value({ + proxyVersion: edgeAdoptionState.proxyVersion, }) - : m.edge_setup_adaptation_checking_version(), + : m.edge_setup_adoption_checking_version(), }, { id: 'ObtainingCsr', - title: m.edge_setup_adaptation_obtaining_csr(), + title: m.edge_setup_adoption_obtaining_csr(), }, { id: 'SigningCertificate', - title: m.edge_setup_adaptation_signing_certificate(), + title: m.edge_setup_adoption_signing_certificate(), }, { id: 'ConfiguringTls', - title: m.edge_setup_adaptation_configuring_tls(), + title: m.edge_setup_adoption_configuring_tls(), }, ], - [setupWizardStore, edgeAdaptationState.proxyVersion], + [setupWizardStore, edgeAdoptionState.proxyVersion], ); const stepDone = useCallback( (stepId: SetupStepId): boolean => { const stepIndex = steps.findIndex((step) => step.id === stepId); - const currentStepIndex = edgeAdaptationState.currentStep - ? steps.findIndex((step) => step.id === edgeAdaptationState.currentStep) + const currentStepIndex = edgeAdoptionState.currentStep + ? steps.findIndex((step) => step.id === edgeAdoptionState.currentStep) : -1; - return stepIndex < currentStepIndex || edgeAdaptationState.isComplete; + return stepIndex < currentStepIndex || edgeAdoptionState.isComplete; }, - [edgeAdaptationState.isComplete, edgeAdaptationState.currentStep, steps], + [edgeAdoptionState.isComplete, edgeAdoptionState.currentStep, steps], ); const stepLoading = useCallback( (stepId: SetupStepId): boolean => { - return ( - edgeAdaptationState.isProcessing && edgeAdaptationState.currentStep === stepId - ); + return edgeAdoptionState.isProcessing && edgeAdoptionState.currentStep === stepId; }, - [edgeAdaptationState.isProcessing, edgeAdaptationState.currentStep], + [edgeAdoptionState.isProcessing, edgeAdoptionState.currentStep], ); const stepError = useCallback( (stepId: SetupStepId): string | null => { - if ( - edgeAdaptationState.errorMessage && - edgeAdaptationState.currentStep === stepId - ) { - return edgeAdaptationState.errorMessage; + if (edgeAdoptionState.errorMessage && edgeAdoptionState.currentStep === stepId) { + return edgeAdoptionState.errorMessage; } return null; }, - [edgeAdaptationState.errorMessage, edgeAdaptationState.currentStep], + [edgeAdoptionState.errorMessage, edgeAdoptionState.currentStep], ); // biome-ignore lint/correctness/useExhaustiveDependencies: only run on mount useEffect(() => { - resetEdgeAdaptationState(); + resetEdgeAdoptionState(); sse.start(); return () => { @@ -149,11 +160,11 @@ export const SetupEdgeAdaptationStep = () => { error={!!stepError(step.id)} errorMessage={stepError(step.id) || undefined} > - {edgeAdaptationState.proxyLogs.length > 0 ? ( + {edgeAdoptionState.proxyLogs.length > 0 ? ( <> @@ -162,12 +173,12 @@ export const SetupEdgeAdaptationStep = () => {
@@ -176,15 +187,15 @@ export const SetupEdgeAdaptationStep = () => { diff --git a/web/src/pages/SetupPage/steps/SetupEdgeComponentStep.tsx b/web/src/pages/SetupPage/steps/SetupEdgeComponentStep.tsx index 3f0a4c826d..1cd66e261b 100644 --- a/web/src/pages/SetupPage/steps/SetupEdgeComponentStep.tsx +++ b/web/src/pages/SetupPage/steps/SetupEdgeComponentStep.tsx @@ -18,7 +18,6 @@ type StoreValues = { common_name: string; ip_or_domain: string; grpc_port: number; - public_domain: string; }; export const SetupEdgeComponentStep = () => { @@ -30,7 +29,6 @@ export const SetupEdgeComponentStep = () => { common_name: s.common_name, ip_or_domain: s.ip_or_domain, grpc_port: s.grpc_port, - public_domain: s.public_domain, }), ), ); @@ -53,9 +51,6 @@ export const SetupEdgeComponentStep = () => { .number() .min(1, m.edge_setup_component_error_grpc_port_required()) .max(65535, m.edge_setup_component_error_grpc_port_max()), - public_domain: z - .string() - .min(1, m.edge_setup_component_error_public_domain_required()), }), [], ); @@ -71,7 +66,7 @@ export const SetupEdgeComponentStep = () => { useSetupWizardStore.setState({ ...value, }); - setActiveStep(SetupPageStep.EdgeAdaptation); + setActiveStep(SetupPageStep.EdgeAdoption); }, }); @@ -114,16 +109,6 @@ export const SetupEdgeComponentStep = () => { /> )} - - - {(field) => ( - - )} - { @@ -32,6 +33,7 @@ export const SetupGeneralConfigStep = () => { 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, }), ), ); @@ -51,6 +53,9 @@ export const SetupGeneralConfigStep = () => { default_mfa_code_lifetime: z .number() .min(60, m.initial_setup_general_config_error_mfa_timeout_min()), + public_proxy_url: z + .url(m.initial_setup_general_config_error_public_proxy_url_invalid()) + .min(1, m.initial_setup_general_config_error_public_proxy_url_required()), }), [], ); @@ -82,12 +87,14 @@ export const SetupGeneralConfigStep = () => { 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, }); mutate({ defguard_url: value.defguard_url, default_admin_group_name: value.default_admin_group_name, default_authentication: value.default_authentication, default_mfa_code_lifetime: value.default_mfa_code_lifetime, + public_proxy_url: value.public_proxy_url, admin_username: useSetupWizardStore.getState().admin_username, }); }, @@ -147,6 +154,15 @@ export const SetupGeneralConfigStep = () => { )} + + {(field) => ( + + )} + .buttons:has(button:nth-child(2)) { + justify-content: flex-end; + } + } } } diff --git a/web/src/pages/SetupPage/types.ts b/web/src/pages/SetupPage/types.ts index 9ddb44c03d..93ef82286c 100644 --- a/web/src/pages/SetupPage/types.ts +++ b/web/src/pages/SetupPage/types.ts @@ -5,7 +5,7 @@ export const SetupPageStep = { CertificateAuthority: 'certificateAuthority', CASummary: 'certificateAuthoritySummary', EdgeComponent: 'edgeComponent', - EdgeAdaptation: 'edgeAdaptation', + EdgeAdoption: 'edgeAdoption', Confirmation: 'confirmation', } as const; diff --git a/web/src/pages/SetupPage/useSetupWizardStore.tsx b/web/src/pages/SetupPage/useSetupWizardStore.tsx index 3e233a07ad..565f266780 100644 --- a/web/src/pages/SetupPage/useSetupWizardStore.tsx +++ b/web/src/pages/SetupPage/useSetupWizardStore.tsx @@ -1,10 +1,10 @@ import { omit } from 'lodash-es'; import { create } from 'zustand'; import { createJSONStorage, persist } from 'zustand/middleware'; -import type { EdgeAdaptationState } from '../EdgeSetupPage/types'; +import type { EdgeAdoptionState } from '../EdgeSetupPage/types'; import { type CAOptionType, SetupPageStep, type SetupPageStepValue } from './types'; -const edgeAdaptationStateDefaults: EdgeAdaptationState = { +const edgeAdoptionStateDefaults: EdgeAdoptionState = { isProcessing: false, isComplete: false, currentStep: null, @@ -27,6 +27,7 @@ type StoreValues = { default_admin_group_name: string; default_authentication_period_days: number; default_mfa_code_timeout_seconds: number; + public_proxy_url: string; // CA settings ca_common_name: string; ca_email: string; @@ -37,8 +38,7 @@ type StoreValues = { common_name: string; ip_or_domain: string; grpc_port: number; - public_domain: string; - edgeAdaptationState: EdgeAdaptationState; + edgeAdoptionState: EdgeAdoptionState; }; type StoreMethods = { @@ -46,8 +46,8 @@ type StoreMethods = { start: (values?: Partial) => void; setActiveStep: (step: SetupPageStepValue) => void; updateValues: (values: Partial) => void; - resetEdgeAdaptationState: () => void; - setEdgeAdaptationState: (state: Partial) => void; + resetEdgeAdoptionState: () => void; + setEdgeAdoptionState: (state: Partial) => void; }; const defaults: StoreValues = { @@ -64,6 +64,7 @@ const defaults: StoreValues = { default_admin_group_name: 'admin', default_authentication_period_days: 30, default_mfa_code_timeout_seconds: 300, + public_proxy_url: '', // CA settings ca_common_name: '', ca_email: '', @@ -74,8 +75,7 @@ const defaults: StoreValues = { common_name: '', ip_or_domain: '', grpc_port: 50051, - public_domain: '', - edgeAdaptationState: edgeAdaptationStateDefaults, + edgeAdoptionState: edgeAdoptionStateDefaults, }; export const useSetupWizardStore = create()( @@ -96,13 +96,13 @@ export const useSetupWizardStore = create()( }, setActiveStep: (step) => set({ activeStep: step }), updateValues: (values) => set(values), - resetEdgeAdaptationState: () => + resetEdgeAdoptionState: () => set(() => ({ - edgeAdaptationState: { ...edgeAdaptationStateDefaults }, + edgeAdoptionState: { ...edgeAdoptionStateDefaults }, })), - setEdgeAdaptationState: (state: Partial) => + setEdgeAdoptionState: (state: Partial) => set((s) => ({ - edgeAdaptationState: { ...s.edgeAdaptationState, ...state }, + edgeAdoptionState: { ...s.edgeAdoptionState, ...state }, })), }), { @@ -114,8 +114,8 @@ export const useSetupWizardStore = create()( 'start', 'setActiveStep', 'updateValues', - 'resetEdgeAdaptationState', - 'setEdgeAdaptationState', + 'resetEdgeAdoptionState', + 'setEdgeAdoptionState', ]), }, ), diff --git a/web/src/shared/api/types.ts b/web/src/shared/api/types.ts index bc3c070111..2c1d17e380 100644 --- a/web/src/shared/api/types.ts +++ b/web/src/shared/api/types.ts @@ -39,6 +39,7 @@ export interface SetGeneralConfigRequest { default_admin_group_name: string; default_authentication: number; default_mfa_code_lifetime: number; + public_proxy_url: string; admin_username: string; } diff --git a/web/src/shared/components/wizard/WizardPage/WizardPage.tsx b/web/src/shared/components/wizard/WizardPage/WizardPage.tsx index 72ad14fcc9..6b60333ad6 100644 --- a/web/src/shared/components/wizard/WizardPage/WizardPage.tsx +++ b/web/src/shared/components/wizard/WizardPage/WizardPage.tsx @@ -15,7 +15,7 @@ import { WizardWelcomePage } from '../WizardWelcomePage/WizardWelcomePage'; type Props = HTMLProps & PropsWithChildren & WizardPageConfig & { - onClose: () => void; + onClose?: () => void; }; export const WizardPage = ({ @@ -27,7 +27,7 @@ export const WizardPage = ({ children, onClose, welcomePageConfig, - showWelcome, + showWelcome = false, ...containerProps }: Props) => { const activeStep = steps[activeStepId]; diff --git a/web/src/shared/components/wizard/WizardTop/WizardTop.tsx b/web/src/shared/components/wizard/WizardTop/WizardTop.tsx index dbdcf0a6a8..5f1137237e 100644 --- a/web/src/shared/components/wizard/WizardTop/WizardTop.tsx +++ b/web/src/shared/components/wizard/WizardTop/WizardTop.tsx @@ -3,7 +3,7 @@ import { NavLogo } from '../../Navigation/assets/NavLogo'; import './style.scss'; type Props = { - onClick: () => void; + onClick?: () => void; }; export const WizardTop = ({ onClick }: Props) => { @@ -11,7 +11,7 @@ export const WizardTop = ({ onClick }: Props) => {
- + {onClick && }
); From 3373ef1d720dc770ecc0724ea7b4ae7bb750b1f5 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:50:24 +0100 Subject: [PATCH 3/4] fixes --- ...b66d99f840930ae3975df316d5a9bf3f627ea.json} | 5 ++--- ...0e5500bcc3ee7a16dda35eb139ae6a664aff8.json} | 7 ++++--- ...bbf644b8ba2e7170dd50ed4c085bf2d79b86e.json} | 18 ++++++------------ ...0d8a8823983288f8425947be41963668fbe00.json} | 10 ++++++++-- ...dddf4c77e2b510a9038fbd842402ef8442c97.json} | 5 ++--- ...6d82a34d3bfa4364cbe9b8368e71445bc20877.json | 14 ++++---------- ...874e3a334d66920cad457bcdbf9abe3378243.json} | 18 ++++++------------ .../GatewaySetupPage/GatewaySetupPage.tsx | 2 +- 8 files changed, 33 insertions(+), 46 deletions(-) rename .sqlx/{query-ebb3c3e89e4d1469d6e54ae6ba4c284b051bd034a869e38581a0db0c613037ab.json => query-07693036f0a901f11c7ed1e9d04b66d99f840930ae3975df316d5a9bf3f627ea.json} (57%) rename .sqlx/{query-27f9e867cb4e0740f0fe8635ccf0c609e6de71153d7a6bad4de27434e11a0a7d.json => query-3dc129f41487f6f876ec077b7750e5500bcc3ee7a16dda35eb139ae6a664aff8.json} (95%) rename .sqlx/{query-2ce4e93d1b2c0fd0c8783e9b574ebdcb7ef45d2f40b8c785353b2cb51c9ee3e8.json => query-5bdcca89592744ef271fb17cf65bbf644b8ba2e7170dd50ed4c085bf2d79b86e.json} (75%) rename .sqlx/{query-0cf6283fa8e927f0b74ddae4230a6108df8b6d4d4ebab0288d6182a29056d7a5.json => query-69d91cc060e5d6990fc3bb512a80d8a8823983288f8425947be41963668fbe00.json} (97%) rename .sqlx/{query-5117dfaead4847c2432f4354ee7caf1a9fd548f64e067ef2d8ec9b1f1d3ea8fb.json => query-7db637afcdd1abda3ec1114e9eddddf4c77e2b510a9038fbd842402ef8442c97.json} (64%) rename .sqlx/{query-4d03f4506afc44d5467c9e7a1f04daff875a766935e93b53df8bbf2be28421a9.json => query-e5cc17561bc0151af0bdd00cb53874e3a334d66920cad457bcdbf9abe3378243.json} (75%) diff --git a/.sqlx/query-ebb3c3e89e4d1469d6e54ae6ba4c284b051bd034a869e38581a0db0c613037ab.json b/.sqlx/query-07693036f0a901f11c7ed1e9d04b66d99f840930ae3975df316d5a9bf3f627ea.json similarity index 57% rename from .sqlx/query-ebb3c3e89e4d1469d6e54ae6ba4c284b051bd034a869e38581a0db0c613037ab.json rename to .sqlx/query-07693036f0a901f11c7ed1e9d04b66d99f840930ae3975df316d5a9bf3f627ea.json index 6cbaef5106..921cfba70c 100644 --- a/.sqlx/query-ebb3c3e89e4d1469d6e54ae6ba4c284b051bd034a869e38581a0db0c613037ab.json +++ b/.sqlx/query-07693036f0a901f11c7ed1e9d04b66d99f840930ae3975df316d5a9bf3f627ea.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"proxy\" SET \"name\" = $2,\"address\" = $3,\"port\" = $4,\"public_address\" = $5,\"connected_at\" = $6,\"disconnected_at\" = $7,\"version\" = $8,\"has_certificate\" = $9,\"certificate_expiry\" = $10 WHERE id = $1", + "query": "UPDATE \"proxy\" SET \"name\" = $2,\"address\" = $3,\"port\" = $4,\"connected_at\" = $5,\"disconnected_at\" = $6,\"version\" = $7,\"has_certificate\" = $8,\"certificate_expiry\" = $9 WHERE id = $1", "describe": { "columns": [], "parameters": { @@ -9,7 +9,6 @@ "Text", "Text", "Int4", - "Text", "Timestamp", "Timestamp", "Text", @@ -19,5 +18,5 @@ }, "nullable": [] }, - "hash": "ebb3c3e89e4d1469d6e54ae6ba4c284b051bd034a869e38581a0db0c613037ab" + "hash": "07693036f0a901f11c7ed1e9d04b66d99f840930ae3975df316d5a9bf3f627ea" } diff --git a/.sqlx/query-27f9e867cb4e0740f0fe8635ccf0c609e6de71153d7a6bad4de27434e11a0a7d.json b/.sqlx/query-3dc129f41487f6f876ec077b7750e5500bcc3ee7a16dda35eb139ae6a664aff8.json similarity index 95% rename from .sqlx/query-27f9e867cb4e0740f0fe8635ccf0c609e6de71153d7a6bad4de27434e11a0a7d.json rename to .sqlx/query-3dc129f41487f6f876ec077b7750e5500bcc3ee7a16dda35eb139ae6a664aff8.json index 32f500a463..ca5375fdbf 100644 --- a/.sqlx/query-27f9e867cb4e0740f0fe8635ccf0c609e6de71153d7a6bad4de27434e11a0a7d.json +++ b/.sqlx/query-3dc129f41487f6f876ec077b7750e5500bcc3ee7a16dda35eb139ae6a664aff8.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE \"settings\" SET openid_enabled = $1, wireguard_enabled = $2, webhooks_enabled = $3, worker_enabled = $4, challenge_template = $5, instance_name = $6, main_logo_url = $7, nav_logo_url = $8, smtp_server = $9, smtp_port = $10, smtp_encryption = $11, smtp_user = $12, smtp_password = $13, smtp_sender = $14, enrollment_vpn_step_optional = $15, enrollment_welcome_message = $16, enrollment_welcome_email = $17, enrollment_welcome_email_subject = $18, enrollment_use_welcome_message_as_email = $19, uuid = $20, ldap_url = $21, ldap_bind_username = $22, ldap_bind_password = $23, ldap_group_search_base = $24, ldap_user_search_base = $25, ldap_user_obj_class = $26, ldap_group_obj_class = $27, ldap_username_attr = $28, ldap_groupname_attr = $29, ldap_group_member_attr = $30, ldap_member_attr = $31, ldap_use_starttls = $32, ldap_tls_verify_cert = $33, openid_create_account = $34, license = $35, gateway_disconnect_notifications_enabled = $36, gateway_disconnect_notifications_inactivity_threshold = $37, gateway_disconnect_notifications_reconnect_notification_enabled = $38, ldap_sync_status = $39, ldap_enabled = $40, ldap_sync_enabled = $41, ldap_is_authoritative = $42, ldap_sync_interval = $43, ldap_user_auxiliary_obj_classes = $44, ldap_uses_ad = $45, ldap_user_rdn_attr = $46, ldap_sync_groups = $47, openid_username_handling = $48, ca_key_der = $49, ca_cert_der = $50, ca_expiry = $51, initial_setup_completed = $52, defguard_url = $53, default_admin_group_name = $54, authentication_period_days = $55, mfa_code_timeout_seconds = $56 WHERE id = 1", + "query": "UPDATE \"settings\" SET openid_enabled = $1, wireguard_enabled = $2, webhooks_enabled = $3, worker_enabled = $4, challenge_template = $5, instance_name = $6, main_logo_url = $7, nav_logo_url = $8, smtp_server = $9, smtp_port = $10, smtp_encryption = $11, smtp_user = $12, smtp_password = $13, smtp_sender = $14, enrollment_vpn_step_optional = $15, enrollment_welcome_message = $16, enrollment_welcome_email = $17, enrollment_welcome_email_subject = $18, enrollment_use_welcome_message_as_email = $19, uuid = $20, ldap_url = $21, ldap_bind_username = $22, ldap_bind_password = $23, ldap_group_search_base = $24, ldap_user_search_base = $25, ldap_user_obj_class = $26, ldap_group_obj_class = $27, ldap_username_attr = $28, ldap_groupname_attr = $29, ldap_group_member_attr = $30, ldap_member_attr = $31, ldap_use_starttls = $32, ldap_tls_verify_cert = $33, openid_create_account = $34, license = $35, gateway_disconnect_notifications_enabled = $36, gateway_disconnect_notifications_inactivity_threshold = $37, gateway_disconnect_notifications_reconnect_notification_enabled = $38, ldap_sync_status = $39, ldap_enabled = $40, ldap_sync_enabled = $41, ldap_is_authoritative = $42, ldap_sync_interval = $43, ldap_user_auxiliary_obj_classes = $44, ldap_uses_ad = $45, ldap_user_rdn_attr = $46, ldap_sync_groups = $47, openid_username_handling = $48, ca_key_der = $49, ca_cert_der = $50, ca_expiry = $51, initial_setup_completed = $52, defguard_url = $53, default_admin_group_name = $54, authentication_period_days = $55, mfa_code_timeout_seconds = $56, public_proxy_url = $57 WHERE id = 1", "describe": { "columns": [], "parameters": { @@ -92,10 +92,11 @@ "Text", "Text", "Int4", - "Int4" + "Int4", + "Text" ] }, "nullable": [] }, - "hash": "27f9e867cb4e0740f0fe8635ccf0c609e6de71153d7a6bad4de27434e11a0a7d" + "hash": "3dc129f41487f6f876ec077b7750e5500bcc3ee7a16dda35eb139ae6a664aff8" } diff --git a/.sqlx/query-2ce4e93d1b2c0fd0c8783e9b574ebdcb7ef45d2f40b8c785353b2cb51c9ee3e8.json b/.sqlx/query-5bdcca89592744ef271fb17cf65bbf644b8ba2e7170dd50ed4c085bf2d79b86e.json similarity index 75% rename from .sqlx/query-2ce4e93d1b2c0fd0c8783e9b574ebdcb7ef45d2f40b8c785353b2cb51c9ee3e8.json rename to .sqlx/query-5bdcca89592744ef271fb17cf65bbf644b8ba2e7170dd50ed4c085bf2d79b86e.json index 9c785ac3e0..acd21a2689 100644 --- a/.sqlx/query-2ce4e93d1b2c0fd0c8783e9b574ebdcb7ef45d2f40b8c785353b2cb51c9ee3e8.json +++ b/.sqlx/query-5bdcca89592744ef271fb17cf65bbf644b8ba2e7170dd50ed4c085bf2d79b86e.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"name\",\"address\",\"port\",\"public_address\",\"connected_at\",\"disconnected_at\",\"version\",\"has_certificate\",\"certificate_expiry\" FROM \"proxy\" WHERE id = $1", + "query": "SELECT id, \"name\",\"address\",\"port\",\"connected_at\",\"disconnected_at\",\"version\",\"has_certificate\",\"certificate_expiry\" FROM \"proxy\" WHERE id = $1", "describe": { "columns": [ { @@ -25,31 +25,26 @@ }, { "ordinal": 4, - "name": "public_address", - "type_info": "Text" - }, - { - "ordinal": 5, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 6, + "ordinal": 5, "name": "disconnected_at", "type_info": "Timestamp" }, { - "ordinal": 7, + "ordinal": 6, "name": "version", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 7, "name": "has_certificate", "type_info": "Bool" }, { - "ordinal": 9, + "ordinal": 8, "name": "certificate_expiry", "type_info": "Timestamp" } @@ -64,7 +59,6 @@ false, false, false, - false, true, true, true, @@ -72,5 +66,5 @@ true ] }, - "hash": "2ce4e93d1b2c0fd0c8783e9b574ebdcb7ef45d2f40b8c785353b2cb51c9ee3e8" + "hash": "5bdcca89592744ef271fb17cf65bbf644b8ba2e7170dd50ed4c085bf2d79b86e" } diff --git a/.sqlx/query-0cf6283fa8e927f0b74ddae4230a6108df8b6d4d4ebab0288d6182a29056d7a5.json b/.sqlx/query-69d91cc060e5d6990fc3bb512a80d8a8823983288f8425947be41963668fbe00.json similarity index 97% rename from .sqlx/query-0cf6283fa8e927f0b74ddae4230a6108df8b6d4d4ebab0288d6182a29056d7a5.json rename to .sqlx/query-69d91cc060e5d6990fc3bb512a80d8a8823983288f8425947be41963668fbe00.json index 29abad2393..29d8f0f49c 100644 --- a/.sqlx/query-0cf6283fa8e927f0b74ddae4230a6108df8b6d4d4ebab0288d6182a29056d7a5.json +++ b/.sqlx/query-69d91cc060e5d6990fc3bb512a80d8a8823983288f8425947be41963668fbe00.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT openid_enabled, wireguard_enabled, webhooks_enabled, worker_enabled, challenge_template, instance_name, main_logo_url, nav_logo_url, smtp_server, smtp_port, smtp_encryption \"smtp_encryption: _\", smtp_user, smtp_password \"smtp_password?: SecretStringWrapper\", smtp_sender, enrollment_vpn_step_optional, enrollment_welcome_message, enrollment_welcome_email, enrollment_welcome_email_subject, enrollment_use_welcome_message_as_email, uuid, ldap_url, ldap_bind_username, ldap_bind_password \"ldap_bind_password?: SecretStringWrapper\", ldap_group_search_base, ldap_user_search_base, ldap_user_obj_class, ldap_group_obj_class, ldap_username_attr, ldap_groupname_attr, ldap_group_member_attr, ldap_member_attr, openid_create_account, license, gateway_disconnect_notifications_enabled, ldap_use_starttls, ldap_tls_verify_cert, gateway_disconnect_notifications_inactivity_threshold, gateway_disconnect_notifications_reconnect_notification_enabled, ldap_sync_status \"ldap_sync_status: LdapSyncStatus\", ldap_enabled, ldap_sync_enabled, ldap_is_authoritative, ldap_sync_interval, ldap_user_auxiliary_obj_classes, ldap_uses_ad, ldap_user_rdn_attr, ldap_sync_groups, openid_username_handling \"openid_username_handling: OpenIdUsernameHandling\", ca_key_der, ca_cert_der, ca_expiry, initial_setup_completed, defguard_url, default_admin_group_name, authentication_period_days, mfa_code_timeout_seconds FROM \"settings\" WHERE id = 1", + "query": "SELECT openid_enabled, wireguard_enabled, webhooks_enabled, worker_enabled, challenge_template, instance_name, main_logo_url, nav_logo_url, smtp_server, smtp_port, smtp_encryption \"smtp_encryption: _\", smtp_user, smtp_password \"smtp_password?: SecretStringWrapper\", smtp_sender, enrollment_vpn_step_optional, enrollment_welcome_message, enrollment_welcome_email, enrollment_welcome_email_subject, enrollment_use_welcome_message_as_email, uuid, ldap_url, ldap_bind_username, ldap_bind_password \"ldap_bind_password?: SecretStringWrapper\", ldap_group_search_base, ldap_user_search_base, ldap_user_obj_class, ldap_group_obj_class, ldap_username_attr, ldap_groupname_attr, ldap_group_member_attr, ldap_member_attr, openid_create_account, license, gateway_disconnect_notifications_enabled, ldap_use_starttls, ldap_tls_verify_cert, gateway_disconnect_notifications_inactivity_threshold, gateway_disconnect_notifications_reconnect_notification_enabled, ldap_sync_status \"ldap_sync_status: LdapSyncStatus\", ldap_enabled, ldap_sync_enabled, ldap_is_authoritative, ldap_sync_interval, ldap_user_auxiliary_obj_classes, ldap_uses_ad, ldap_user_rdn_attr, ldap_sync_groups, openid_username_handling \"openid_username_handling: OpenIdUsernameHandling\", ca_key_der, ca_cert_der, ca_expiry, initial_setup_completed, defguard_url, default_admin_group_name, authentication_period_days, mfa_code_timeout_seconds, public_proxy_url FROM \"settings\" WHERE id = 1", "describe": { "columns": [ { @@ -314,6 +314,11 @@ "ordinal": 55, "name": "mfa_code_timeout_seconds", "type_info": "Int4" + }, + { + "ordinal": 56, + "name": "public_proxy_url", + "type_info": "Text" } ], "parameters": { @@ -375,8 +380,9 @@ false, false, false, + false, false ] }, - "hash": "0cf6283fa8e927f0b74ddae4230a6108df8b6d4d4ebab0288d6182a29056d7a5" + "hash": "69d91cc060e5d6990fc3bb512a80d8a8823983288f8425947be41963668fbe00" } diff --git a/.sqlx/query-5117dfaead4847c2432f4354ee7caf1a9fd548f64e067ef2d8ec9b1f1d3ea8fb.json b/.sqlx/query-7db637afcdd1abda3ec1114e9eddddf4c77e2b510a9038fbd842402ef8442c97.json similarity index 64% rename from .sqlx/query-5117dfaead4847c2432f4354ee7caf1a9fd548f64e067ef2d8ec9b1f1d3ea8fb.json rename to .sqlx/query-7db637afcdd1abda3ec1114e9eddddf4c77e2b510a9038fbd842402ef8442c97.json index 145f25c369..a84e42fe43 100644 --- a/.sqlx/query-5117dfaead4847c2432f4354ee7caf1a9fd548f64e067ef2d8ec9b1f1d3ea8fb.json +++ b/.sqlx/query-7db637afcdd1abda3ec1114e9eddddf4c77e2b510a9038fbd842402ef8442c97.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO \"proxy\" (\"name\",\"address\",\"port\",\"public_address\",\"connected_at\",\"disconnected_at\",\"version\",\"has_certificate\",\"certificate_expiry\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9) RETURNING id", + "query": "INSERT INTO \"proxy\" (\"name\",\"address\",\"port\",\"connected_at\",\"disconnected_at\",\"version\",\"has_certificate\",\"certificate_expiry\") VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING id", "describe": { "columns": [ { @@ -14,7 +14,6 @@ "Text", "Text", "Int4", - "Text", "Timestamp", "Timestamp", "Text", @@ -26,5 +25,5 @@ false ] }, - "hash": "5117dfaead4847c2432f4354ee7caf1a9fd548f64e067ef2d8ec9b1f1d3ea8fb" + "hash": "7db637afcdd1abda3ec1114e9eddddf4c77e2b510a9038fbd842402ef8442c97" } diff --git a/.sqlx/query-a41787c8c8307414165ab23ef96d82a34d3bfa4364cbe9b8368e71445bc20877.json b/.sqlx/query-a41787c8c8307414165ab23ef96d82a34d3bfa4364cbe9b8368e71445bc20877.json index 2408b9888f..a8b382e492 100644 --- a/.sqlx/query-a41787c8c8307414165ab23ef96d82a34d3bfa4364cbe9b8368e71445bc20877.json +++ b/.sqlx/query-a41787c8c8307414165ab23ef96d82a34d3bfa4364cbe9b8368e71445bc20877.json @@ -25,31 +25,26 @@ }, { "ordinal": 4, - "name": "public_address", - "type_info": "Text" - }, - { - "ordinal": 5, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 6, + "ordinal": 5, "name": "disconnected_at", "type_info": "Timestamp" }, { - "ordinal": 7, + "ordinal": 6, "name": "has_certificate", "type_info": "Bool" }, { - "ordinal": 8, + "ordinal": 7, "name": "certificate_expiry", "type_info": "Timestamp" }, { - "ordinal": 9, + "ordinal": 8, "name": "version", "type_info": "Text" } @@ -65,7 +60,6 @@ false, false, false, - false, true, true, false, diff --git a/.sqlx/query-4d03f4506afc44d5467c9e7a1f04daff875a766935e93b53df8bbf2be28421a9.json b/.sqlx/query-e5cc17561bc0151af0bdd00cb53874e3a334d66920cad457bcdbf9abe3378243.json similarity index 75% rename from .sqlx/query-4d03f4506afc44d5467c9e7a1f04daff875a766935e93b53df8bbf2be28421a9.json rename to .sqlx/query-e5cc17561bc0151af0bdd00cb53874e3a334d66920cad457bcdbf9abe3378243.json index a62f6fc534..94e1bb3aa7 100644 --- a/.sqlx/query-4d03f4506afc44d5467c9e7a1f04daff875a766935e93b53df8bbf2be28421a9.json +++ b/.sqlx/query-e5cc17561bc0151af0bdd00cb53874e3a334d66920cad457bcdbf9abe3378243.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT id, \"name\",\"address\",\"port\",\"public_address\",\"connected_at\",\"disconnected_at\",\"version\",\"has_certificate\",\"certificate_expiry\" FROM \"proxy\"", + "query": "SELECT id, \"name\",\"address\",\"port\",\"connected_at\",\"disconnected_at\",\"version\",\"has_certificate\",\"certificate_expiry\" FROM \"proxy\"", "describe": { "columns": [ { @@ -25,31 +25,26 @@ }, { "ordinal": 4, - "name": "public_address", - "type_info": "Text" - }, - { - "ordinal": 5, "name": "connected_at", "type_info": "Timestamp" }, { - "ordinal": 6, + "ordinal": 5, "name": "disconnected_at", "type_info": "Timestamp" }, { - "ordinal": 7, + "ordinal": 6, "name": "version", "type_info": "Text" }, { - "ordinal": 8, + "ordinal": 7, "name": "has_certificate", "type_info": "Bool" }, { - "ordinal": 9, + "ordinal": 8, "name": "certificate_expiry", "type_info": "Timestamp" } @@ -62,7 +57,6 @@ false, false, false, - false, true, true, true, @@ -70,5 +64,5 @@ true ] }, - "hash": "4d03f4506afc44d5467c9e7a1f04daff875a766935e93b53df8bbf2be28421a9" + "hash": "e5cc17561bc0151af0bdd00cb53874e3a334d66920cad457bcdbf9abe3378243" } diff --git a/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx b/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx index 9573bdecf9..9478c5f9b1 100644 --- a/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx +++ b/web/src/pages/GatewaySetupPage/GatewaySetupPage.tsx @@ -10,7 +10,7 @@ import { Divider } from '../../shared/defguard-ui/components/Divider/Divider'; import { ThemeSpacing } from '../../shared/defguard-ui/types'; import welcomeImage from './assets/welcome_image.svg'; import { SetupConfirmationStep } from './steps/SetupConfirmationStep'; -import { SetupGatewayAdoptionStep } from './steps/SetupGatewayAdoptionStep'; +import { SetupGatewayAdoptionStep } from './steps/SetupGatewayAdaptationStep'; import { SetupGatewayComponentStep } from './steps/SetupGatewayComponentStep'; import { GatewaySetupStep, type GatewaySetupStepValue } from './types'; import { useGatewayWizardStore } from './useGatewayWizardStore'; From e8227d63c341566eec1ee635a879f1ef03471fe0 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:35:35 +0100 Subject: [PATCH 4/4] suggestions --- web/src/pages/EdgeSetupPage/useEdgeWizardStore.tsx | 3 --- web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx | 3 --- web/src/pages/SetupPage/useSetupWizardStore.tsx | 3 --- web/src/shared/components/wizard/WizardTop/WizardTop.tsx | 3 ++- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/web/src/pages/EdgeSetupPage/useEdgeWizardStore.tsx b/web/src/pages/EdgeSetupPage/useEdgeWizardStore.tsx index 971f1efc0c..44b5f09b05 100644 --- a/web/src/pages/EdgeSetupPage/useEdgeWizardStore.tsx +++ b/web/src/pages/EdgeSetupPage/useEdgeWizardStore.tsx @@ -17,7 +17,6 @@ type StoreMethods = { start: (values?: Partial) => void; setActiveStep: (step: EdgeSetupStepValue) => void; setShowWelcome: (show: boolean) => void; - updateValues: (values: Partial) => void; resetEdgeAdoptionState: () => void; setEdgeAdoptionState: (state: EdgeAdoptionState) => void; }; @@ -53,7 +52,6 @@ export const useEdgeWizardStore = create()( }, setActiveStep: (step) => set({ activeStep: step }), setShowWelcome: (show) => set({ showWelcome: show }), - updateValues: (values) => set(values), resetEdgeAdoptionState: () => set(() => ({ edgeAdoptionState: { ...edgeAdoptionStateDefaults }, @@ -71,7 +69,6 @@ export const useEdgeWizardStore = create()( 'reset', 'start', 'setActiveStep', - 'updateValues', 'setShowWelcome', 'resetEdgeAdoptionState', 'setEdgeAdoptionState', diff --git a/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx b/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx index 34af84a60b..792b62d59c 100644 --- a/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx +++ b/web/src/pages/GatewaySetupPage/useGatewayWizardStore.tsx @@ -28,7 +28,6 @@ type StoreMethods = { start: (values?: Partial) => void; setActiveStep: (step: GatewaySetupStepValue) => void; setShowWelcome: (show: boolean) => void; - updateValues: (values: Partial) => void; resetGatewayAdoptionState: () => void; setGatewayAdoptionState: (state: GatewayAdoptionState) => void; }; @@ -65,7 +64,6 @@ export const useGatewayWizardStore = create()( }, setActiveStep: (step) => set({ activeStep: step }), setShowWelcome: (show) => set({ showWelcome: show }), - updateValues: (values) => set(values), resetGatewayAdoptionState: () => set(() => ({ gatewayAdoptionState: { ...gatewayAdoptionStateDefaults }, @@ -83,7 +81,6 @@ export const useGatewayWizardStore = create()( 'reset', 'start', 'setActiveStep', - 'updateValues', 'setShowWelcome', 'resetEdgeAdoptionState', 'setEdgeAdoptionState', diff --git a/web/src/pages/SetupPage/useSetupWizardStore.tsx b/web/src/pages/SetupPage/useSetupWizardStore.tsx index 565f266780..b8087b355b 100644 --- a/web/src/pages/SetupPage/useSetupWizardStore.tsx +++ b/web/src/pages/SetupPage/useSetupWizardStore.tsx @@ -45,7 +45,6 @@ type StoreMethods = { reset: () => void; start: (values?: Partial) => void; setActiveStep: (step: SetupPageStepValue) => void; - updateValues: (values: Partial) => void; resetEdgeAdoptionState: () => void; setEdgeAdoptionState: (state: Partial) => void; }; @@ -95,7 +94,6 @@ export const useSetupWizardStore = create()( }); }, setActiveStep: (step) => set({ activeStep: step }), - updateValues: (values) => set(values), resetEdgeAdoptionState: () => set(() => ({ edgeAdoptionState: { ...edgeAdoptionStateDefaults }, @@ -113,7 +111,6 @@ export const useSetupWizardStore = create()( 'reset', 'start', 'setActiveStep', - 'updateValues', 'resetEdgeAdoptionState', 'setEdgeAdoptionState', ]), diff --git a/web/src/shared/components/wizard/WizardTop/WizardTop.tsx b/web/src/shared/components/wizard/WizardTop/WizardTop.tsx index 5f1137237e..3696618f11 100644 --- a/web/src/shared/components/wizard/WizardTop/WizardTop.tsx +++ b/web/src/shared/components/wizard/WizardTop/WizardTop.tsx @@ -1,4 +1,5 @@ import { IconButton } from '../../../defguard-ui/components/IconButton/IconButton'; +import { isPresent } from '../../../defguard-ui/utils/isPresent'; import { NavLogo } from '../../Navigation/assets/NavLogo'; import './style.scss'; @@ -11,7 +12,7 @@ export const WizardTop = ({ onClick }: Props) => {
- {onClick && } + {isPresent(onClick) && }
);