diff --git a/Cargo.lock b/Cargo.lock index c9cf97a7d..7fcdf032c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3129,9 +3129,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.88" +version = "0.3.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e709f3e3d22866f9c25b3aff01af289b18422cc8b4262fb19103ee80fe513d" +checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6" dependencies = [ "once_cell", "wasm-bindgen", @@ -3323,7 +3323,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.11.0", "libc", - "redox_syscall 0.7.1", + "redox_syscall 0.7.2", ] [[package]] @@ -3338,9 +3338,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.23" +version = "1.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +checksum = "4735e9cbde5aac84a5ce588f6b23a90b9b0b528f6c5a8db8a4aff300463a0839" dependencies = [ "cc", "libc", @@ -4962,9 +4962,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" +checksum = "6d94dd2f7cd932d4dc02cc8b2b50dfd38bd079a4e5d79198b99743d7fcf9a4b4" dependencies = [ "bitflags 2.11.0", ] @@ -5014,9 +5014,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "replace_with" @@ -5218,9 +5218,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "aws-lc-rs", "log", @@ -5516,9 +5516,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "381b283ce7bc6b476d903296fb59d0d36633652b633b27f64db4fb46dcbfc3b9" dependencies = [ "base64 0.22.1", "chrono", @@ -5535,9 +5535,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "a6d4e30573c8cb306ed6ab1dca8423eec9a463ea0e155f45399455e0368b27e0" dependencies = [ "darling 0.21.3", "proc-macro2", @@ -6186,9 +6186,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.25.0" +version = "3.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" dependencies = [ "fastrand", "getrandom 0.4.1", @@ -7016,9 +7016,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1adf1535672f5b7824f817792b1afd731d7e843d2d04ec8f27e8cb51edd8ac" +checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2" dependencies = [ "cfg-if", "once_cell", @@ -7029,9 +7029,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.61" +version = "0.4.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88540d1c934c4ec8e6db0afa536876c5441289d7f9f9123d4f065ac1250a6b" +checksum = "8a89f4650b770e4521aa6573724e2aed4704372151bd0de9d16a3bbabb87441a" dependencies = [ "cfg-if", "futures-util", @@ -7043,9 +7043,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e638317c08b21663aed4d2b9a2091450548954695ff4efa75bff5fa546b3b1" +checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7053,9 +7053,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c64760850114d03d5f65457e96fc988f11f01d38fbaa51b254e4ab5809102af" +checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60" dependencies = [ "bumpalo", "proc-macro2", @@ -7066,9 +7066,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.111" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60eecd4fe26177cfa3339eb00b4a36445889ba3ad37080c2429879718e20ca41" +checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5" dependencies = [ "unicode-ident", ] @@ -7122,9 +7122,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.88" +version = "0.3.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6bb20ed2d9572df8584f6dc81d68a41a625cadc6f15999d649a70ce7e3597a" +checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/crates/defguard_common/src/db/models/settings.rs b/crates/defguard_common/src/db/models/settings.rs index 8daaf158a..ad132f544 100644 --- a/crates/defguard_common/src/db/models/settings.rs +++ b/crates/defguard_common/src/db/models/settings.rs @@ -10,7 +10,7 @@ use url::Url; use utoipa::ToSchema; use uuid::Uuid; -use crate::{db::Id, global_value, secret::SecretStringWrapper}; +use crate::{db::Id, global_value, secret::SecretStringWrapper, types::AuthFlowType}; global_value!(SETTINGS, Option, None, set_settings, get_settings); @@ -529,6 +529,19 @@ impl Settings { pub fn proxy_public_url(&self) -> Result { Url::parse(&self.public_proxy_url) } + + /// Returns configured Edge Component URL with the correct callback path appended depending on auth flow type. + pub fn edge_callback_url(&self, auth_flow_type: AuthFlowType) -> Result { + let mut url = self.proxy_public_url()?; + // Append callback segments to the URL. + if let Ok(mut path_segments) = url.path_segments_mut() { + match auth_flow_type { + AuthFlowType::Enrollment => path_segments.extend(&["openid", "callback"]), + AuthFlowType::Mfa => path_segments.extend(&["openid", "mfa", "callback"]), + }; + } + Ok(url) + } } #[derive(Serialize)] @@ -676,4 +689,35 @@ mod test { "https://defguard.example.com:8443/path/auth/callback" ); } + + #[test] + fn test_edge_callback_url() { + let mut s = Settings { + public_proxy_url: "https://edge.example.com".into(), + ..Default::default() + }; + + assert_eq!( + s.edge_callback_url(AuthFlowType::Enrollment) + .unwrap() + .as_str(), + "https://edge.example.com/openid/callback" + ); + assert_eq!( + s.edge_callback_url(AuthFlowType::Mfa).unwrap().as_str(), + "https://edge.example.com/openid/mfa/callback" + ); + + s.public_proxy_url = "https://edge.example.com:8443/path".into(); + assert_eq!( + s.edge_callback_url(AuthFlowType::Enrollment) + .unwrap() + .as_str(), + "https://edge.example.com:8443/path/openid/callback" + ); + assert_eq!( + s.edge_callback_url(AuthFlowType::Mfa).unwrap().as_str(), + "https://edge.example.com:8443/path/openid/mfa/callback" + ); + } } diff --git a/crates/defguard_common/src/types/mod.rs b/crates/defguard_common/src/types/mod.rs index 04247cadd..2b5d8ad81 100644 --- a/crates/defguard_common/src/types/mod.rs +++ b/crates/defguard_common/src/types/mod.rs @@ -3,3 +3,9 @@ pub mod proxy; pub mod user_info; pub type UrlParseError = url::ParseError; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AuthFlowType { + Enrollment, + Mfa, +} diff --git a/crates/defguard_core/src/enterprise/grpc/desktop_client_mfa.rs b/crates/defguard_core/src/enterprise/grpc/desktop_client_mfa.rs index e23c402c2..6b595cde4 100644 --- a/crates/defguard_core/src/enterprise/grpc/desktop_client_mfa.rs +++ b/crates/defguard_core/src/enterprise/grpc/desktop_client_mfa.rs @@ -1,6 +1,6 @@ +use defguard_common::{db::models::Settings, types::AuthFlowType}; use defguard_proto::proxy::{ClientMfaOidcAuthenticateRequest, DeviceInfo, MfaMethod}; use openidconnect::{AuthorizationCode, Nonce}; -use reqwest::Url; use tonic::Status; use crate::{ @@ -84,10 +84,12 @@ impl ClientMfaServer { ); let code = AuthorizationCode::new(request.code.clone()); - let url = match Url::parse(&request.callback_url).map_err(|err| { - error!("Invalid redirect URL provided: {err}"); - Status::invalid_argument("invalid redirect URL") - }) { + let url = match Settings::get_current_settings() + .edge_callback_url(AuthFlowType::Mfa) + .map_err(|err| { + error!("Invalid callback URL configuration: {err}"); + Status::invalid_argument("invalid callback URL") + }) { Ok(url) => url, Err(status) => { self.sessions @@ -101,7 +103,7 @@ impl ClientMfaServer { location: location.clone(), device: device.clone(), method, - message: "provided invalid redirect URL".to_string(), + message: "provided invalid callback URL".to_string(), }, )), })?; diff --git a/crates/defguard_proxy_manager/src/handler.rs b/crates/defguard_proxy_manager/src/handler.rs index 06b24aba0..f16fb182c 100644 --- a/crates/defguard_proxy_manager/src/handler.rs +++ b/crates/defguard_proxy_manager/src/handler.rs @@ -12,6 +12,7 @@ use defguard_common::{ Id, models::{Settings, proxy::Proxy}, }, + types::AuthFlowType, }; use defguard_core::{ db::models::enrollment::{ENROLLMENT_TOKEN_TYPE, Token}, @@ -34,8 +35,8 @@ use defguard_core::{ }; use defguard_grpc_tls::{certs as tls_certs, connector::HttpsSchemeConnector}; use defguard_proto::proxy::{ - AuthCallbackResponse, AuthInfoResponse, CoreError, CoreRequest, CoreResponse, InitialInfo, - core_request, core_response, proxy_client::ProxyClient, + AuthCallbackResponse, AuthFlowType as ProtoAuthFlowType, AuthInfoResponse, CoreError, + CoreRequest, CoreResponse, InitialInfo, core_request, core_response, proxy_client::ProxyClient, }; use defguard_version::{ ComponentInfo, DefguardComponent, client::ClientVersionInterceptor, get_tracing_variables, @@ -624,70 +625,90 @@ impl ProxyHandler { status_code: Code::FailedPrecondition as i32, message: "no valid license".into(), })) - } else if let Ok(redirect_url) = Url::parse(&request.redirect_url) { - if let Some(provider) = OpenIdProvider::get_current(&pool).await? { - match make_oidc_client(redirect_url, &provider).await { - Ok((_client_id, client)) => { - let mut authorize_url_builder = client - .authorize_url( - CoreAuthenticationFlow::AuthorizationCode, - || build_state(request.state), - Nonce::new_random, - ) - .add_scope(Scope::new("email".to_string())) - .add_scope(Scope::new("profile".to_string())); - - if SELECT_ACCOUNT_SUPPORTED_PROVIDERS - .iter() - .all(|p| p.eq_ignore_ascii_case(&provider.name)) - { - authorize_url_builder = authorize_url_builder - .add_prompt( - openidconnect::core::CoreAuthPrompt::SelectAccount, - ); + } else { + let redirect_url = match request.auth_flow_type() { + ProtoAuthFlowType::Enrollment => { + let settings = Settings::get_current_settings(); + settings.edge_callback_url(AuthFlowType::Enrollment) + } + ProtoAuthFlowType::Mfa => { + let settings = Settings::get_current_settings(); + settings.edge_callback_url(AuthFlowType::Mfa) + } + // fall back for legacy pre-2.0 clients + ProtoAuthFlowType::Unspecified => + { + #[allow(deprecated)] + Url::parse(&request.redirect_url) + } + }; + + if let Ok(redirect_url) = redirect_url { + if let Some(provider) = + OpenIdProvider::get_current(&pool).await? + { + match make_oidc_client(redirect_url, &provider).await { + Ok((_client_id, client)) => { + let mut authorize_url_builder = client + .authorize_url( + CoreAuthenticationFlow::AuthorizationCode, + || build_state(request.state), + Nonce::new_random, + ) + .add_scope(Scope::new("email".to_string())) + .add_scope(Scope::new("profile".to_string())); + + if SELECT_ACCOUNT_SUPPORTED_PROVIDERS + .iter() + .all(|p| p.eq_ignore_ascii_case(&provider.name)) + { + authorize_url_builder = authorize_url_builder + .add_prompt( + openidconnect::core::CoreAuthPrompt::SelectAccount, + ); + } + let (url, csrf_token, nonce) = + authorize_url_builder.url(); + + Some(core_response::Payload::AuthInfo( + AuthInfoResponse { + url: url.into(), + csrf_token: csrf_token.secret().to_owned(), + nonce: nonce.secret().to_owned(), + button_display_name: provider.display_name, + }, + )) + } + Err(err) => { + error!( + "Failed to setup external OIDC provider client: {err}" + ); + Some(core_response::Payload::CoreError(CoreError { + status_code: Code::Internal as i32, + message: "failed to build OIDC client".into(), + })) } - let (url, csrf_token, nonce) = - authorize_url_builder.url(); - - Some(core_response::Payload::AuthInfo( - AuthInfoResponse { - url: url.into(), - csrf_token: csrf_token.secret().to_owned(), - nonce: nonce.secret().to_owned(), - button_display_name: provider.display_name, - }, - )) - } - Err(err) => { - error!( - "Failed to setup external OIDC provider client: {err}" - ); - Some(core_response::Payload::CoreError(CoreError { - status_code: Code::Internal as i32, - message: "failed to build OIDC client".into(), - })) } + } else { + error!("Failed to get current OpenID provider"); + Some(core_response::Payload::CoreError(CoreError { + status_code: Code::NotFound as i32, + message: "failed to get current OpenID provider".into(), + })) } } else { - error!("Failed to get current OpenID provider"); + error!("Invalid redirect URL in authentication info request"); Some(core_response::Payload::CoreError(CoreError { - status_code: Code::NotFound as i32, - message: "failed to get current OpenID provider".into(), + status_code: Code::Internal as i32, + message: "invalid redirect URL".into(), })) } - } else { - error!( - "Invalid redirect URL in authentication info request: {}", - request.redirect_url - ); - Some(core_response::Payload::CoreError(CoreError { - status_code: Code::Internal as i32, - message: "invalid redirect URL".into(), - })) } } Some(core_request::Payload::AuthCallback(request)) => { - match Url::parse(&request.callback_url) { + match Settings::get_current_settings() + .edge_callback_url(AuthFlowType::Enrollment) + { Ok(callback_url) => { let code = AuthorizationCode::new(request.code); match user_from_claims( @@ -759,8 +780,7 @@ impl ProxyHandler { Err(err) => { error!( "Proxy requested an OpenID authentication info for a callback \ - URL ({}) that couldn't be parsed. Details: {err}", - request.callback_url + URL that couldn't be built. Details: {err}" ); Some(core_response::Payload::CoreError(CoreError { status_code: Code::Internal as i32, diff --git a/flake.lock b/flake.lock index 9e9c9e4be..9b2d3e562 100644 --- a/flake.lock +++ b/flake.lock @@ -32,11 +32,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1771369470, - "narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=", + "lastModified": 1771848320, + "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0182a361324364ae3f436a63005877674cf45efb", + "rev": "2fc6539b481e1d2569f25f8799236694180c0993", "type": "github" }, "original": { @@ -74,11 +74,11 @@ ] }, "locked": { - "lastModified": 1771556776, - "narHash": "sha256-zKprqMQDl3xVfhSSYvgru1IGXjFdxryWk+KqK0I20Xk=", + "lastModified": 1771988922, + "narHash": "sha256-Fc6FHXtfEkLtuVJzd0B6tFYMhmcPLuxr90rWfb/2jtQ=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8b3f46b8a6d17ab46e533a5e3d5b1cc2ff228860", + "rev": "f4443dc3f0b6c5e6b77d923156943ce816d1fcb9", "type": "github" }, "original": { diff --git a/proto b/proto index faebcc544..30c9e8e5c 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit faebcc5449ae803e15cf5faf838c0c508401caf1 +Subproject commit 30c9e8e5c1d6c05c5aa8132fe48952eaca17f5b9 diff --git a/web/messages/en/edge.json b/web/messages/en/edge.json index 9eb606ef6..39d05a93f 100644 --- a/web/messages/en/edge.json +++ b/web/messages/en/edge.json @@ -1,15 +1,15 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", - "edge_title": "Edge components", - "edge_edit_title": "Edit edge component", + "edge_title": "Edge Components", + "edge_edit_title": "Edit Edge Component", "edge_edit_general_info": "General information", "edge_edit_name": "Name", "edge_edit_address": "IP or Domain", "edge_edit_port": "gRPC port", "edge_edit_public_address": "Public domain", "edge_edit_delete": "Delete", - "edge_edit_success": "Edge component updated", - "edge_edit_failed": "Failed to update edge component", + "edge_edit_success": "Edge Component updated", + "edge_edit_failed": "Failed to update Edge Component", "edges_header_title": "All components", "edges_col_name": "Name", "edges_col_address": "Address", @@ -19,11 +19,12 @@ "edges_col_modified_by": "Modified by", "edges_col_status": "Status", "edges_row_menu_edit": "Edit", - "edges_empty_title": "No edge components added yet.", - "edges_empty_subtitle": "Add edge components by clicking the button below.", + "edges_empty_title": "No Edge Components added yet.", + "edges_empty_subtitle": "Add Edge Components by clicking the button below.", "edges_search_placeholder": "Search", - "edge_delete_success": "Edge component deleted", - "edge_delete_failed": "Failed to delete edge component", + "edge_delete_success": "Edge Component deleted", + "edge_delete_failed": "Failed to delete Edge Component", "edge_connected": "Connected", - "edge_disconnected": "Disconnected" + "edge_disconnected": "Disconnected", + "edge_add": "Add Edge Component" } diff --git a/web/messages/en/settings.json b/web/messages/en/settings.json index ff8c4635a..d7f3e95e7 100644 --- a/web/messages/en/settings.json +++ b/web/messages/en/settings.json @@ -1,5 +1,20 @@ { "$schema": "https://inlang.com/schema/inlang-message-format", + "settings_page_title": "Settings", + "settings_breadcrumb_general": "General", + "settings_breadcrumb_instance": "Instance settings", + "settings_instance_title": "Instance settings", + "settings_instance_subtitle": "Here you can configure general instance parameters.", + "settings_instance_label_name": "Instance name", + "settings_instance_label_public_proxy_url": "Public Edge Component URL", + "settings_instance_label_session_duration": "Session duration", + "settings_instance_session_duration_1": "1 day", + "settings_instance_session_duration_2": "2 days", + "settings_instance_session_duration_3": "3 days", + "settings_instance_session_duration_7": "7 days", + "settings_instance_session_duration_10": "10 days", + "settings_instance_session_duration_14": "14 days", + "settings_instance_session_duration_30": "30 days", "settings_activity_log_streaming_title": "Activity log streaming", "settings_activity_log_streaming_description": "Monitor and export real-time activity logs from your Defguard instance. Stream events to external systems for auditing, analytics, or security monitoring.", "settings_activity_log_streaming_no_upstreams": "You don't have any activity log upstreams.", @@ -9,5 +24,6 @@ "settings_activity_log_streaming_table_title": "All log streams", "settings_activity_log_streaming_table_header_name": "Name", "settings_activity_log_streaming_table_stream_type_name": "Destination", - "settings_msg_saved": "Settings saved" + "settings_msg_saved": "Settings saved", + "settings_msg_save_failed": "Failed to save settings" } diff --git a/web/package.json b/web/package.json index 137071df3..ab5afc1ce 100644 --- a/web/package.json +++ b/web/package.json @@ -22,7 +22,7 @@ "@stablelib/x25519": "^2.0.1", "@tanstack/react-form": "^1.28.3", "@tanstack/react-query": "^5.90.21", - "@tanstack/react-router": "^1.162.8", + "@tanstack/react-router": "^1.163.2", "@tanstack/react-table": "^8.21.3", "@tanstack/react-virtual": "^3.13.19", "@uidotdev/usehooks": "^2.4.1", @@ -37,7 +37,7 @@ "motion": "^12.34.3", "qrcode.react": "^4.2.0", "qs": "^6.15.0", - "radashi": "^12.7.1", + "radashi": "^12.7.2", "react": "^19.2.4", "react-dom": "^19.2.4", "react-intersection-observer": "^10.0.3", @@ -56,8 +56,8 @@ "@tanstack/devtools-vite": "^0.5.1", "@tanstack/react-devtools": "^0.9.6", "@tanstack/react-query-devtools": "^5.91.3", - "@tanstack/react-router-devtools": "^1.162.8", - "@tanstack/router-plugin": "^1.162.8", + "@tanstack/react-router-devtools": "^1.163.2", + "@tanstack/router-plugin": "^1.163.2", "@types/byte-size": "^8.1.2", "@types/humanize-duration": "^3.27.4", "@types/lodash-es": "^4.17.12", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index a7d6cc56d..8485cd99e 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -36,8 +36,8 @@ importers: specifier: ^5.90.21 version: 5.90.21(react@19.2.4) '@tanstack/react-router': - specifier: ^1.162.8 - version: 1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: ^1.163.2 + version: 1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -81,8 +81,8 @@ importers: specifier: ^6.15.0 version: 6.15.0 radashi: - specifier: ^12.7.1 - version: 12.7.1 + specifier: ^12.7.2 + version: 12.7.2 react: specifier: ^19.2.4 version: 19.2.4 @@ -130,11 +130,11 @@ importers: specifier: ^5.91.3 version: 5.91.3(@tanstack/react-query@5.90.21(react@19.2.4))(react@19.2.4) '@tanstack/react-router-devtools': - specifier: ^1.162.8 - version: 1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.162.6)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: ^1.163.2 + version: 1.163.2(@tanstack/react-router@1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.163.2)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/router-plugin': - specifier: ^1.162.8 - version: 1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.3.0)(sass@1.97.3)(tsx@4.21.0)) + specifier: ^1.163.2 + version: 1.163.2(@tanstack/react-router@1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.3.0)(sass@1.97.3)(tsx@4.21.0)) '@types/byte-size': specifier: ^8.1.2 version: 8.1.2 @@ -306,28 +306,24 @@ packages: engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - libc: [musl] '@biomejs/cli-linux-arm64@2.4.4': resolution: {integrity: sha512-V/NFfbWhsUU6w+m5WYbBenlEAz8eYnSqRMDMAW3K+3v0tYVkNyZn8VU0XPxk/lOqNXLSCCrV7FmV/u3SjCBShg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - libc: [glibc] '@biomejs/cli-linux-x64-musl@2.4.4': resolution: {integrity: sha512-gGvFTGpOIQDb5CQ2VC0n9Z2UEqlP46c4aNgHmAMytYieTGEcfqhfCFnhs6xjt0S3igE6q5GLuIXtdQt3Izok+g==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - libc: [musl] '@biomejs/cli-linux-x64@2.4.4': resolution: {integrity: sha512-R4+ZCDtG9kHArasyBO+UBD6jr/FcFCTH8QkNTOCu0pRJzCWyWC4EtZa2AmUZB5h3e0jD7bRV2KvrENcf8rndBg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - libc: [glibc] '@biomejs/cli-win32-arm64@2.4.4': resolution: {integrity: sha512-trzCqM7x+Gn832zZHgr28JoYagQNX4CZkUZhMUac2YxvvyDRLJDrb5m9IA7CaZLlX6lTQmADVfLEKP1et1Ma4Q==} @@ -596,105 +592,89 @@ packages: resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.2.4': resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.2.4': resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.2.4': resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.2.4': resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.2.4': resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.34.5': resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.34.5': resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - libc: [glibc] '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [riscv64] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.34.5': resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.34.5': resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.34.5': resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.34.5': resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.34.5': resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} @@ -803,42 +783,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.6': resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.6': resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.6': resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.6': resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.6': resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.6': resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} @@ -925,79 +899,66 @@ packages: resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} @@ -1126,28 +1087,24 @@ packages: engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [glibc] '@swc/core-linux-arm64-musl@1.15.13': resolution: {integrity: sha512-SmZ9m+XqCB35NddHCctvHFLqPZDAs5j8IgD36GoutufDJmeq2VNfgk5rQoqNqKmAK3Y7iFdEmI76QoHIWiCLyw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - libc: [musl] '@swc/core-linux-x64-gnu@1.15.13': resolution: {integrity: sha512-5rij+vB9a29aNkHq72EXI2ihDZPszJb4zlApJY4aCC/q6utgqFA6CkrfTfIb+O8hxtG3zP5KERETz8mfFK6A0A==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [glibc] '@swc/core-linux-x64-musl@1.15.13': resolution: {integrity: sha512-OlSlaOK9JplQ5qn07WiBLibkOw7iml2++ojEXhhR3rbWrNEKCD7sd8+6wSavsInyFdw4PhLA+Hy6YyDBIE23Yw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - libc: [musl] '@swc/core-win32-arm64-msvc@1.15.13': resolution: {integrity: sha512-zwQii5YVdsfG8Ti9gIKgBKZg8qMkRZxl+OlYWUT5D93Jl4NuNBRausP20tfEkQdAPSRrMCSUZBM6FhW7izAZRg==} @@ -1258,20 +1215,20 @@ packages: peerDependencies: react: ^18 || ^19 - '@tanstack/react-router-devtools@1.162.8': - resolution: {integrity: sha512-dDohOU8eNbCukLQNcuocCTnvwSu8Z1XwbKvPc4U7KDYoUTUlJls48fXl5y/ENThK/nZEsA7i3oCy1BcX42OOlw==} + '@tanstack/react-router-devtools@1.163.2': + resolution: {integrity: sha512-gk/tC+vx8eoNNIM27vfb/bZTXQjpopw7tZA4WkRQWLh9A8PG3V6QjMQysbPcRRO5m7KtdCbTk51ZG4ERi0J1kA==} engines: {node: '>=20.19'} peerDependencies: - '@tanstack/react-router': ^1.162.8 - '@tanstack/router-core': ^1.162.6 + '@tanstack/react-router': ^1.163.2 + '@tanstack/router-core': ^1.163.2 react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' peerDependenciesMeta: '@tanstack/router-core': optional: true - '@tanstack/react-router@1.162.8': - resolution: {integrity: sha512-WunoknGI5ielJ833yl/F7Vq4nv/OWzrJVBsMgyxX16Db1DwVvX/B5zTg8EMjdZUOJ7ONpvur3t4aq7KQiYRagQ==} + '@tanstack/react-router@1.163.2': + resolution: {integrity: sha512-1LosUlpL2mRMWxUZXmkEg5+Br5P5j9TrLngqRgHVbZoFkjnbcj1x9fQN2OVLrBv9Npw97NRsHeJljnAH/c7oSw==} engines: {node: '>=20.19'} peerDependencies: react: '>=18.0.0 || >=19.0.0' @@ -1302,30 +1259,30 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-core@1.162.6': - resolution: {integrity: sha512-WFMNysDsDtnlM0G0L4LPWJuvpGatlPvBLGlPnieWYKem/Ed4mRHu7Hqw78MR/CMuFSRi9Gvv91/h8F3EVswAJw==} + '@tanstack/router-core@1.163.2': + resolution: {integrity: sha512-mD0Pav6kcpS317XSJN+wCZaxLLngDhlwgzPNca56dWCp8YKPEvhhj/Zdl+LdRlJQ2VJ5BOy7FbOV1hErc9Nj5Q==} engines: {node: '>=20.19'} - '@tanstack/router-devtools-core@1.162.6': - resolution: {integrity: sha512-ni+9XmQOg9ale1e6FnhNrBymVVQAkzQ02SfAB6MgobXLp97MHiBk7d0k7DkoyVLk3tXRqmrCERWYRC8IGrcQmw==} + '@tanstack/router-devtools-core@1.163.2': + resolution: {integrity: sha512-IrbSK30AtMOgCLXTbvhnVsU6BGjhwB8EjfZIQLtUPDvFsP0RH3/2ZiWRA3a0EsQhxkl+fxIVByVP7wgyRzkZPQ==} engines: {node: '>=20.19'} peerDependencies: - '@tanstack/router-core': ^1.162.6 + '@tanstack/router-core': ^1.163.2 csstype: ^3.0.10 peerDependenciesMeta: csstype: optional: true - '@tanstack/router-generator@1.162.6': - resolution: {integrity: sha512-mzkD3kfPW50xgX1hI8YrQx76+hshsUmpI9fVvS741L0cRQKH7bCIYTvcNHkz3sftZwmjt/lh+k7arV1AMLaWhA==} + '@tanstack/router-generator@1.163.2': + resolution: {integrity: sha512-6LjU3+8iKEgt8iOaYCmCnQCs0jsOhc7z8fa1yAYlj3s82uYWv3g5CB9mwv8wZXblXBQWOl+hW4PI6WNjP/CK9w==} engines: {node: '>=20.19'} - '@tanstack/router-plugin@1.162.8': - resolution: {integrity: sha512-u6ZqYEjIA8jXge6JSl5UFFYPzVRciee0vwDwtkIF1Sb+G4cDdDaEjYQ4aN1/va8D7n3LptYvSMU8SeGkX+9slA==} + '@tanstack/router-plugin@1.163.2': + resolution: {integrity: sha512-SrVILMz/c15RYWxIMG+bf/glLbP/O9DUxOg0E7bo9pooBxGPvgWSlEzHNjhVekLhK5l7fiuQZzKsfksVeIEqDA==} engines: {node: '>=20.19'} peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.162.8 + '@tanstack/react-router': ^1.163.2 vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' vite-plugin-solid: ^2.11.10 webpack: '>=5.92.0' @@ -2142,8 +2099,8 @@ packages: resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==} engines: {node: '>=14.0.0'} - launch-editor@2.13.0: - resolution: {integrity: sha512-u+9asUHMJ99lA15VRMXw5XKfySFR9dGXwgsgS14YTbUq3GITP58mIM32At90P5fZ+MUId5Yw+IwI/yKub7jnCQ==} + launch-editor@2.13.1: + resolution: {integrity: sha512-lPSddlAAluRKJ7/cjRFoXUFzaX7q/YKI7yPHuEvSJVqoXvFnJov1/Ud87Aa4zULIbA9Nja4mSPK8l0z/7eV2wA==} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -2406,8 +2363,8 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - radashi@12.7.1: - resolution: {integrity: sha512-rwxcGY3oKMQJ+ojhS4MlxyVWqdXPVJSxg3ZjXDYYz26DYRuAAs+7XBM406/GYzPEBbjSJqdKfHDpBRfjWn34RQ==} + radashi@12.7.2: + resolution: {integrity: sha512-BfoN4XJll34ok3rCHjVlRypymJvD3cE+M3UATC8519wUgHl1/AXt2dEEIFv5865gO6t0ENAU6qEOqjr/NcIRyQ==} engines: {node: '>=16.0.0'} react-dom@19.2.4: @@ -3768,7 +3725,7 @@ snapshots: '@tanstack/devtools-client': 0.0.5 '@tanstack/devtools-event-bus': 0.4.1 chalk: 5.6.2 - launch-editor: 2.13.0 + launch-editor: 2.13.1 picomatch: 4.0.3 vite: 7.3.1(@types/node@25.3.0)(sass@1.97.3)(tsx@4.21.0) transitivePeerDependencies: @@ -3838,22 +3795,22 @@ snapshots: '@tanstack/query-core': 5.90.20 react: 19.2.4 - '@tanstack/react-router-devtools@1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.162.6)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-router-devtools@1.163.2(@tanstack/react-router@1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.163.2)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@tanstack/react-router': 1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-devtools-core': 1.162.6(@tanstack/router-core@1.162.6)(csstype@3.2.3) + '@tanstack/react-router': 1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/router-devtools-core': 1.163.2(@tanstack/router-core@1.163.2)(csstype@3.2.3) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) optionalDependencies: - '@tanstack/router-core': 1.162.6 + '@tanstack/router-core': 1.163.2 transitivePeerDependencies: - csstype - '@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-router@1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/history': 1.161.4 '@tanstack/react-store': 0.9.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-core': 1.162.6 + '@tanstack/router-core': 1.163.2 isbot: 5.1.35 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -3886,7 +3843,7 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - '@tanstack/router-core@1.162.6': + '@tanstack/router-core@1.163.2': dependencies: '@tanstack/history': 1.161.4 '@tanstack/store': 0.9.1 @@ -3896,18 +3853,18 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-devtools-core@1.162.6(@tanstack/router-core@1.162.6)(csstype@3.2.3)': + '@tanstack/router-devtools-core@1.163.2(@tanstack/router-core@1.163.2)(csstype@3.2.3)': dependencies: - '@tanstack/router-core': 1.162.6 + '@tanstack/router-core': 1.163.2 clsx: 2.1.1 goober: 2.1.18(csstype@3.2.3) tiny-invariant: 1.3.3 optionalDependencies: csstype: 3.2.3 - '@tanstack/router-generator@1.162.6': + '@tanstack/router-generator@1.163.2': dependencies: - '@tanstack/router-core': 1.162.6 + '@tanstack/router-core': 1.163.2 '@tanstack/router-utils': 1.161.4 '@tanstack/virtual-file-routes': 1.161.4 prettier: 3.8.1 @@ -3918,7 +3875,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.162.8(@tanstack/react-router@1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.3.0)(sass@1.97.3)(tsx@4.21.0))': + '@tanstack/router-plugin@1.163.2(@tanstack/react-router@1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@25.3.0)(sass@1.97.3)(tsx@4.21.0))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) @@ -3926,15 +3883,15 @@ snapshots: '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 - '@tanstack/router-core': 1.162.6 - '@tanstack/router-generator': 1.162.6 + '@tanstack/router-core': 1.163.2 + '@tanstack/router-generator': 1.163.2 '@tanstack/router-utils': 1.161.4 '@tanstack/virtual-file-routes': 1.161.4 chokidar: 3.6.0 unplugin: 2.3.11 zod: 3.25.76 optionalDependencies: - '@tanstack/react-router': 1.162.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-router': 1.163.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4) vite: 7.3.1(@types/node@25.3.0)(sass@1.97.3)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -4692,7 +4649,7 @@ snapshots: kysely@0.27.6: {} - launch-editor@2.13.0: + launch-editor@2.13.1: dependencies: picocolors: 1.1.1 shell-quote: 1.8.3 @@ -5059,7 +5016,7 @@ snapshots: queue-microtask@1.2.3: {} - radashi@12.7.1: {} + radashi@12.7.2: {} react-dom@19.2.4(react@19.2.4): dependencies: diff --git a/web/src/pages/EdgesPage/EdgesTable.tsx b/web/src/pages/EdgesPage/EdgesTable.tsx index 2d9abe550..80b3d67da 100644 --- a/web/src/pages/EdgesPage/EdgesTable.tsx +++ b/web/src/pages/EdgesPage/EdgesTable.tsx @@ -62,7 +62,7 @@ export const EdgesTable = () => { const addButtonProps = useMemo( (): ButtonProps => ({ variant: 'primary', - text: 'Add Edge component', + text: m.edge_add(), iconLeft: 'globe', testId: 'add-edge', onClick: () => { diff --git a/web/src/pages/settings/SettingsInstancePage/SettingsInstancePage.tsx b/web/src/pages/settings/SettingsInstancePage/SettingsInstancePage.tsx index e026c78d7..be047c14b 100644 --- a/web/src/pages/settings/SettingsInstancePage/SettingsInstancePage.tsx +++ b/web/src/pages/settings/SettingsInstancePage/SettingsInstancePage.tsx @@ -12,6 +12,9 @@ import { SettingsCard } from '../../../shared/components/SettingsCard/SettingsCa import { SettingsHeader } from '../../../shared/components/SettingsHeader/SettingsHeader'; import { SettingsLayout } from '../../../shared/components/SettingsLayout/SettingsLayout'; import { Button } from '../../../shared/defguard-ui/components/Button/Button'; +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 { isPresent } from '../../../shared/defguard-ui/utils/isPresent'; import { useAppForm } from '../../../shared/form'; import { formChangeLogic } from '../../../shared/formLogic'; @@ -25,23 +28,23 @@ const breadcrumbs = [ }} key={0} > - General + {m.settings_breadcrumb_general()} , - Instance settings + {m.settings_breadcrumb_instance()} , ]; export const SettingsInstancePage = () => { const { data: settings } = useQuery(getSettingsQueryOptions); return ( - + {isPresent(settings) && ( @@ -65,23 +68,61 @@ const formSchema = z.object({ }), ) .max(64, m.form_error_max_len({ length: 64 })), + 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()), + authentication_period_days: z.number().min(1, m.form_error_invalid()), }); type FormFields = z.infer; +const sessionDurationOptions = [ + { key: 1, value: 1, label: m.settings_instance_session_duration_1() }, + { key: 2, value: 2, label: m.settings_instance_session_duration_2() }, + { key: 3, value: 3, label: m.settings_instance_session_duration_3() }, + { key: 7, value: 7, label: m.settings_instance_session_duration_7() }, + { + key: 10, + value: 10, + label: m.settings_instance_session_duration_10(), + }, + { + key: 14, + value: 14, + label: m.settings_instance_session_duration_14(), + }, + { + key: 30, + value: 30, + label: m.settings_instance_session_duration_30(), + }, +]; + const Content = ({ settings }: { settings: Settings }) => { const { mutateAsync } = useMutation({ mutationFn: api.settings.patchSettings, meta: { invalidate: ['settings'], }, + onSuccess: () => { + Snackbar.success(m.settings_msg_saved()); + }, + onError: () => { + Snackbar.error(m.settings_msg_save_failed()); + }, }); const defaultValues = useMemo( (): FormFields => ({ instance_name: settings.instance_name ?? '', + public_proxy_url: settings.public_proxy_url ?? '', + authentication_period_days: settings.authentication_period_days ?? 7, }), - [settings.instance_name], + [ + settings.instance_name, + settings.public_proxy_url, + settings.authentication_period_days, + ], ); const form = useAppForm({ @@ -93,6 +134,7 @@ const Content = ({ settings }: { settings: Settings }) => { }, onSubmit: async ({ value }) => { await mutateAsync(value); + form.reset(value); }, }); @@ -106,22 +148,44 @@ const Content = ({ settings }: { settings: Settings }) => { > - {(field) => } + {(field) => ( + + )} + + + + {(field) => ( + + )} + + + + {(field) => ( + + )} ({ isDefault: s.isDefaultValue || s.isPristine, isSubmitting: s.isSubmitting, + canSubmit: s.canSubmit, })} > - {({ isDefault, isSubmitting }) => ( + {({ isDefault, isSubmitting, canSubmit }) => (