feat(frontend): git agent versions plate on Hosts list (0.20.32)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-06-22 15:17:47 +10:00
parent 54c1d96933
commit 5e26834e16
7 changed files with 97 additions and 9 deletions
+7 -1
View File
@@ -27,7 +27,11 @@ from app.services.host_remote_actions import (
run_ssh_monitor_update_action, run_ssh_monitor_update_action,
start_host_remote_action, start_host_remote_action,
) )
from app.services.agent_update_settings import get_effective_agent_update_config from app.services.agent_update_settings import (
PRODUCT_RDP,
PRODUCT_SSH,
get_effective_agent_update_config,
)
from app.services.agent_host_config import get_host_agent_settings, update_host_agent_config from app.services.agent_host_config import get_host_agent_settings, update_host_agent_config
from app.services.host_sessions import ( from app.services.host_sessions import (
HostSessionRow, HostSessionRow,
@@ -165,6 +169,8 @@ def list_hosts(
page_size=page_size, page_size=page_size,
latest_agent_versions=latest_map, latest_agent_versions=latest_map,
reference_agent_versions=reference_map, reference_agent_versions=reference_map,
git_latest_rdp_version=git_release.versions.get(PRODUCT_RDP) or None,
git_latest_ssh_version=git_release.versions.get(PRODUCT_SSH) or None,
) )
+8
View File
@@ -53,6 +53,14 @@ class HostListResponse(BaseModel):
default_factory=dict, default_factory=dict,
description="Эталон для «устарела»: max(git latest, min, max в fleet)", description="Эталон для «устарела»: max(git latest, min, max в fleet)",
) )
git_latest_rdp_version: str | None = Field(
None,
description="Последняя версия rdp-login-monitor из git (version.txt / main)",
)
git_latest_ssh_version: str | None = Field(
None,
description="Последняя версия ssh-monitor из git (version.txt / main)",
)
class EventSummary(BaseModel): class EventSummary(BaseModel):
+1 -1
View File
@@ -1,5 +1,5 @@
"""Единый источник версии SAC (API, health, логи, OpenAPI).""" """Единый источник версии SAC (API, health, логи, OpenAPI)."""
APP_NAME = "Security Alert Center" APP_NAME = "Security Alert Center"
APP_VERSION = "0.20.31" APP_VERSION = "0.20.32"
APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}" APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}"
+2 -2
View File
@@ -4,6 +4,6 @@ from app.version import APP_NAME, APP_VERSION, APP_VERSION_LABEL
def test_version_constants(): def test_version_constants():
assert APP_VERSION == "0.20.31" assert APP_VERSION == "0.20.32"
assert APP_NAME == "Security Alert Center" assert APP_NAME == "Security Alert Center"
assert APP_VERSION_LABEL == "Security Alert Center v.0.20.31" assert APP_VERSION_LABEL == "Security Alert Center v.0.20.32"
+3
View File
@@ -286,6 +286,9 @@ export interface HostListResponse {
latest_agent_versions?: Record<string, string>; latest_agent_versions?: Record<string, string>;
/** Эталон для «устарела»: max(git latest, min, max в fleet) */ /** Эталон для «устарела»: max(git latest, min, max в fleet) */
reference_agent_versions?: Record<string, string>; reference_agent_versions?: Record<string, string>;
/** Последние версии из git (version.txt на main) */
git_latest_rdp_version?: string | null;
git_latest_ssh_version?: string | null;
} }
export interface ProblemSummary { export interface ProblemSummary {
+1 -1
View File
@@ -1,4 +1,4 @@
/** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */ /** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */
export const APP_NAME = "Security Alert Center"; export const APP_NAME = "Security Alert Center";
export const APP_VERSION = "0.20.31"; export const APP_VERSION = "0.20.32";
export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`; export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`;
+75 -4
View File
@@ -19,10 +19,24 @@
<p v-if="error" class="error">{{ error }}</p> <p v-if="error" class="error">{{ error }}</p>
<p v-else-if="loading">Загрузка</p> <p v-else-if="loading">Загрузка</p>
<template v-else> <template v-else>
<p> <div class="hosts-list-meta">
Всего: {{ data?.total ?? 0 }} <p class="hosts-list-total">
<span v-if="live" class="live-badge">live</span> Всего: {{ data?.total ?? 0 }}
</p> <span v-if="live" class="live-badge">live</span>
</p>
<aside
class="agent-releases-plate"
title="Версии из git-репозиториев агентов (version.txt, ветка main)"
>
<span class="agent-releases-plate__label">Последние версии агентов:</span>
<span class="agent-releases-plate__item">
Win: <code>{{ gitWinVersion }}</code>
</span>
<span class="agent-releases-plate__item">
SSH: <code>{{ gitSshVersion }}</code>
</span>
</aside>
</div>
<table class="hosts-table"> <table class="hosts-table">
<thead> <thead>
<tr> <tr>
@@ -242,6 +256,13 @@ function setSort(key: SortKey) {
saveHostsSortPrefs(sortBy.value, sortDir.value); saveHostsSortPrefs(sortBy.value, sortDir.value);
} }
const gitWinVersion = computed(
() => data.value?.git_latest_rdp_version?.trim() || "—",
);
const gitSshVersion = computed(
() => data.value?.git_latest_ssh_version?.trim() || "—",
);
function sortMark(key: SortKey) { function sortMark(key: SortKey) {
if (sortBy.value !== key) return ""; if (sortBy.value !== key) return "";
return sortDir.value === "asc" ? "↑" : "↓"; return sortDir.value === "asc" ? "↑" : "↓";
@@ -412,3 +433,53 @@ watch(
}, },
); );
</script> </script>
<style scoped>
.hosts-list-meta {
display: flex;
flex-wrap: wrap;
align-items: stretch;
gap: 0.5rem 0.75rem;
margin: 0.35rem 0 1rem;
}
.hosts-list-total {
display: flex;
align-items: center;
gap: 0.5rem;
margin: 0;
line-height: 1.5;
}
.agent-releases-plate {
display: inline-flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.35rem 0.85rem;
margin: -0.15rem 0 0;
padding: 0.45rem 1rem 0.5rem 1.1rem;
border-radius: 0 0.75rem 0.75rem 0.35rem;
border: 1px solid #2d3a4d;
border-left: 3px solid #58a6ff;
background: linear-gradient(135deg, #1a2332 0%, #151d28 100%);
box-shadow:
0 2px 8px rgba(0, 0, 0, 0.35),
0 1px 0 rgba(88, 166, 255, 0.12) inset;
font-size: 0.9rem;
color: #9aa4b2;
transform: translateY(-1px);
}
.agent-releases-plate__label {
color: #c9d1d9;
font-weight: 500;
}
.agent-releases-plate__item code {
font-size: 0.88em;
color: #79c0ff;
background: rgba(88, 166, 255, 0.08);
padding: 0.1rem 0.35rem;
border-radius: 0.25rem;
}
</style>