forked from iarv/Remote-Terminal-for-MeshCore
90 lines
2.8 KiB
Python
90 lines
2.8 KiB
Python
"""WebSocket router for real-time updates."""
|
|
|
|
import logging
|
|
import os
|
|
|
|
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
|
|
|
|
from app.config import settings
|
|
from app.radio import radio_manager
|
|
from app.repository import ChannelRepository, ContactRepository, RawPacketRepository
|
|
from app.websocket import ws_manager
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter()
|
|
|
|
|
|
@router.websocket("/ws")
|
|
async def websocket_endpoint(websocket: WebSocket) -> None:
|
|
"""WebSocket endpoint for real-time updates."""
|
|
await ws_manager.connect(websocket)
|
|
|
|
# Send initial state
|
|
try:
|
|
# Health status
|
|
db_size_mb = 0.0
|
|
try:
|
|
db_size_bytes = os.path.getsize(settings.database_path)
|
|
db_size_mb = round(db_size_bytes / (1024 * 1024), 2)
|
|
except OSError:
|
|
pass
|
|
|
|
# Get oldest undecrypted packet info
|
|
oldest_ts = None
|
|
try:
|
|
oldest_ts = await RawPacketRepository.get_oldest_undecrypted()
|
|
except RuntimeError:
|
|
pass # Database not connected
|
|
|
|
health_data = {
|
|
"status": "ok" if radio_manager.is_connected else "degraded",
|
|
"radio_connected": radio_manager.is_connected,
|
|
"serial_port": radio_manager.port,
|
|
"database_size_mb": db_size_mb,
|
|
"oldest_undecrypted_timestamp": oldest_ts,
|
|
}
|
|
await ws_manager.send_personal(websocket, "health", health_data)
|
|
|
|
# Contacts - fetch all by paginating until exhausted
|
|
all_contacts = []
|
|
chunk_size = 500
|
|
offset = 0
|
|
while True:
|
|
chunk = await ContactRepository.get_all(limit=chunk_size, offset=offset)
|
|
all_contacts.extend(chunk)
|
|
if len(chunk) < chunk_size:
|
|
break
|
|
offset += chunk_size
|
|
|
|
await ws_manager.send_personal(
|
|
websocket,
|
|
"contacts",
|
|
[c.model_dump() for c in all_contacts],
|
|
)
|
|
|
|
# Channels
|
|
channels = await ChannelRepository.get_all()
|
|
await ws_manager.send_personal(
|
|
websocket,
|
|
"channels",
|
|
[c.model_dump() for c in channels],
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error("Error sending initial state: %s", e)
|
|
|
|
# Keep connection alive and handle incoming messages
|
|
try:
|
|
while True:
|
|
# We don't expect messages from client, but need to keep connection open
|
|
# and handle pings/pongs
|
|
data = await websocket.receive_text()
|
|
# Client can send "ping" to keep alive
|
|
if data == "ping":
|
|
await websocket.send_text('{"type":"pong"}')
|
|
except WebSocketDisconnect:
|
|
await ws_manager.disconnect(websocket)
|
|
except Exception as e:
|
|
logger.debug("WebSocket error: %s", e)
|
|
await ws_manager.disconnect(websocket)
|