fix(portal): polish security events fallback states
This commit is contained in:
@@ -2529,16 +2529,17 @@ fn security_events_summary_status(summary: &SecurityEventsSummary) -> String {
|
||||
|
||||
fn security_events_summary_text(summary: &SecurityEventsSummary) -> String {
|
||||
if summary.backend == "disabled" || summary.status == "disabled" {
|
||||
return "агрегированные события безопасности отключены".to_string();
|
||||
return "Источник событий безопасности отключён. Используется локальный режим без ClickHouse."
|
||||
.to_string();
|
||||
}
|
||||
if summary.fallback_used {
|
||||
return format!(
|
||||
"ClickHouse недоступен, используется локальный режим; причина: {}",
|
||||
"События безопасности временно недоступны. Используется локальный режим без ClickHouse; причина: {}",
|
||||
summary.error.as_deref().unwrap_or("неизвестная ошибка")
|
||||
);
|
||||
}
|
||||
format!(
|
||||
"событий={}, неуспешных входов={}, подозрительных входов={}, RDP={}, изменения учетных записей={}, ошибки агентов={}",
|
||||
"События безопасности доступны: событий={}, неуспешных входов={}, подозрительных входов={}, RDP={}, изменения учетных записей={}, ошибки агентов={}",
|
||||
summary.events_24h,
|
||||
summary.failed_logins_24h,
|
||||
summary.suspicious_logins_24h,
|
||||
@@ -10135,6 +10136,46 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn security_events_summary_texts_are_pilot_safe() {
|
||||
let disabled = SecurityEventsSummary::disabled();
|
||||
assert!(
|
||||
security_events_summary_text(&disabled)
|
||||
.contains("Источник событий безопасности отключён")
|
||||
);
|
||||
assert!(
|
||||
security_events_summary_text(&disabled)
|
||||
.contains("Используется локальный режим без ClickHouse")
|
||||
);
|
||||
|
||||
let fallback = SecurityEventsSummary::fallback("network unavailable", 1);
|
||||
assert!(
|
||||
security_events_summary_text(&fallback)
|
||||
.contains("События безопасности временно недоступны")
|
||||
);
|
||||
assert!(
|
||||
security_events_summary_text(&fallback)
|
||||
.contains("Используется локальный режим без ClickHouse")
|
||||
);
|
||||
|
||||
let available = SecurityEventsSummary {
|
||||
status: "ok".to_string(),
|
||||
backend: "clickhouse".to_string(),
|
||||
events_24h: 3,
|
||||
failed_logins_24h: 1,
|
||||
suspicious_logins_24h: 0,
|
||||
rdp_sessions_24h: 2,
|
||||
account_changes_24h: 0,
|
||||
agent_errors_24h: 0,
|
||||
top_departments: Vec::new(),
|
||||
last_event_utc: Some("2026-06-04T10:00:00Z".to_string()),
|
||||
query_ms: 7,
|
||||
fallback_used: false,
|
||||
error: None,
|
||||
};
|
||||
assert!(security_events_summary_text(&available).contains("События безопасности доступны"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reports_include_commercial_kpis_and_disclaimer() {
|
||||
let snapshot = Snapshot {
|
||||
|
||||
@@ -2156,18 +2156,42 @@ function renderSecurityEventsSummary(summary, options = {}) {
|
||||
const disabled = backend === "disabled" || s.status === "disabled" || !summary;
|
||||
const fallback = Boolean(s.fallback_used);
|
||||
const status = disabled ? "UNKNOWN" : fallback ? "WARN" : Number(s.events_24h || 0) > 0 ? "WARN" : "OK";
|
||||
const stateText = disabled
|
||||
? "Источник событий безопасности отключён"
|
||||
: fallback
|
||||
? "События безопасности временно недоступны"
|
||||
: "События безопасности доступны";
|
||||
const localModeText = disabled ? "Используется локальный режим без ClickHouse" : "";
|
||||
const title = options.compact
|
||||
? "События безопасности"
|
||||
: "События безопасности за 24 часа";
|
||||
const subtitle = disabled
|
||||
? "Агрегированный источник событий безопасности отключён."
|
||||
? `${stateText}. ${localModeText}.`
|
||||
: fallback
|
||||
? "ClickHouse недоступен, портал работает в резервном режиме без событий безопасности."
|
||||
? "Портал работает в резервном режиме без событий безопасности."
|
||||
: "Агрегированная сводка без сырых журналов и без автоматического создания инцидентов.";
|
||||
const top = Array.isArray(s.top_departments) ? s.top_departments.slice(0, 5) : [];
|
||||
const warning = fallback
|
||||
? `<div class="quality-warning">События безопасности временно недоступны. Проверьте ClickHouse и переменные SECURITY_EVENTS_BACKEND/CLICKHOUSE_*.</div>`
|
||||
: "";
|
||||
if (options.compact) {
|
||||
return `
|
||||
<section class="card security-events-card">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h3 ${tooltip("Краткая управленческая сводка доступности событий безопасности за последние 24 часа.")}>${ui(title)}</h3>
|
||||
<p class="muted">${ui(stateText)}</p>
|
||||
</div>
|
||||
<span class="badge ${statusClass(status)}">${ui(status)}</span>
|
||||
</div>
|
||||
<div class="quality-grid">
|
||||
<div><span class="muted">Состояние</span><strong>${ui(stateText)}</strong></div>
|
||||
<div><span class="muted">Событий за 24 часа</span><strong>${ui(s.events_24h ?? 0)}</strong></div>
|
||||
<div><span class="muted">Режим</span><strong>${ui(disabled ? localModeText : "Агрегированная сводка")}</strong></div>
|
||||
</div>
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<section class="card security-events-card">
|
||||
<div class="section-head">
|
||||
@@ -2178,6 +2202,7 @@ function renderSecurityEventsSummary(summary, options = {}) {
|
||||
<span class="badge ${statusClass(status)}">${ui(status)}</span>
|
||||
</div>
|
||||
<div class="quality-grid">
|
||||
<div><span class="muted">Состояние</span><strong>${ui(stateText)}</strong></div>
|
||||
<div><span class="muted">Источник</span><strong>${ui(backend)}</strong></div>
|
||||
<div><span class="muted">Событий</span><strong>${ui(s.events_24h ?? 0)}</strong></div>
|
||||
<div><span class="muted">Неуспешные входы</span><strong>${ui(s.failed_logins_24h ?? 0)}</strong></div>
|
||||
|
||||
Reference in New Issue
Block a user