Files
homelable/backend/app/api/routes/status.py
T
Pouzor dbfc8a2a32 security: fix C3, C1 and H1
C3 - config.yml contains credentials, remove from git tracking:
  - Add backend/config.yml to .gitignore
  - git rm --cached to untrack it
  - Add backend/config.yml.example with instructions

C1 - SECRET_KEY must come from .env, no unsafe default:
  - Remove hardcoded "change_me_in_production" default from config.py
  - App now fails to start if SECRET_KEY is not set (pydantic required field)
  - Generate real random key in backend/.env (gitignored)
  - Add backend/.env.example for new contributors

H1 - WebSocket /ws/status was unauthenticated:
  - Backend: require ?token= query param, validate via decode_token(),
    close with code 1008 (Policy Violation) if missing or invalid
  - Frontend: append ?token=<jwt> to WebSocket URL
2026-03-09 00:05:10 +01:00

51 lines
1.4 KiB
Python

import json
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from app.core.security import decode_token
router = APIRouter()
# Active WebSocket connections
_connections: list[WebSocket] = []
@router.websocket("/ws/status")
async def ws_status(websocket: WebSocket, token: str | None = None) -> None:
if not token or not decode_token(token):
await websocket.close(code=1008) # Policy Violation
return
await websocket.accept()
_connections.append(websocket)
try:
while True:
await websocket.receive_text()
except WebSocketDisconnect:
_connections.remove(websocket)
async def _broadcast(payload: str) -> None:
for conn in list(_connections):
try:
await conn.send_text(payload)
except Exception:
_connections.remove(conn)
async def broadcast_status(node_id: str, status: str, checked_at: str, response_time_ms: int | None = None) -> None:
await _broadcast(json.dumps({
"type": "status",
"node_id": node_id,
"status": status,
"checked_at": checked_at,
"response_time_ms": response_time_ms,
}))
async def broadcast_scan_update(run_id: str, devices_found: int) -> None:
await _broadcast(json.dumps({
"type": "scan_device_found",
"run_id": run_id,
"devices_found": devices_found,
}))