Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd6d4572e0 | |||
| 78b215bb77 | |||
| 58428892a8 | |||
| 24b604ce51 | |||
| a02827a34b | |||
| 5b431f65dc | |||
| b5ee887387 | |||
| 75a35dbb85 |
@@ -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]
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
+25
-5
@@ -262,10 +262,30 @@ jobs:
|
||||
needs: [test]
|
||||
if: github.ref == 'refs/heads/main'
|
||||
container:
|
||||
image: alpine:latest
|
||||
image: docker:27-cli
|
||||
steps:
|
||||
- name: Trigger Coolify deploy
|
||||
- name: Checkout
|
||||
run: |
|
||||
apk add --no-cache curl
|
||||
curl -sf "${{ secrets.COOLIFY_WEBHOOK }}" \
|
||||
-H "Authorization: Bearer ${{ secrets.COOLIFY_TOKEN }}"
|
||||
apk add --no-cache git curl openssl
|
||||
git init
|
||||
git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
|
||||
git fetch --depth=1 origin "${GITHUB_SHA}"
|
||||
git checkout FETCH_HEAD
|
||||
- name: Build and push image
|
||||
run: |
|
||||
IMAGE=registry.meghsakha.com/certifai-dashboard
|
||||
echo "${{ secrets.REGISTRY_PASSWORD }}" | \
|
||||
docker login registry.meghsakha.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
|
||||
docker build -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
|
||||
docker push "$IMAGE:latest"
|
||||
docker push "$IMAGE:${GITHUB_SHA}"
|
||||
- name: Trigger orca redeploy
|
||||
run: |
|
||||
PAYLOAD=$(printf '{"ref":"refs/heads/main","repository":{"full_name":"sharang/certifai"},"head_commit":{"id":"%s","message":"CI deploy"}}' "${GITHUB_SHA}")
|
||||
SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.ORCA_WEBHOOK_SECRET }}" | awk '{print $2}')
|
||||
echo "Calling orca webhook for sharang/certifai@${GITHUB_SHA}"
|
||||
RESP=$(curl -fsS -w "\nHTTP %{http_code}" -X POST "http://46.225.100.82:6880/api/v1/webhooks/github" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Hub-Signature-256: sha256=$SIG" \
|
||||
-d "$PAYLOAD")
|
||||
echo "$RESP"
|
||||
|
||||
Generated
+393
-332
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -46,7 +46,8 @@
|
||||
"agents": "Agenten",
|
||||
"flow": "Flow",
|
||||
"analytics": "Analytics",
|
||||
"pricing": "Preise"
|
||||
"pricing": "Preise",
|
||||
"compliance": "Compliance"
|
||||
},
|
||||
"auth": {
|
||||
"redirecting_login": "Weiterleitung zur Anmeldung...",
|
||||
|
||||
+2
-1
@@ -46,7 +46,8 @@
|
||||
"agents": "Agents",
|
||||
"flow": "Flow",
|
||||
"analytics": "Analytics",
|
||||
"pricing": "Pricing"
|
||||
"pricing": "Pricing",
|
||||
"compliance": "Compliance"
|
||||
},
|
||||
"auth": {
|
||||
"redirecting_login": "Redirecting to login...",
|
||||
|
||||
+2
-1
@@ -46,7 +46,8 @@
|
||||
"agents": "Agentes",
|
||||
"flow": "Flujo",
|
||||
"analytics": "Estadisticas",
|
||||
"pricing": "Precios"
|
||||
"pricing": "Precios",
|
||||
"compliance": "Cumplimiento"
|
||||
},
|
||||
"auth": {
|
||||
"redirecting_login": "Redirigiendo al inicio de sesion...",
|
||||
|
||||
+2
-1
@@ -46,7 +46,8 @@
|
||||
"agents": "Agents",
|
||||
"flow": "Flux",
|
||||
"analytics": "Analytique",
|
||||
"pricing": "Tarifs"
|
||||
"pricing": "Tarifs",
|
||||
"compliance": "Conformite"
|
||||
},
|
||||
"auth": {
|
||||
"redirecting_login": "Redirection vers la connexion...",
|
||||
|
||||
+2
-1
@@ -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...",
|
||||
|
||||
+202
-6
@@ -53,7 +53,7 @@
|
||||
"description": "CERTifAI administration dashboard",
|
||||
"enabled": true,
|
||||
"publicClient": true,
|
||||
"directAccessGrantsEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"standardFlowEnabled": true,
|
||||
"implicitFlowEnabled": false,
|
||||
"serviceAccountsEnabled": false,
|
||||
@@ -73,7 +73,8 @@
|
||||
"defaultClientScopes": [
|
||||
"openid",
|
||||
"profile",
|
||||
"email"
|
||||
"email",
|
||||
"tenant-context"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"offline_access"
|
||||
@@ -106,7 +107,8 @@
|
||||
"defaultClientScopes": [
|
||||
"openid",
|
||||
"profile",
|
||||
"email"
|
||||
"email",
|
||||
"tenant-context"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"offline_access"
|
||||
@@ -139,7 +141,8 @@
|
||||
"defaultClientScopes": [
|
||||
"openid",
|
||||
"profile",
|
||||
"email"
|
||||
"email",
|
||||
"tenant-context"
|
||||
],
|
||||
"optionalClientScopes": [
|
||||
"offline_access"
|
||||
@@ -269,6 +272,105 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tenant-context",
|
||||
"description": "Breakpilot platform tenant + org claims (M7.1)",
|
||||
"protocol": "openid-connect",
|
||||
"attributes": {
|
||||
"include.in.token.scope": "true",
|
||||
"display.on.consent.screen": "false"
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "tenant_id",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "tenant_id",
|
||||
"claim.name": "tenant_id",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tenant_slug",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "tenant_slug",
|
||||
"claim.name": "tenant_slug",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tenant_status",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "tenant_status",
|
||||
"claim.name": "tenant_status",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "plan",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "plan",
|
||||
"claim.name": "plan",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"jsonType.label": "String"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "org_roles",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "org_roles",
|
||||
"claim.name": "org_roles",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"jsonType.label": "String",
|
||||
"multivalued": "true",
|
||||
"aggregate.attrs": "true"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "products",
|
||||
"protocol": "openid-connect",
|
||||
"protocolMapper": "oidc-usermodel-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "products",
|
||||
"claim.name": "products",
|
||||
"id.token.claim": "true",
|
||||
"access.token.claim": "true",
|
||||
"userinfo.token.claim": "true",
|
||||
"jsonType.label": "String",
|
||||
"multivalued": "true",
|
||||
"aggregate.attrs": "true"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
@@ -289,7 +391,15 @@
|
||||
"realmRoles": [
|
||||
"admin",
|
||||
"user"
|
||||
]
|
||||
],
|
||||
"attributes": {
|
||||
"tenant_id": ["00000000-0000-0000-0000-000000000001"],
|
||||
"tenant_slug": ["acme"],
|
||||
"tenant_status": ["active"],
|
||||
"plan": ["professional"],
|
||||
"org_roles": ["IT_ADMIN", "CXO"],
|
||||
"products": ["compliance", "certifai"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username": "user@certifai.local",
|
||||
@@ -307,7 +417,93 @@
|
||||
],
|
||||
"realmRoles": [
|
||||
"user"
|
||||
]
|
||||
],
|
||||
"attributes": {
|
||||
"tenant_id": ["00000000-0000-0000-0000-000000000001"],
|
||||
"tenant_slug": ["acme"],
|
||||
"tenant_status": ["active"],
|
||||
"plan": ["professional"],
|
||||
"org_roles": ["USER"],
|
||||
"products": ["compliance"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username": "frozen@acme.local",
|
||||
"email": "frozen@acme.local",
|
||||
"firstName": "Frozen",
|
||||
"lastName": "Tenant",
|
||||
"enabled": true,
|
||||
"emailVerified": true,
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "frozen",
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": [
|
||||
"user"
|
||||
],
|
||||
"attributes": {
|
||||
"tenant_id": ["00000000-0000-0000-0000-000000000002"],
|
||||
"tenant_slug": ["frozenco"],
|
||||
"tenant_status": ["frozen"],
|
||||
"plan": ["starter"],
|
||||
"org_roles": ["IT_ADMIN"],
|
||||
"products": ["compliance"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username": "archived@acme.local",
|
||||
"email": "archived@acme.local",
|
||||
"firstName": "Archived",
|
||||
"lastName": "Tenant",
|
||||
"enabled": true,
|
||||
"emailVerified": true,
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "archived",
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": [
|
||||
"user"
|
||||
],
|
||||
"attributes": {
|
||||
"tenant_id": ["00000000-0000-0000-0000-000000000003"],
|
||||
"tenant_slug": ["archiveco"],
|
||||
"tenant_status": ["archived"],
|
||||
"plan": ["starter"],
|
||||
"org_roles": ["IT_ADMIN"],
|
||||
"products": ["compliance"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username": "trial@acme.local",
|
||||
"email": "trial@acme.local",
|
||||
"firstName": "Trial",
|
||||
"lastName": "Tenant",
|
||||
"enabled": true,
|
||||
"emailVerified": true,
|
||||
"credentials": [
|
||||
{
|
||||
"type": "password",
|
||||
"value": "trial",
|
||||
"temporary": false
|
||||
}
|
||||
],
|
||||
"realmRoles": [
|
||||
"user"
|
||||
],
|
||||
"attributes": {
|
||||
"tenant_id": ["00000000-0000-0000-0000-000000000004"],
|
||||
"tenant_slug": ["trialco"],
|
||||
"tenant_status": ["trial"],
|
||||
"plan": ["starter"],
|
||||
"org_roles": ["IT_ADMIN"],
|
||||
"products": ["compliance"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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::<Signal<Locale>>();
|
||||
let locale_val = *locale.read();
|
||||
|
||||
let nav_items: Vec<NavItem> = vec![
|
||||
let mut nav_items: Vec<NavItem> = 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::<Route>();
|
||||
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 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 +47,7 @@ pub async fn check_auth() -> Result<AuthInfo, ServerFnError> {
|
||||
langgraph_url,
|
||||
langflow_url,
|
||||
langfuse_url,
|
||||
compliance_scanner_url,
|
||||
})
|
||||
}
|
||||
None => Ok(AuthInfo::default()),
|
||||
|
||||
@@ -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"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user