feat(sidebar): add compliance scanner link from env config #19
@@ -74,6 +74,11 @@ LANGGRAPH_URL=
|
|||||||
LANGFLOW_URL=
|
LANGFLOW_URL=
|
||||||
LANGFUSE_URL=
|
LANGFUSE_URL=
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Compliance scanner (external tool, opens in new tab) [OPTIONAL]
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
COMPLIANCE_SCANNER_URL=
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Vector database [OPTIONAL]
|
# Vector database [OPTIONAL]
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -46,7 +46,8 @@
|
|||||||
"agents": "Agenten",
|
"agents": "Agenten",
|
||||||
"flow": "Flow",
|
"flow": "Flow",
|
||||||
"analytics": "Analytics",
|
"analytics": "Analytics",
|
||||||
"pricing": "Preise"
|
"pricing": "Preise",
|
||||||
|
"compliance": "Compliance"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"redirecting_login": "Weiterleitung zur Anmeldung...",
|
"redirecting_login": "Weiterleitung zur Anmeldung...",
|
||||||
|
|||||||
@@ -46,7 +46,8 @@
|
|||||||
"agents": "Agents",
|
"agents": "Agents",
|
||||||
"flow": "Flow",
|
"flow": "Flow",
|
||||||
"analytics": "Analytics",
|
"analytics": "Analytics",
|
||||||
"pricing": "Pricing"
|
"pricing": "Pricing",
|
||||||
|
"compliance": "Compliance"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"redirecting_login": "Redirecting to login...",
|
"redirecting_login": "Redirecting to login...",
|
||||||
|
|||||||
@@ -46,7 +46,8 @@
|
|||||||
"agents": "Agentes",
|
"agents": "Agentes",
|
||||||
"flow": "Flujo",
|
"flow": "Flujo",
|
||||||
"analytics": "Estadisticas",
|
"analytics": "Estadisticas",
|
||||||
"pricing": "Precios"
|
"pricing": "Precios",
|
||||||
|
"compliance": "Cumplimiento"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"redirecting_login": "Redirigiendo al inicio de sesion...",
|
"redirecting_login": "Redirigiendo al inicio de sesion...",
|
||||||
|
|||||||
@@ -46,7 +46,8 @@
|
|||||||
"agents": "Agents",
|
"agents": "Agents",
|
||||||
"flow": "Flux",
|
"flow": "Flux",
|
||||||
"analytics": "Analytique",
|
"analytics": "Analytique",
|
||||||
"pricing": "Tarifs"
|
"pricing": "Tarifs",
|
||||||
|
"compliance": "Conformite"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"redirecting_login": "Redirection vers la connexion...",
|
"redirecting_login": "Redirection vers la connexion...",
|
||||||
|
|||||||
@@ -46,7 +46,8 @@
|
|||||||
"agents": "Agentes",
|
"agents": "Agentes",
|
||||||
"flow": "Fluxo",
|
"flow": "Fluxo",
|
||||||
"analytics": "Analise",
|
"analytics": "Analise",
|
||||||
"pricing": "Precos"
|
"pricing": "Precos",
|
||||||
|
"compliance": "Conformidade"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"redirecting_login": "A redirecionar para o inicio de sessao...",
|
"redirecting_login": "A redirecionar para o inicio de sessao...",
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ pub fn AppShell() -> Element {
|
|||||||
name: info.name,
|
name: info.name,
|
||||||
avatar_url: info.avatar_url,
|
avatar_url: info.avatar_url,
|
||||||
librechat_url: info.librechat_url,
|
librechat_url: info.librechat_url,
|
||||||
|
compliance_scanner_url: info.compliance_scanner_url,
|
||||||
class: sidebar_cls,
|
class: sidebar_cls,
|
||||||
on_nav: move |_| mobile_menu_open.set(false),
|
on_nav: move |_| mobile_menu_open.set(false),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_free_icons::icons::bs_icons::{
|
use dioxus_free_icons::icons::bs_icons::{
|
||||||
BsBoxArrowRight, BsBuilding, BsChatDots, BsCloudArrowUp, BsCodeSlash, BsGithub, BsGlobe2,
|
BsBoxArrowRight, BsBuilding, BsChatDots, BsCloudArrowUp, BsCodeSlash, BsGithub, BsGlobe2,
|
||||||
BsGrid, BsHouseDoor, BsMoonFill, BsSunFill,
|
BsGrid, BsHouseDoor, BsMoonFill, BsShieldCheck, BsSunFill,
|
||||||
};
|
};
|
||||||
use dioxus_free_icons::Icon;
|
use dioxus_free_icons::Icon;
|
||||||
|
|
||||||
@@ -44,13 +44,14 @@ pub fn Sidebar(
|
|||||||
email: String,
|
email: String,
|
||||||
avatar_url: String,
|
avatar_url: String,
|
||||||
#[props(default = "http://localhost:3080".to_string())] librechat_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 = "sidebar".to_string())] class: String,
|
||||||
#[props(default)] on_nav: EventHandler<()>,
|
#[props(default)] on_nav: EventHandler<()>,
|
||||||
) -> Element {
|
) -> Element {
|
||||||
let locale = use_context::<Signal<Locale>>();
|
let locale = use_context::<Signal<Locale>>();
|
||||||
let locale_val = *locale.read();
|
let locale_val = *locale.read();
|
||||||
|
|
||||||
let nav_items: Vec<NavItem> = vec![
|
let mut nav_items: Vec<NavItem> = vec![
|
||||||
NavItem {
|
NavItem {
|
||||||
key: "dashboard",
|
key: "dashboard",
|
||||||
label: t(locale_val, "nav.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.
|
// Determine current path to highlight the active nav link.
|
||||||
let current_route = use_route::<Route>();
|
let current_route = use_route::<Route>();
|
||||||
let logout_label = t(locale_val, "common.logout");
|
let logout_label = t(locale_val, "common.logout");
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ pub async fn check_auth() -> Result<AuthInfo, ServerFnError> {
|
|||||||
let langgraph_url = state.services.langgraph_url.clone();
|
let langgraph_url = state.services.langgraph_url.clone();
|
||||||
let langflow_url = state.services.langflow_url.clone();
|
let langflow_url = state.services.langflow_url.clone();
|
||||||
let langfuse_url = state.services.langfuse_url.clone();
|
let langfuse_url = state.services.langfuse_url.clone();
|
||||||
|
let compliance_scanner_url = state.services.compliance_scanner_url.clone();
|
||||||
|
|
||||||
Ok(AuthInfo {
|
Ok(AuthInfo {
|
||||||
authenticated: true,
|
authenticated: true,
|
||||||
@@ -46,6 +47,7 @@ pub async fn check_auth() -> Result<AuthInfo, ServerFnError> {
|
|||||||
langgraph_url,
|
langgraph_url,
|
||||||
langflow_url,
|
langflow_url,
|
||||||
langfuse_url,
|
langfuse_url,
|
||||||
|
compliance_scanner_url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
None => Ok(AuthInfo::default()),
|
None => Ok(AuthInfo::default()),
|
||||||
|
|||||||
@@ -168,6 +168,8 @@ pub struct ServiceUrls {
|
|||||||
pub s3_access_key: String,
|
pub s3_access_key: String,
|
||||||
/// S3 secret key (wrapped for debug safety).
|
/// S3 secret key (wrapped for debug safety).
|
||||||
pub s3_secret_key: SecretString,
|
pub s3_secret_key: SecretString,
|
||||||
|
/// Compliance scanner URL (external tool opened in a new tab).
|
||||||
|
pub compliance_scanner_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServiceUrls {
|
impl ServiceUrls {
|
||||||
@@ -194,6 +196,7 @@ impl ServiceUrls {
|
|||||||
s3_url: optional_env("S3_URL"),
|
s3_url: optional_env("S3_URL"),
|
||||||
s3_access_key: optional_env("S3_ACCESS_KEY"),
|
s3_access_key: optional_env("S3_ACCESS_KEY"),
|
||||||
s3_secret_key: SecretString::from(optional_env("S3_SECRET_KEY")),
|
s3_secret_key: SecretString::from(optional_env("S3_SECRET_KEY")),
|
||||||
|
compliance_scanner_url: optional_env("COMPLIANCE_SCANNER_URL"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ pub struct AuthInfo {
|
|||||||
pub langflow_url: String,
|
pub langflow_url: String,
|
||||||
/// Langfuse observability URL (empty if not configured)
|
/// Langfuse observability URL (empty if not configured)
|
||||||
pub langfuse_url: String,
|
pub langfuse_url: String,
|
||||||
|
/// Compliance scanner URL (empty if not configured)
|
||||||
|
pub compliance_scanner_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Per-user LLM provider configuration stored in MongoDB.
|
/// Per-user LLM provider configuration stored in MongoDB.
|
||||||
@@ -100,6 +102,7 @@ mod tests {
|
|||||||
assert_eq!(info.langgraph_url, "");
|
assert_eq!(info.langgraph_url, "");
|
||||||
assert_eq!(info.langflow_url, "");
|
assert_eq!(info.langflow_url, "");
|
||||||
assert_eq!(info.langfuse_url, "");
|
assert_eq!(info.langfuse_url, "");
|
||||||
|
assert_eq!(info.compliance_scanner_url, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -114,6 +117,7 @@ mod tests {
|
|||||||
langgraph_url: "http://localhost:8123".into(),
|
langgraph_url: "http://localhost:8123".into(),
|
||||||
langflow_url: "http://localhost:7860".into(),
|
langflow_url: "http://localhost:7860".into(),
|
||||||
langfuse_url: "http://localhost:3000".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 json = serde_json::to_string(&info).expect("serialize AuthInfo");
|
||||||
let back: AuthInfo = serde_json::from_str(&json).expect("deserialize AuthInfo");
|
let back: AuthInfo = serde_json::from_str(&json).expect("deserialize AuthInfo");
|
||||||
|
|||||||
Reference in New Issue
Block a user