Implement Bellhop: Matrix-authenticated *arr request portal
- FastAPI backend with async SQLite session storage - Matrix homeserver authentication (login/logout/session validation via whoami) - Server-side proxy for Radarr, Sonarr, and Lidarr search & add APIs - Fire-and-forget Matrix audit log bot for request tracking - Rate-limited login endpoint (5 req/min per IP via slowapi) - Single-page Alpine.js frontend with dark theme, media type tabs, search grid - Dockerfile for single-container deployment - Secure session cookies (httponly, samesite=strict, secure) https://claude.ai/code/session_018iMt5qm4j9bk8wzytLC3E6
This commit is contained in:
51
app/audit.py
Normal file
51
app/audit.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Matrix audit log — fire-and-forget messages to an unencrypted room."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import httpx
|
||||
|
||||
from app.config import (
|
||||
MATRIX_AUDIT_ROOM_ID,
|
||||
MATRIX_BOT_ACCESS_TOKEN,
|
||||
MATRIX_HOMESERVER_URL,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def send_audit_message(message: str) -> None:
|
||||
"""Schedule an audit message to be sent without blocking the caller."""
|
||||
if not MATRIX_AUDIT_ROOM_ID or not MATRIX_BOT_ACCESS_TOKEN:
|
||||
logger.debug("Audit log not configured, skipping")
|
||||
return
|
||||
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
return
|
||||
|
||||
loop.create_task(_send(message))
|
||||
|
||||
|
||||
async def _send(message: str) -> None:
|
||||
import time
|
||||
|
||||
room_id = MATRIX_AUDIT_ROOM_ID
|
||||
txn_id = str(int(time.time() * 1000))
|
||||
|
||||
url = (
|
||||
f"{MATRIX_HOMESERVER_URL.rstrip('/')}/_matrix/client/v3/rooms/"
|
||||
f"{room_id}/send/m.room.message/{txn_id}"
|
||||
)
|
||||
body = {
|
||||
"msgtype": "m.text",
|
||||
"body": message,
|
||||
}
|
||||
headers = {"Authorization": f"Bearer {MATRIX_BOT_ACCESS_TOKEN}"}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.put(url, json=body, headers=headers, timeout=10.0)
|
||||
except Exception:
|
||||
logger.warning("Failed to send audit message: %s", message, exc_info=True)
|
||||
Reference in New Issue
Block a user