From 53a0e4b2ab1d4bd8ef6d57479fc36162e73b3aae Mon Sep 17 00:00:00 2001 From: igor04091968 Date: Sat, 6 Jun 2026 19:10:00 +0300 Subject: [PATCH] fix(portal): clarify unassigned department labels --- adk-rust/crates/detmir-portal/src/main.rs | 14 +++++++++++--- .../crates/detmir-portal/src/static/app.js | 18 ++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/adk-rust/crates/detmir-portal/src/main.rs b/adk-rust/crates/detmir-portal/src/main.rs index 9727ab5..55c2e10 100644 --- a/adk-rust/crates/detmir-portal/src/main.rs +++ b/adk-rust/crates/detmir-portal/src/main.rs @@ -31,7 +31,8 @@ const API_CONTRACT_OPENAPI: &str = include_str!("contracts/openapi.json"); const API_CONTRACT_TYPESCRIPT: &str = include_str!("contracts/typescript.d.ts"); const UEBA_BASELINE_MIN_SAMPLES: usize = 3; const SNAPSHOT_CACHE_TTL: Duration = Duration::from_secs(120); -const DEFAULT_DEPARTMENT_LABEL: &str = "Без подразделения"; +const DEFAULT_DEPARTMENT_LABEL: &str = "Не привязано к подразделению"; +const LEGACY_UNASSIGNED_DEPARTMENT_LABEL: &str = "Без подразделения"; #[cfg(unix)] const SIGKILL: i32 = 9; @@ -2165,7 +2166,10 @@ fn display_name_opt(value: Option<&str>, fallback: &str) -> String { return fallback.to_string(); }; let value = value.trim(); - if value.is_empty() || has_broken_display_chars(value) { + if value.is_empty() + || value == LEGACY_UNASSIGNED_DEPARTMENT_LABEL + || has_broken_display_chars(value) + { fallback.to_string() } else { value.to_string() @@ -2670,7 +2674,7 @@ FORMAT JSONEachRow let top_sql = format!( r#" WITH now() - INTERVAL 24 HOUR AS since -SELECT if(empty(infobase), 'Без подразделения', infobase) AS department, toUInt64(count()) AS events +SELECT if(empty(infobase), 'Не привязано к подразделению', infobase) AS department, toUInt64(count()) AS events FROM {database}.entity_timeline WHERE ts >= since GROUP BY department @@ -10336,6 +10340,10 @@ mod tests { fn display_department_name_replaces_corrupt_runtime_labels() { assert_eq!(display_department_name(Some("Бухгалтерия")), "Бухгалтерия"); assert_eq!(display_department_name(Some("")), DEFAULT_DEPARTMENT_LABEL); + assert_eq!( + display_department_name(Some("Без подразделения")), + DEFAULT_DEPARTMENT_LABEL + ); assert_eq!( display_department_name(Some("\u{FFFD}\u{FFFD}\u{FFFD}\u{FFFD}")), DEFAULT_DEPARTMENT_LABEL diff --git a/adk-rust/crates/detmir-portal/src/static/app.js b/adk-rust/crates/detmir-portal/src/static/app.js index 7830fd0..d441be9 100644 --- a/adk-rust/crates/detmir-portal/src/static/app.js +++ b/adk-rust/crates/detmir-portal/src/static/app.js @@ -17,6 +17,8 @@ const state = { }, }; +const DEFAULT_DEPARTMENT_LABEL = "Не привязано к подразделению"; + const VIEW_MODES = { executive: { label: "Руководитель", @@ -802,7 +804,7 @@ function departmentRows(report) { const status = item.status || "INFO"; const responsible = item.responsible || departmentResponsible(owners, index); return { - label: item.label || "Без подразделения", + label: item.label || DEFAULT_DEPARTMENT_LABEL, activity, activityText: Number.isFinite(activity) ? `${Math.round(activity)}%` : firstPercent(item.value), deviation: item.deviation || departmentDeviation(item), @@ -1392,7 +1394,7 @@ function renderInvestigationPacks(candidates) {
${rows.length ? rows.map(item => `
- ${ui(item.department || "Без подразделения")} + ${ui(item.department || DEFAULT_DEPARTMENT_LABEL)} ${ui(item.reason || "требуется проверка")} · ${ui(item.hostname || "-")} Скачать
@@ -2364,7 +2366,7 @@ function renderSecurityEventsSummary(summary, options = {}) { ${top.length ? top.map(item => ` - ${ui(item.department || "Без подразделения")} + ${ui(item.department || DEFAULT_DEPARTMENT_LABEL)} ${ui(item.events ?? 0)} `).join("") : ` @@ -2376,7 +2378,7 @@ function renderSecurityEventsSummary(summary, options = {}) {
-

Последнее событие: ${ui(s.last_event_utc || "нет данных")} · запрос ${ui(s.query_ms ?? 0)} ms.

+

${ui(DEFAULT_DEPARTMENT_LABEL)} — событие есть в источнике, но в нем не передана связь с оргструктурой. Последнее событие: ${ui(s.last_event_utc || "нет данных")} · запрос ${ui(s.query_ms ?? 0)} ms.

`} `; @@ -2408,7 +2410,7 @@ function renderBusinessRisk(items) { ${rows.length ? rows.map(item => ` - ${ui(item.department || "Без подразделения")} + ${ui(item.department || DEFAULT_DEPARTMENT_LABEL)} ${ui(item.risk_level || "UNKNOWN")} ${ui(businessRiskReasons(item))} ${ui(item.security_events_24h ?? 0)} @@ -2459,7 +2461,7 @@ function renderRiskHeatmap(items) { ${rows.length ? rows.map(item => ` - ${ui(item.department || "Без подразделения")} + ${ui(item.department || DEFAULT_DEPARTMENT_LABEL)} ${ui(riskPercentText(item.trust_kpi_score))} ${ui(riskPercentText(item.activity_score))} ${ui(riskPercentText(item.agent_coverage_pct))} @@ -2546,7 +2548,7 @@ function renderSecurityCorrelation(items) { ${rows.length ? rows.map(item => ` - ${ui(item.department || "Без подразделения")} + ${ui(item.department || DEFAULT_DEPARTMENT_LABEL)} ${ui(riskPercentText(item.trust_kpi_score))} ${ui(riskPercentText(item.activity_score))} ${ui(item.business_risk_level || "UNKNOWN")} @@ -2625,7 +2627,7 @@ function renderBusinessRiskTimeline(history, summary) { ${rows.length ? rows.map(item => ` ${ui(item.date || "-")} - ${ui(item.department || "Без подразделения")} + ${ui(item.department || DEFAULT_DEPARTMENT_LABEL)} ${ui(item.risk_level || "UNKNOWN")} ${ui(Array.isArray(item.reasons) && item.reasons.length ? item.reasons.join("; ") : "нет существенных причин")}