fix: support nested remnawave activity fields
This commit is contained in:
@@ -110,15 +110,24 @@ def _panel_nested_dicts(panel_user: Dict[str, Any], keys: Tuple[str, ...]) -> Li
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _panel_user_connection_containers(panel_user: Dict[str, Any]) -> List[Dict[str, Any]]:
|
||||||
|
traffic_containers = _panel_nested_dicts(panel_user, _PANEL_TRAFFIC_OBJECT_KEYS)
|
||||||
|
marker_containers = _panel_nested_dicts(
|
||||||
|
panel_user,
|
||||||
|
_PANEL_CONNECTION_MARKER_OBJECT_KEYS,
|
||||||
|
)
|
||||||
|
for traffic_container in traffic_containers:
|
||||||
|
marker_containers.extend(
|
||||||
|
_panel_nested_dicts(traffic_container, _PANEL_CONNECTION_MARKER_OBJECT_KEYS)
|
||||||
|
)
|
||||||
|
return [panel_user, *traffic_containers, *marker_containers]
|
||||||
|
|
||||||
|
|
||||||
def _panel_user_last_connected_at(panel_user_data: Any) -> Optional[str]:
|
def _panel_user_last_connected_at(panel_user_data: Any) -> Optional[str]:
|
||||||
panel_user = _panel_user_payload(panel_user_data)
|
panel_user = _panel_user_payload(panel_user_data)
|
||||||
if not panel_user:
|
if not panel_user:
|
||||||
return None
|
return None
|
||||||
containers = [
|
for container in _panel_user_connection_containers(panel_user):
|
||||||
panel_user,
|
|
||||||
*_panel_nested_dicts(panel_user, _PANEL_CONNECTION_MARKER_OBJECT_KEYS),
|
|
||||||
]
|
|
||||||
for container in containers:
|
|
||||||
for key in _PANEL_LAST_CONNECTED_KEYS:
|
for key in _PANEL_LAST_CONNECTED_KEYS:
|
||||||
connected_at = _coerce_panel_datetime(container.get(key))
|
connected_at = _coerce_panel_datetime(container.get(key))
|
||||||
if connected_at:
|
if connected_at:
|
||||||
@@ -137,30 +146,31 @@ def _panel_user_positive_traffic_bytes(panel_user: Dict[str, Any]) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def _panel_user_has_connection_marker(panel_user: Dict[str, Any]) -> bool:
|
def _panel_user_has_connection_marker(panel_user: Dict[str, Any]) -> bool:
|
||||||
|
for container in _panel_user_connection_containers(panel_user):
|
||||||
for key in _PANEL_CONNECTION_MARKER_KEYS:
|
for key in _PANEL_CONNECTION_MARKER_KEYS:
|
||||||
if key in panel_user:
|
if key in container:
|
||||||
return True
|
return True
|
||||||
|
for container in [panel_user, *_panel_nested_dicts(panel_user, _PANEL_TRAFFIC_OBJECT_KEYS)]:
|
||||||
for key in _PANEL_CONNECTION_MARKER_OBJECT_KEYS:
|
for key in _PANEL_CONNECTION_MARKER_OBJECT_KEYS:
|
||||||
if key in panel_user:
|
if key in container:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def _panel_user_has_connected_marker_value(panel_user: Dict[str, Any]) -> bool:
|
def _panel_user_has_connected_marker_value(panel_user: Dict[str, Any]) -> bool:
|
||||||
containers = [
|
for container in _panel_user_connection_containers(panel_user):
|
||||||
panel_user,
|
|
||||||
*_panel_nested_dicts(panel_user, _PANEL_CONNECTION_MARKER_OBJECT_KEYS),
|
|
||||||
]
|
|
||||||
for container in containers:
|
|
||||||
for key in (*_PANEL_LAST_CONNECTED_KEYS, "firstConnectedAt", "first_connected_at"):
|
for key in (*_PANEL_LAST_CONNECTED_KEYS, "firstConnectedAt", "first_connected_at"):
|
||||||
if _coerce_panel_datetime(container.get(key)):
|
if _coerce_panel_datetime(container.get(key)):
|
||||||
return True
|
return True
|
||||||
for key in ("lastConnectedNodeUuid", "last_connected_node_uuid"):
|
for key in ("lastConnectedNodeUuid", "last_connected_node_uuid"):
|
||||||
if str(container.get(key) or "").strip():
|
if str(container.get(key) or "").strip():
|
||||||
return True
|
return True
|
||||||
|
for container in [panel_user, *_panel_nested_dicts(panel_user, _PANEL_TRAFFIC_OBJECT_KEYS)]:
|
||||||
for key in _PANEL_CONNECTION_MARKER_OBJECT_KEYS:
|
for key in _PANEL_CONNECTION_MARKER_OBJECT_KEYS:
|
||||||
marker = panel_user.get(key)
|
marker = container.get(key)
|
||||||
if isinstance(marker, dict) and any(str(value or "").strip() for value in marker.values()):
|
if isinstance(marker, dict) and any(
|
||||||
|
str(value or "").strip() for value in marker.values()
|
||||||
|
):
|
||||||
return True
|
return True
|
||||||
if marker and not isinstance(marker, dict):
|
if marker and not isinstance(marker, dict):
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class AdminPanelActivityTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
def test_panel_activity_detects_connected_and_never_connected_users(self):
|
def test_panel_activity_detects_connected_and_never_connected_users(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
common_module._panel_user_connection_activity(
|
common_module._panel_user_connection_activity(
|
||||||
{"onlineAt": "2026-06-05T12:00:00Z"}
|
{"userTraffic": {"onlineAt": "2026-06-05T12:00:00Z"}}
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
"status": "connected",
|
"status": "connected",
|
||||||
@@ -82,10 +82,12 @@ class AdminPanelActivityTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
common_module._panel_user_connection_activity(
|
common_module._panel_user_connection_activity(
|
||||||
{
|
{
|
||||||
|
"userTraffic": {
|
||||||
"onlineAt": None,
|
"onlineAt": None,
|
||||||
"firstConnectedAt": None,
|
"firstConnectedAt": None,
|
||||||
"lastConnectedNode": None,
|
"lastConnectedNodeUuid": None,
|
||||||
"userTraffic": {"lifetimeUsedTrafficBytes": 0},
|
"lifetimeUsedTrafficBytes": 0,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
{"status": "never", "last_connected_at": None},
|
{"status": "never", "last_connected_at": None},
|
||||||
@@ -115,15 +117,19 @@ class AdminPanelActivityTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
async def get_user_by_uuid(panel_uuid):
|
async def get_user_by_uuid(panel_uuid):
|
||||||
return {
|
return {
|
||||||
"never-panel": {
|
"never-panel": {
|
||||||
|
"userTraffic": {
|
||||||
"onlineAt": None,
|
"onlineAt": None,
|
||||||
"firstConnectedAt": None,
|
"firstConnectedAt": None,
|
||||||
"lastConnectedNode": None,
|
"lastConnectedNodeUuid": None,
|
||||||
},
|
},
|
||||||
"connected-panel": {"onlineAt": "2026-06-05T12:00:00Z"},
|
},
|
||||||
|
"connected-panel": {"userTraffic": {"onlineAt": "2026-06-05T12:00:00Z"}},
|
||||||
"also-never-panel": {
|
"also-never-panel": {
|
||||||
|
"userTraffic": {
|
||||||
"onlineAt": None,
|
"onlineAt": None,
|
||||||
"firstConnectedAt": None,
|
"firstConnectedAt": None,
|
||||||
"lastConnectedNode": None,
|
"lastConnectedNodeUuid": None,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"also-connected-panel": {
|
"also-connected-panel": {
|
||||||
"userTraffic": {"lifetimeUsedTrafficBytes": 1},
|
"userTraffic": {"lifetimeUsedTrafficBytes": 1},
|
||||||
@@ -172,7 +178,7 @@ class AdminPanelActivityTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
get_user_by_uuid=AsyncMock(
|
get_user_by_uuid=AsyncMock(
|
||||||
return_value={
|
return_value={
|
||||||
"subscriptionUrl": "https://panel.example/sub/short",
|
"subscriptionUrl": "https://panel.example/sub/short",
|
||||||
"onlineAt": "2026-06-05T12:00:00Z",
|
"userTraffic": {"onlineAt": "2026-06-05T12:00:00Z"},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user