fix: dashboard and hosts count SSH and RDP daily reports

analytics-a4: daily_reports_24h uses report.daily.*; sac-deploy enables timers
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PTah
2026-05-29 16:31:09 +10:00
parent 415d863b3b
commit 42f31b3bb5
7 changed files with 50 additions and 7 deletions
+2 -2
View File
@@ -10,7 +10,7 @@ from app.config import get_settings
from app.database import get_db
from app.models import Event, Host, Problem
from app.schemas.list_models import EventSummary
from app.services.host_health import DAILY_REPORT_SSH, HEARTBEAT_TYPE, count_stale_hosts
from app.services.host_health import DAILY_REPORT_TYPES, HEARTBEAT_TYPE, count_stale_hosts
router = APIRouter(prefix="/dashboards", tags=["dashboards"])
@@ -89,7 +89,7 @@ def dashboard_summary(
daily_reports_24h = db.scalar(
select(func.count())
.select_from(Event)
.where(Event.received_at >= since, Event.type == DAILY_REPORT_SSH)
.where(Event.received_at >= since, Event.type.in_(DAILY_REPORT_TYPES))
) or 0
problems_open = (
db.scalar(select(func.count()).select_from(Problem).where(Problem.status == "open")) or 0
+2 -2
View File
@@ -8,9 +8,9 @@ from app.database import get_db
from app.models import Event, Host
from app.schemas.list_models import HostListResponse, HostSummary
from app.services.host_health import (
DAILY_REPORT_SSH,
HEARTBEAT_TYPE,
agent_status,
max_daily_report_time_by_host,
max_event_time_by_host,
)
@@ -47,7 +47,7 @@ def list_hosts(
settings = get_settings()
hb_map = max_event_time_by_host(db, HEARTBEAT_TYPE)
report_map = max_event_time_by_host(db, DAILY_REPORT_SSH)
report_map = max_daily_report_time_by_host(db)
items: list[HostSummary] = []
for host in rows:
+2 -2
View File
@@ -11,7 +11,7 @@ from app.auth.jwt_auth import verify_access_token
from app.database import SessionLocal
from app.models import Event, Problem
from app.config import get_settings
from app.services.host_health import DAILY_REPORT_SSH, HEARTBEAT_TYPE, count_stale_hosts
from app.services.host_health import DAILY_REPORT_TYPES, HEARTBEAT_TYPE, count_stale_hosts
from app.version import APP_VERSION
router = APIRouter(prefix="/stream", tags=["stream"])
@@ -43,7 +43,7 @@ def _dashboard_snapshot(db: Session) -> dict:
"daily_reports_24h": db.scalar(
select(func.count())
.select_from(Event)
.where(Event.received_at >= since, Event.type == DAILY_REPORT_SSH)
.where(Event.received_at >= since, Event.type.in_(DAILY_REPORT_TYPES))
)
or 0,
"problems_open": problems_open,
+11
View File
@@ -9,6 +9,8 @@ from app.models import Event, Host
HEARTBEAT_TYPE = "agent.heartbeat"
DAILY_REPORT_SSH = "report.daily.ssh"
DAILY_REPORT_RDP = "report.daily.rdp"
DAILY_REPORT_TYPES = (DAILY_REPORT_SSH, DAILY_REPORT_RDP)
def max_event_time_by_host(db: Session, event_type: str) -> dict[int, datetime]:
@@ -20,6 +22,15 @@ def max_event_time_by_host(db: Session, event_type: str) -> dict[int, datetime]:
return {int(host_id): ts for host_id, ts in rows if ts is not None}
def max_daily_report_time_by_host(db: Session) -> dict[int, datetime]:
rows = db.execute(
select(Event.host_id, func.max(Event.received_at))
.where(Event.type.in_(DAILY_REPORT_TYPES))
.group_by(Event.host_id)
).all()
return {int(host_id): ts for host_id, ts in rows if ts is not None}
def agent_status(
last_heartbeat_at: datetime | None,
*,
+14
View File
@@ -92,6 +92,20 @@ def test_dashboard_summary_top_and_problems_24h(client, db_session, jwt_headers)
assert types["agent.heartbeat"] == 2
def test_dashboard_daily_reports_24h_includes_ssh_and_rdp(client, db_session, jwt_headers):
h_ssh = _host(db_session, "ssh-pilot")
h_rdp = _host(db_session, "rdp-pilot")
h_rdp.product = "rdp-login-monitor"
now = datetime.now(timezone.utc)
_event(db_session, h_ssh, "report.daily.ssh", received_at=now)
_event(db_session, h_rdp, "report.daily.rdp", received_at=now)
db_session.commit()
r = client.get("/api/v1/dashboards/summary", headers=jwt_headers)
assert r.status_code == 200
assert r.json()["daily_reports_24h"] == 2
def test_dashboard_recent_events_ordered_by_received_at(client, db_session, jwt_headers):
h = _host(db_session, "live-host")
now = datetime.now(timezone.utc)
+18
View File
@@ -122,4 +122,22 @@ else
die "${SERVICE_NAME} запущен, но /health не ответил — journalctl -u ${SERVICE_NAME} -n 80 --no-pager"
fi
for aux in sac-retention sac-daily-report; do
for suffix in service timer; do
src="${APP_ROOT}/deploy/systemd/${aux}.${suffix}"
dst="/etc/systemd/system/${aux}.${suffix}"
if [ -f "${src}" ]; then
if [ ! -f "${dst}" ] || ! cmp -s "${src}" "${dst}"; then
log "Обновление ${dst}"
cp "${src}" "${dst}"
systemctl daemon-reload
fi
fi
done
if [ -f "/etc/systemd/system/${aux}.timer" ]; then
systemctl enable "${aux}.timer" 2>/dev/null || true
systemctl start "${aux}.timer" 2>/dev/null || true
fi
done
log "Готово."
+1 -1
View File
@@ -9,7 +9,7 @@
| analytics-a1 | `GET /api/v1/analytics/series` (`bucket=hour\|day\|week\|month`, `from`/`to`) | JSON series + тесты |
| analytics-a2 | `group_by=severity\|type\|product`, `exclude_types=agent.heartbeat` | heartbeat не в общем счётчике |
| analytics-a3 | Presets 24h / 7d / 30d / 90d | документация API |
| analytics-a4 | Dashboard: `daily_reports_24h` учитывает `report.daily.rdp` | test_dashboard |
| analytics-a4 | Dashboard: `daily_reports_24h` учитывает `report.daily.rdp` | test_dashboard |
| analytics-a5 | Индексы под агрегации | EXPLAIN без full scan |
## Фаза B — UI