diff --git a/.env.example b/.env.example index a401835..337c06a 100644 --- a/.env.example +++ b/.env.example @@ -74,6 +74,11 @@ LANGGRAPH_URL= LANGFLOW_URL= LANGFUSE_URL= +# --------------------------------------------------------------------------- +# Compliance scanner (external tool, opens in new tab) [OPTIONAL] +# --------------------------------------------------------------------------- +COMPLIANCE_SCANNER_URL= + # --------------------------------------------------------------------------- # Vector database [OPTIONAL] # --------------------------------------------------------------------------- diff --git a/assets/i18n/de.json b/assets/i18n/de.json index 1c895fd..63136b3 100644 --- a/assets/i18n/de.json +++ b/assets/i18n/de.json @@ -46,7 +46,8 @@ "agents": "Agenten", "flow": "Flow", "analytics": "Analytics", - "pricing": "Preise" + "pricing": "Preise", + "compliance": "Compliance" }, "auth": { "redirecting_login": "Weiterleitung zur Anmeldung...", diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 7334837..a327529 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -46,7 +46,8 @@ "agents": "Agents", "flow": "Flow", "analytics": "Analytics", - "pricing": "Pricing" + "pricing": "Pricing", + "compliance": "Compliance" }, "auth": { "redirecting_login": "Redirecting to login...", diff --git a/assets/i18n/es.json b/assets/i18n/es.json index 8ebe514..f51a439 100644 --- a/assets/i18n/es.json +++ b/assets/i18n/es.json @@ -46,7 +46,8 @@ "agents": "Agentes", "flow": "Flujo", "analytics": "Estadisticas", - "pricing": "Precios" + "pricing": "Precios", + "compliance": "Cumplimiento" }, "auth": { "redirecting_login": "Redirigiendo al inicio de sesion...", diff --git a/assets/i18n/fr.json b/assets/i18n/fr.json index 1cd640a..840ce33 100644 --- a/assets/i18n/fr.json +++ b/assets/i18n/fr.json @@ -46,7 +46,8 @@ "agents": "Agents", "flow": "Flux", "analytics": "Analytique", - "pricing": "Tarifs" + "pricing": "Tarifs", + "compliance": "Conformite" }, "auth": { "redirecting_login": "Redirection vers la connexion...", diff --git a/assets/i18n/pt.json b/assets/i18n/pt.json index b3db706..7b72fba 100644 --- a/assets/i18n/pt.json +++ b/assets/i18n/pt.json @@ -46,7 +46,8 @@ "agents": "Agentes", "flow": "Fluxo", "analytics": "Analise", - "pricing": "Precos" + "pricing": "Precos", + "compliance": "Conformidade" }, "auth": { "redirecting_login": "A redirecionar para o inicio de sessao...", diff --git a/src/components/app_shell.rs b/src/components/app_shell.rs index 2eb96aa..f03c786 100644 --- a/src/components/app_shell.rs +++ b/src/components/app_shell.rs @@ -76,6 +76,7 @@ pub fn AppShell() -> Element { name: info.name, avatar_url: info.avatar_url, librechat_url: info.librechat_url, + compliance_scanner_url: info.compliance_scanner_url, class: sidebar_cls, on_nav: move |_| mobile_menu_open.set(false), } diff --git a/src/components/sidebar.rs b/src/components/sidebar.rs index d8beba0..494ec9b 100644 --- a/src/components/sidebar.rs +++ b/src/components/sidebar.rs @@ -1,7 +1,7 @@ use dioxus::prelude::*; use dioxus_free_icons::icons::bs_icons::{ BsBoxArrowRight, BsBuilding, BsChatDots, BsCloudArrowUp, BsCodeSlash, BsGithub, BsGlobe2, - BsGrid, BsHouseDoor, BsMoonFill, BsSunFill, + BsGrid, BsHouseDoor, BsMoonFill, BsShieldCheck, BsSunFill, }; use dioxus_free_icons::Icon; @@ -44,13 +44,14 @@ pub fn Sidebar( email: String, avatar_url: String, #[props(default = "http://localhost:3080".to_string())] librechat_url: String, + #[props(default)] compliance_scanner_url: String, #[props(default = "sidebar".to_string())] class: String, #[props(default)] on_nav: EventHandler<()>, ) -> Element { let locale = use_context::>(); let locale_val = *locale.read(); - let nav_items: Vec = vec![ + let mut nav_items: Vec = vec![ NavItem { key: "dashboard", label: t(locale_val, "nav.dashboard"), @@ -84,6 +85,16 @@ pub fn Sidebar( }, ]; + // Only show the compliance scanner link when a URL is configured. + if !compliance_scanner_url.is_empty() { + nav_items.push(NavItem { + key: "compliance", + label: t(locale_val, "nav.compliance"), + target: NavTarget::External(compliance_scanner_url.clone()), + icon: rsx! { Icon { icon: BsShieldCheck, width: 18, height: 18 } }, + }); + } + // Determine current path to highlight the active nav link. let current_route = use_route::(); let logout_label = t(locale_val, "common.logout"); diff --git a/src/infrastructure/auth_check.rs b/src/infrastructure/auth_check.rs index 009ec52..9f1cc2a 100644 --- a/src/infrastructure/auth_check.rs +++ b/src/infrastructure/auth_check.rs @@ -35,6 +35,8 @@ pub async fn check_auth() -> Result { let langgraph_url = state.services.langgraph_url.clone(); let langflow_url = state.services.langflow_url.clone(); let langfuse_url = state.services.langfuse_url.clone(); + let compliance_scanner_url = + state.services.compliance_scanner_url.clone(); Ok(AuthInfo { authenticated: true, @@ -46,6 +48,7 @@ pub async fn check_auth() -> Result { langgraph_url, langflow_url, langfuse_url, + compliance_scanner_url, }) } None => Ok(AuthInfo::default()), diff --git a/src/infrastructure/config.rs b/src/infrastructure/config.rs index 8f82d9e..eecec0d 100644 --- a/src/infrastructure/config.rs +++ b/src/infrastructure/config.rs @@ -168,6 +168,8 @@ pub struct ServiceUrls { pub s3_access_key: String, /// S3 secret key (wrapped for debug safety). pub s3_secret_key: SecretString, + /// Compliance scanner URL (external tool opened in a new tab). + pub compliance_scanner_url: String, } impl ServiceUrls { @@ -194,6 +196,7 @@ impl ServiceUrls { s3_url: optional_env("S3_URL"), s3_access_key: optional_env("S3_ACCESS_KEY"), s3_secret_key: SecretString::from(optional_env("S3_SECRET_KEY")), + compliance_scanner_url: optional_env("COMPLIANCE_SCANNER_URL"), }) } } diff --git a/src/models/user.rs b/src/models/user.rs index 4b7b615..0fa23ba 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -30,6 +30,8 @@ pub struct AuthInfo { pub langflow_url: String, /// Langfuse observability URL (empty if not configured) pub langfuse_url: String, + /// Compliance scanner URL (empty if not configured) + pub compliance_scanner_url: String, } /// Per-user LLM provider configuration stored in MongoDB. @@ -100,6 +102,7 @@ mod tests { assert_eq!(info.langgraph_url, ""); assert_eq!(info.langflow_url, ""); assert_eq!(info.langfuse_url, ""); + assert_eq!(info.compliance_scanner_url, ""); } #[test] @@ -114,6 +117,7 @@ mod tests { langgraph_url: "http://localhost:8123".into(), langflow_url: "http://localhost:7860".into(), langfuse_url: "http://localhost:3000".into(), + compliance_scanner_url: "http://localhost:9090".into(), }; let json = serde_json::to_string(&info).expect("serialize AuthInfo"); let back: AuthInfo = serde_json::from_str(&json).expect("deserialize AuthInfo");