diff --git a/backend/app/services/host_remote_actions.py b/backend/app/services/host_remote_actions.py index f27149c..397ecff 100644 --- a/backend/app/services/host_remote_actions.py +++ b/backend/app/services/host_remote_actions.py @@ -126,7 +126,10 @@ def _poll_ssh_update_log_loop( session = SessionLocal() try: row = session.get(Host, host_id) - if row is None or row.agent_update_state != "running": + if row is None or (row.agent_update_state or "").strip().lower() != "running": + continue + payload = dict(row.remote_action or {}) + if (payload.get("status") or "").strip().lower() != "running": continue cfg = get_effective_linux_admin_config(session) if not cfg.configured: @@ -148,7 +151,12 @@ def _poll_ssh_update_log_loop( continue if tail: break + session.refresh(row) + if (row.agent_update_state or "").strip().lower() != "running": + continue payload = dict(row.remote_action or {}) + if (payload.get("status") or "").strip().lower() != "running": + continue if tail: payload["output"] = tail payload["message"] = "Выполняется обновление… (лог с хоста)" @@ -339,8 +347,16 @@ def get_remote_action_status(host: Host) -> dict[str, Any]: payload = dict(host.remote_action or {}) if not payload: return {"active": False, "host_id": host.id} - status = payload.get("status") or host.agent_update_state or "unknown" - active = status == "running" or host.agent_update_state == "running" + agent_state = (host.agent_update_state or "").strip().lower() + if agent_state == "running": + active = True + status = payload.get("status") or "running" + elif agent_state in ("success", "failed"): + active = False + status = payload.get("status") or agent_state + else: + status = payload.get("status") or host.agent_update_state or "unknown" + active = status == "running" return { "active": active, "host_id": host.id, diff --git a/backend/app/version.py b/backend/app/version.py index 41ec9ee..ece43fc 100644 --- a/backend/app/version.py +++ b/backend/app/version.py @@ -1,5 +1,5 @@ """Единый источник версии SAC (API, health, логи, OpenAPI).""" APP_NAME = "Security Alert Center" -APP_VERSION = "0.5.3" +APP_VERSION = "0.5.4" APP_VERSION_LABEL = f"{APP_NAME} v.{APP_VERSION}" diff --git a/backend/tests/test_agent_update.py b/backend/tests/test_agent_update.py index 249ff68..b14e844 100644 --- a/backend/tests/test_agent_update.py +++ b/backend/tests/test_agent_update.py @@ -247,6 +247,30 @@ def test_clear_stale_running_remote_actions(db_session): assert host.remote_action["ok"] is False +def test_get_remote_action_status_ignores_stale_running_payload(db_session): + from app.services.host_remote_actions import get_remote_action_status + + host = Host( + hostname="done-linux", + os_family="linux", + product="ssh-monitor", + agent_update_state="success", + remote_action={ + "status": "running", + "message": "Выполняется обновление… (лог с хоста)", + "output": "=== Script update completed successfully ===", + "ok": True, + "finished_at": "2026-07-08T02:21:11+00:00", + }, + ) + db_session.add(host) + db_session.commit() + + status = get_remote_action_status(host) + assert status["active"] is False + assert status["agent_update_state"] == "success" + + def test_cancel_host_remote_job_api(jwt_headers, client, db_session): host = Host( hostname="cancel-me",