Skip to content

Commit a5aa767

Browse files
committed
feat(node): get user all ips with single request
1 parent 732996a commit a5aa767

File tree

3 files changed

+63
-13
lines changed

3 files changed

+63
-13
lines changed

app/models/node.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,3 +173,15 @@ class NodeNotification(BaseModel):
173173
message: str | None = None
174174

175175
model_config = ConfigDict(from_attributes=True)
176+
177+
178+
class UserIPList(BaseModel):
179+
"""User IP list - mapping of IP addresses to connection counts"""
180+
181+
ips: dict[str, int] # {ip_address: connection_count}
182+
183+
184+
class UserIPListAll(BaseModel):
185+
"""User IP lists for all nodes"""
186+
187+
nodes: dict[int, UserIPList | None] # {node_id: UserIPList | None}

app/operation/node.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from app.db.crud.user import get_user
2424
from app.db.models import Node, NodeStatus
2525
from app.models.admin import AdminDetails
26-
from app.models.node import NodeCreate, NodeModify, NodeNotification, NodeResponse, UsageTable
26+
from app.models.node import NodeCreate, NodeModify, NodeNotification, NodeResponse, UsageTable, UserIPList, UserIPListAll
2727
from app.models.stats import NodeRealtimeStats, NodeStatsList, NodeUsageStatsList, Period
2828
from app.node import core_users, node_manager
2929
from app.operation import BaseOperation
@@ -416,25 +416,55 @@ async def get_user_online_stats_by_node(self, db: AsyncSession, node_id: Node, u
416416

417417
async def get_user_ip_list_by_node(
418418
self, db: AsyncSession, node_id: Node, username: str
419-
) -> dict[int, dict[str, int]]:
419+
) -> UserIPList:
420420
db_user = await get_user(db, username=username)
421421
if db_user is None:
422422
await self.raise_error(message="User not found", code=404)
423423

424-
node = await node_manager.get_node(node_id)
424+
email = f"{db_user.id}.{db_user.username}"
425+
ips = await self._get_node_user_ip_list_safe(node_id, email)
425426

426-
if node is None:
427-
await self.raise_error(message="Node not found", code=404)
427+
if ips is None:
428+
await self.raise_error(message="Node not found or unavailable", code=404)
429+
430+
return UserIPList(ips=ips)
431+
432+
async def get_user_ip_list_all_nodes(self, db: AsyncSession, username: str) -> UserIPListAll:
433+
db_user = await get_user(db, username=username)
434+
if db_user is None:
435+
await self.raise_error(message="User not found", code=404)
436+
437+
nodes = await node_manager.get_healthy_nodes()
438+
email = f"{db_user.id}.{db_user.username}"
439+
440+
ip_list_tasks = {id: asyncio.create_task(self._get_node_user_ip_list_safe(id, email)) for id, _ in nodes}
441+
442+
await asyncio.gather(*ip_list_tasks.values(), return_exceptions=True)
443+
444+
results = {}
445+
for node_id, task in ip_list_tasks.items():
446+
if task.exception() or task.result() is None:
447+
results[node_id] = None
448+
else:
449+
results[node_id] = UserIPList(ips=task.result())
450+
451+
return UserIPListAll(nodes=results)
428452

453+
async def _get_node_user_ip_list_safe(self, node_id: int, email: str) -> dict[str, int] | None:
454+
"""Wrapper method that returns None instead of raising exceptions"""
429455
try:
430-
stats = await node.get_user_online_ip_list(email=f"{db_user.id}.{db_user.username}")
431-
except NodeAPIError as e:
432-
await self.raise_error(message=e.detail, code=e.code)
456+
node = await node_manager.get_node(node_id)
457+
if node is None:
458+
return None
433459

434-
if stats is None:
435-
await self.raise_error(message="Stats not found", code=404)
460+
stats = await node.get_user_online_ip_list(email=email)
461+
if stats is None:
462+
return None
436463

437-
return {node_id: stats.ips}
464+
return stats.ips
465+
except Exception as e:
466+
logger.error(f"Error getting IP list for user {email} on node {node_id}: {e}")
467+
return None
438468

439469
async def sync_node_users(self, db: AsyncSession, node_id: int, flush_users: bool = False) -> NodeResponse:
440470
db_node = await self.get_validated_node(db, node_id=node_id)

app/routers/node.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from app.db import AsyncSession, get_db
1010
from app.models.admin import AdminDetails
11-
from app.models.node import NodeCreate, NodeModify, NodeResponse, NodeSettings, UsageTable
11+
from app.models.node import NodeCreate, NodeModify, NodeResponse, NodeSettings, UsageTable, UserIPList, UserIPListAll
1212
from app.models.stats import NodeRealtimeStats, NodeStatsList, NodeUsageStatsList, Period
1313
from app.operation import OperatorType
1414
from app.operation.node import NodeOperation
@@ -177,6 +177,14 @@ async def realtime_nodes_stats(_: AdminDetails = Depends(check_sudo_admin)):
177177
return await node_operator.get_nodes_system_stats()
178178

179179

180+
@router.get("/online_stats/{username}/ip", response_model=UserIPListAll)
181+
async def user_online_ip_list_all_nodes(
182+
username: str, db: AsyncSession = Depends(get_db), _: AdminDetails = Depends(check_sudo_admin)
183+
):
184+
"""Retrieve user ips from all nodes."""
185+
return await node_operator.get_user_ip_list_all_nodes(db=db, username=username)
186+
187+
180188
@router.get("/{node_id}/online_stats/{username}", response_model=dict[int, int])
181189
async def user_online_stats(
182190
node_id: int, username: str, db: AsyncSession = Depends(get_db), _: AdminDetails = Depends(check_sudo_admin)
@@ -185,7 +193,7 @@ async def user_online_stats(
185193
return await node_operator.get_user_online_stats_by_node(db=db, node_id=node_id, username=username)
186194

187195

188-
@router.get("/{node_id}/online_stats/{username}/ip", response_model=dict[int, dict[str, int]])
196+
@router.get("/{node_id}/online_stats/{username}/ip", response_model=UserIPList)
189197
async def user_online_ip_list(
190198
node_id: int, username: str, db: AsyncSession = Depends(get_db), _: AdminDetails = Depends(check_sudo_admin)
191199
):

0 commit comments

Comments
 (0)