131 lines
4.4 KiB
Python
131 lines
4.4 KiB
Python
"""
|
|
SLMM (Sound Level Meter Manager) Proxy Router
|
|
|
|
Proxies requests from SFM to the standalone SLMM backend service.
|
|
SLMM runs on port 8100 and handles NL43/NL53 sound level meter communication.
|
|
"""
|
|
|
|
from fastapi import APIRouter, HTTPException, Request, Response
|
|
from fastapi.responses import StreamingResponse
|
|
import httpx
|
|
import logging
|
|
import os
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api/slmm", tags=["slmm"])
|
|
|
|
# SLMM backend URL - configurable via environment variable
|
|
SLMM_BASE_URL = os.getenv("SLMM_BASE_URL", "http://localhost:8100")
|
|
|
|
|
|
@router.get("/health")
|
|
async def check_slmm_health():
|
|
"""
|
|
Check if the SLMM backend service is reachable and healthy.
|
|
"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
response = await client.get(f"{SLMM_BASE_URL}/health")
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
return {
|
|
"status": "ok",
|
|
"slmm_status": "connected",
|
|
"slmm_url": SLMM_BASE_URL,
|
|
"slmm_version": data.get("version", "unknown"),
|
|
"slmm_response": data
|
|
}
|
|
else:
|
|
return {
|
|
"status": "degraded",
|
|
"slmm_status": "error",
|
|
"slmm_url": SLMM_BASE_URL,
|
|
"detail": f"SLMM returned status {response.status_code}"
|
|
}
|
|
|
|
except httpx.ConnectError:
|
|
return {
|
|
"status": "error",
|
|
"slmm_status": "unreachable",
|
|
"slmm_url": SLMM_BASE_URL,
|
|
"detail": "Cannot connect to SLMM backend. Is it running?"
|
|
}
|
|
except Exception as e:
|
|
return {
|
|
"status": "error",
|
|
"slmm_status": "error",
|
|
"slmm_url": SLMM_BASE_URL,
|
|
"detail": str(e)
|
|
}
|
|
|
|
|
|
@router.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"])
|
|
async def proxy_to_slmm(path: str, request: Request):
|
|
"""
|
|
Proxy all requests to the SLMM backend service.
|
|
|
|
This allows SFM to act as a unified frontend for all device types,
|
|
while SLMM remains a standalone backend service.
|
|
"""
|
|
# Build target URL
|
|
target_url = f"{SLMM_BASE_URL}/api/nl43/{path}"
|
|
|
|
# Get query parameters
|
|
query_params = dict(request.query_params)
|
|
|
|
# Get request body if present
|
|
body = None
|
|
if request.method in ["POST", "PUT", "PATCH"]:
|
|
try:
|
|
body = await request.body()
|
|
except Exception as e:
|
|
logger.error(f"Failed to read request body: {e}")
|
|
body = None
|
|
|
|
# Get headers (exclude host and other proxy-specific headers)
|
|
headers = dict(request.headers)
|
|
headers_to_exclude = ["host", "content-length", "transfer-encoding", "connection"]
|
|
proxy_headers = {k: v for k, v in headers.items() if k.lower() not in headers_to_exclude}
|
|
|
|
logger.info(f"Proxying {request.method} request to SLMM: {target_url}")
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
# Forward the request to SLMM
|
|
response = await client.request(
|
|
method=request.method,
|
|
url=target_url,
|
|
params=query_params,
|
|
headers=proxy_headers,
|
|
content=body
|
|
)
|
|
|
|
# Return the response from SLMM
|
|
return Response(
|
|
content=response.content,
|
|
status_code=response.status_code,
|
|
headers=dict(response.headers),
|
|
media_type=response.headers.get("content-type")
|
|
)
|
|
|
|
except httpx.ConnectError:
|
|
logger.error(f"Failed to connect to SLMM backend at {SLMM_BASE_URL}")
|
|
raise HTTPException(
|
|
status_code=503,
|
|
detail=f"SLMM backend service unavailable. Is SLMM running on {SLMM_BASE_URL}?"
|
|
)
|
|
except httpx.TimeoutException:
|
|
logger.error(f"Timeout connecting to SLMM backend at {SLMM_BASE_URL}")
|
|
raise HTTPException(
|
|
status_code=504,
|
|
detail="SLMM backend timeout"
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error proxying to SLMM: {e}")
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"Failed to proxy request to SLMM: {str(e)}"
|
|
)
|