feat(api): Integrate uptime and reboot traffic stats into server API

This commit is contained in:
Whispering Wind
2025-08-19 23:39:01 +03:30
committed by GitHub
parent 6fa872b6c3
commit b3595c691b
2 changed files with 80 additions and 40 deletions

View File

@ -5,22 +5,35 @@ from pydantic import BaseModel
# It's better to chnage the underlying script to return bytes instead of changing it here # It's better to chnage the underlying script to return bytes instead of changing it here
# Because of this problem we use str type instead of int as type # Because of this problem we use str type instead of int as type
class ServerStatusResponse(BaseModel): class ServerStatusResponse(BaseModel):
# disk_usage: int # System Info
uptime: str
boot_time: str
cpu_usage: str cpu_usage: str
total_ram: str
ram_usage: str ram_usage: str
total_ram: str
online_users: int online_users: int
uploaded_traffic: str # Real-time Network
downloaded_traffic: str upload_speed: str
total_traffic: str download_speed: str
tcp_connections: int
udp_connections: int
# Traffic Since Reboot
reboot_uploaded_traffic: str
reboot_downloaded_traffic: str
reboot_total_traffic: str
# User Traffic (All Time)
user_uploaded_traffic: str
user_downloaded_traffic: str
user_total_traffic: str
class ServerServicesStatusResponse(BaseModel): class ServerServicesStatusResponse(BaseModel):
hysteria_server: bool hysteria_server: bool
hysteria_webpanel: bool hysteria_webpanel: bool
hysteria_iplimit: bool hysteria_iplimit: bool
# hysteria_singbox: bool
hysteria_normal_sub: bool hysteria_normal_sub: bool
hysteria_telegram_bot: bool hysteria_telegram_bot: bool
hysteria_warp: bool hysteria_warp: bool

View File

@ -11,7 +11,7 @@ async def server_status_api():
Retrieve the server status. Retrieve the server status.
This endpoint provides information about the current server status, This endpoint provides information about the current server status,
including CPU usage, RAM usage, online users, and traffic statistics. including uptime, CPU usage, RAM usage, online users, and traffic statistics.
Returns: Returns:
ServerStatusResponse: A response model containing server status details. ServerStatusResponse: A response model containing server status details.
@ -30,7 +30,6 @@ async def server_status_api():
def __parse_server_status(server_info: str) -> ServerStatusResponse: def __parse_server_status(server_info: str) -> ServerStatusResponse:
# Initial data with default values
""" """
Parse the server information provided by cli_api.server_info() Parse the server information provided by cli_api.server_info()
and return a ServerStatusResponse instance. and return a ServerStatusResponse instance.
@ -45,57 +44,87 @@ def __parse_server_status(server_info: str) -> ServerStatusResponse:
ValueError: If the server information is invalid or incomplete. ValueError: If the server information is invalid or incomplete.
""" """
data = { data = {
'uptime': 'N/A',
'boot_time': 'N/A',
'cpu_usage': '0%', 'cpu_usage': '0%',
'total_ram': '0MB', 'total_ram': '0MB',
'ram_usage': '0MB', 'ram_usage': '0MB',
'online_users': 0, 'online_users': 0,
'uploaded_traffic': '0KB', 'upload_speed': '0 B/s',
'downloaded_traffic': '0KB', 'download_speed': '0 B/s',
'total_traffic': '0KB' 'tcp_connections': 0,
'udp_connections': 0,
'reboot_uploaded_traffic': '0 B',
'reboot_downloaded_traffic': '0 B',
'reboot_total_traffic': '0 B',
'user_uploaded_traffic': '0 B',
'user_downloaded_traffic': '0 B',
'user_total_traffic': '0 B'
} }
# Example output(server_info) from cli_api.server_info(): current_section = 'general'
# 📈 CPU Usage: 9.4%
# 📋 Total RAM: 3815MB
# 💻 Used RAM: 2007MB
# 👥 Online Users: 0
#
# 🔼 Uploaded Traffic: 0 KB
# 🔽 Downloaded Traffic: 0 KB
# 📊 Total Traffic: 0 KB
for line in server_info.splitlines(): for line in server_info.splitlines():
line = line.strip()
if not line:
continue
if 'Traffic Since Last Reboot' in line:
current_section = 'reboot'
continue
elif 'User Traffic (All Time)' in line:
current_section = 'user'
continue
key, _, value = line.partition(":") key, _, value = line.partition(":")
key = key.strip().lower() key = key.strip().lower()
value = value.strip() value = value.strip()
if not key or not value: if not key or not value:
continue # Skip empty or malformed lines continue
try: try:
if 'cpu usage' in key: if 'uptime' in key:
uptime_part, _, boottime_part = value.partition('(')
data['uptime'] = uptime_part.strip()
data['boot_time'] = boottime_part.replace('since ', '').replace(')', '').strip()
elif 'cpu usage' in key:
data['cpu_usage'] = value data['cpu_usage'] = value
elif 'total ram' in key:
data['total_ram'] = value
elif 'used ram' in key: elif 'used ram' in key:
data['ram_usage'] = value parts = value.split('/')
if len(parts) == 2:
data['ram_usage'] = parts[0].strip()
data['total_ram'] = parts[1].strip()
elif 'online users' in key: elif 'online users' in key:
data['online_users'] = int(value) data['online_users'] = int(value)
elif 'uploaded traffic' in key: elif 'upload speed' in key:
value = value.replace(' ', '') data['upload_speed'] = value
data['uploaded_traffic'] = value elif 'download speed' in key:
elif "downloaded traffic" in key: data['download_speed'] = value
value = value.replace(' ', '') elif 'tcp connections' in key:
data['downloaded_traffic'] = value data['tcp_connections'] = int(value)
elif 'total traffic' in key: elif 'udp connections' in key:
value = value.replace(' ', '') data['udp_connections'] = int(value)
data["total_traffic"] = value elif 'total uploaded' in key or 'uploaded traffic' in key:
except ValueError as e: if current_section == 'reboot':
data['reboot_uploaded_traffic'] = value
elif current_section == 'user':
data['user_uploaded_traffic'] = value
elif 'total downloaded' in key or 'downloaded traffic' in key:
if current_section == 'reboot':
data['reboot_downloaded_traffic'] = value
elif current_section == 'user':
data['user_downloaded_traffic'] = value
elif 'combined traffic' in key or 'total traffic' in key:
if current_section == 'reboot':
data['reboot_total_traffic'] = value
elif current_section == 'user':
data['user_total_traffic'] = value
except (ValueError, IndexError) as e:
raise ValueError(f'Error parsing line \'{line}\': {e}') raise ValueError(f'Error parsing line \'{line}\': {e}')
# Validate required fields
try: try:
return ServerStatusResponse(**data) # type: ignore return ServerStatusResponse(**data)
except Exception as e: except Exception as e:
raise ValueError(f'Invalid or incomplete server info: {e}') raise ValueError(f'Invalid or incomplete server info: {e}')
@ -141,8 +170,6 @@ def __parse_services_status(services_status: dict[str, bool]) -> ServerServicesS
parsed_services_status['hysteria_telegram_bot'] = status parsed_services_status['hysteria_telegram_bot'] = status
elif 'hysteria-normal-sub' in service: elif 'hysteria-normal-sub' in service:
parsed_services_status['hysteria_normal_sub'] = status parsed_services_status['hysteria_normal_sub'] = status
# elif 'hysteria-singbox' in service:
# parsed_services_status['hysteria_singbox'] = status
elif 'wg-quick' in service: elif 'wg-quick' in service:
parsed_services_status['hysteria_warp'] = status parsed_services_status['hysteria_warp'] = status
return ServerServicesStatusResponse(**parsed_services_status) return ServerServicesStatusResponse(**parsed_services_status)