From 988ffbebdbdb6c949c9a9883588aff648d243425 Mon Sep 17 00:00:00 2001 From: Egor Date: Mon, 8 Dec 2025 03:19:23 +0300 Subject: [PATCH] Expand Remnawave node statistics --- app/handlers/admin/remnawave.py | 91 +++++++++++++++++++++++++++++-- app/services/remnawave_service.py | 15 ++++- app/webapi/routes/remnawave.py | 13 +++++ app/webapi/schemas/remnawave.py | 13 +++++ 4 files changed, 126 insertions(+), 6 deletions(-) diff --git a/app/handlers/admin/remnawave.py b/app/handlers/admin/remnawave.py index 968a56f2..70159ba9 100644 --- a/app/handlers/admin/remnawave.py +++ b/app/handlers/admin/remnawave.py @@ -1316,7 +1316,29 @@ async def show_node_details( status_emoji = "🟢" if node["is_node_online"] else "🔴" xray_emoji = "✅" if node["is_xray_running"] else "❌" - + + status_change = ( + format_datetime(node["last_status_change"]) + if node.get("last_status_change") + else "—" + ) + created_at = ( + format_datetime(node["created_at"]) + if node.get("created_at") + else "—" + ) + updated_at = ( + format_datetime(node["updated_at"]) + if node.get("updated_at") + else "—" + ) + notify_percent = ( + f"{node['notify_percent']}%" if node.get("notify_percent") is not None else "—" + ) + cpu_info = node.get("cpu_model") or "—" + if node.get("cpu_count"): + cpu_info = f"{node['cpu_count']}x {cpu_info}" + text = f""" 🖥️ Нода: {node['name']} @@ -1325,15 +1347,29 @@ async def show_node_details( - Xray: {xray_emoji} {'Запущен' if node['is_xray_running'] else 'Остановлен'} - Подключена: {'📡 Да' if node['is_connected'] else '📵 Нет'} - Отключена: {'❌ Да' if node['is_disabled'] else '✅ Нет'} +- Изменение статуса: {status_change} +- Сообщение: {node.get('last_status_message') or '—'} +- Uptime Xray: {node.get('xray_uptime') or '—'} Информация: - Адрес: {node['address']} - Страна: {node['country_code']} - Пользователей онлайн: {node['users_online']} +- CPU: {cpu_info} +- RAM: {node.get('total_ram') or '—'} +- Провайдер: {node.get('provider_uuid') or '—'} Трафик: - Использовано: {format_bytes(node['traffic_used_bytes'])} - Лимит: {format_bytes(node['traffic_limit_bytes']) if node['traffic_limit_bytes'] else 'Без лимита'} +- Трекинг: {'✅ Активен' if node.get('is_traffic_tracking_active') else '❌ Отключен'} +- День сброса: {node.get('traffic_reset_day') or '—'} +- Уведомления: {notify_percent} +- Множитель: {node.get('consumption_multiplier') or 1} + +Метаданные: +- Создана: {created_at} +- Обновлена: {updated_at} """ await callback.message.edit_text( @@ -1407,10 +1443,32 @@ async def show_node_statistics( if stats.get('nodeUuid') == node_uuid: node_realtime = stats break - + + status_change = ( + format_datetime(node["last_status_change"]) + if node.get("last_status_change") + else "—" + ) + created_at = ( + format_datetime(node["created_at"]) + if node.get("created_at") + else "—" + ) + updated_at = ( + format_datetime(node["updated_at"]) + if node.get("updated_at") + else "—" + ) + notify_percent = ( + f"{node['notify_percent']}%" if node.get("notify_percent") is not None else "—" + ) + cpu_info = node.get("cpu_model") or "—" + if node.get("cpu_count"): + cpu_info = f"{node['cpu_count']}x {cpu_info}" + status_emoji = "🟢" if node["is_node_online"] else "🔴" xray_emoji = "✅" if node["is_xray_running"] else "❌" - + text = f""" 📊 Статистика ноды: {node['name']} @@ -1418,10 +1476,26 @@ async def show_node_statistics( - Онлайн: {status_emoji} {'Да' if node['is_node_online'] else 'Нет'} - Xray: {xray_emoji} {'Запущен' if node['is_xray_running'] else 'Остановлен'} - Пользователей онлайн: {node['users_online'] or 0} +- Изменение статуса: {status_change} +- Сообщение: {node.get('last_status_message') or '—'} +- Uptime Xray: {node.get('xray_uptime') or '—'} + +Ресурсы: +- CPU: {cpu_info} +- RAM: {node.get('total_ram') or '—'} +- Провайдер: {node.get('provider_uuid') or '—'} Трафик: - Использовано: {format_bytes(node['traffic_used_bytes'] or 0)} - Лимит: {format_bytes(node['traffic_limit_bytes']) if node['traffic_limit_bytes'] else 'Без лимита'} +- Трекинг: {'✅ Активен' if node.get('is_traffic_tracking_active') else '❌ Отключен'} +- День сброса: {node.get('traffic_reset_day') or '—'} +- Уведомления: {notify_percent} +- Множитель: {node.get('consumption_multiplier') or 1} + +Метаданные: +- Создана: {created_at} +- Обновлена: {updated_at} """ if node_realtime: @@ -1456,18 +1530,25 @@ async def show_node_statistics( except Exception as e: logger.error(f"Ошибка получения статистики ноды {node_uuid}: {e}") - + text = f""" 📊 Статистика ноды: {node['name']} Статус: -- Онлайн: {status_emoji} {'Да' if node['is_node_online'] else 'Нет'} +- Онлайн: {status_emoji} {'Да' if node['is_node_online'] else 'Нет'} - Xray: {xray_emoji} {'Запущен' if node['is_xray_running'] else 'Остановлен'} - Пользователей онлайн: {node['users_online'] or 0} +- Изменение статуса: {format_datetime(node.get('last_status_change')) if node.get('last_status_change') else '—'} +- Сообщение: {node.get('last_status_message') or '—'} +- Uptime Xray: {node.get('xray_uptime') or '—'} Трафик: - Использовано: {format_bytes(node['traffic_used_bytes'] or 0)} - Лимит: {format_bytes(node['traffic_limit_bytes']) if node['traffic_limit_bytes'] else 'Без лимита'} +- Трекинг: {'✅ Активен' if node.get('is_traffic_tracking_active') else '❌ Отключен'} +- День сброса: {node.get('traffic_reset_day') or '—'} +- Уведомления: {node.get('notify_percent') or '—'} +- Множитель: {node.get('consumption_multiplier') or 1} ⚠️ Детальная статистика временно недоступна Возможные причины: diff --git a/app/services/remnawave_service.py b/app/services/remnawave_service.py index d5223a30..0c95cd10 100644 --- a/app/services/remnawave_service.py +++ b/app/services/remnawave_service.py @@ -780,7 +780,20 @@ class RemnaWaveService: "is_xray_running": node.is_xray_running, "users_online": node.users_online or 0, "traffic_used_bytes": node.traffic_used_bytes or 0, - "traffic_limit_bytes": node.traffic_limit_bytes or 0 + "traffic_limit_bytes": node.traffic_limit_bytes or 0, + "last_status_change": node.last_status_change, + "last_status_message": node.last_status_message, + "xray_uptime": node.xray_uptime, + "is_traffic_tracking_active": node.is_traffic_tracking_active, + "traffic_reset_day": node.traffic_reset_day, + "notify_percent": node.notify_percent, + "consumption_multiplier": node.consumption_multiplier, + "cpu_count": node.cpu_count, + "cpu_model": node.cpu_model, + "total_ram": node.total_ram, + "created_at": node.created_at, + "updated_at": node.updated_at, + "provider_uuid": node.provider_uuid, } except Exception as e: diff --git a/app/webapi/routes/remnawave.py b/app/webapi/routes/remnawave.py index 421db6d3..d0069f36 100644 --- a/app/webapi/routes/remnawave.py +++ b/app/webapi/routes/remnawave.py @@ -93,6 +93,19 @@ def _serialize_node(node_data: Dict[str, Any]) -> RemnaWaveNode: users_online=node_data.get("users_online"), traffic_used_bytes=node_data.get("traffic_used_bytes"), traffic_limit_bytes=node_data.get("traffic_limit_bytes"), + last_status_change=_parse_last_updated(node_data.get("last_status_change")), + last_status_message=node_data.get("last_status_message"), + xray_uptime=node_data.get("xray_uptime"), + is_traffic_tracking_active=bool(node_data.get("is_traffic_tracking_active", False)), + traffic_reset_day=node_data.get("traffic_reset_day"), + notify_percent=node_data.get("notify_percent"), + consumption_multiplier=float(node_data.get("consumption_multiplier", 1.0)), + cpu_count=node_data.get("cpu_count"), + cpu_model=node_data.get("cpu_model"), + total_ram=node_data.get("total_ram"), + created_at=_parse_last_updated(node_data.get("created_at")), + updated_at=_parse_last_updated(node_data.get("updated_at")), + provider_uuid=node_data.get("provider_uuid"), ) diff --git a/app/webapi/schemas/remnawave.py b/app/webapi/schemas/remnawave.py index bc41a41c..d640f5bd 100644 --- a/app/webapi/schemas/remnawave.py +++ b/app/webapi/schemas/remnawave.py @@ -32,6 +32,19 @@ class RemnaWaveNode(BaseModel): users_online: Optional[int] = None traffic_used_bytes: Optional[int] = None traffic_limit_bytes: Optional[int] = None + last_status_change: Optional[datetime] = None + last_status_message: Optional[str] = None + xray_uptime: Optional[str] = None + is_traffic_tracking_active: bool = False + traffic_reset_day: Optional[int] = None + notify_percent: Optional[int] = None + consumption_multiplier: float = 1.0 + cpu_count: Optional[int] = None + cpu_model: Optional[str] = None + total_ram: Optional[str] = None + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + provider_uuid: Optional[str] = None class RemnaWaveNodeListResponse(BaseModel):