Files
esh-pfi-infrastructure/services/beszel-althing/bridge.py
T

64 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""Receive Beszel's Shoutrrr JSON and deliver through the supported postbox CLI."""
import json
import os
import subprocess
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
def deliver(payload):
title = payload.get('title', 'Beszel fleet alert')
message = payload.get('message')
if not isinstance(title, str) or not isinstance(message, str) or not message.strip():
raise ValueError('Expected a nonempty message and string title')
result = subprocess.run(
[os.environ['POSTBOX'], '--json', 'send', '--to', os.environ['BESZEL_ALERT_RECIPIENT'],
'--subject', '[Beszel] ' + title],
input=message + '\n\nHub: http://10.250.50.70:8090\n',
text=True, capture_output=True, timeout=25,
)
if result.returncode:
raise RuntimeError('postbox delivery failed: ' + result.stderr.strip())
receipt = json.loads(result.stdout)
print(json.dumps({'event': 'delivered', 'title': title, 'receipt': receipt}), flush=True)
return receipt
class Handler(BaseHTTPRequestHandler):
def respond(self, status, body):
data = json.dumps(body).encode()
self.send_response(status)
self.send_header('Content-Type', 'application/json')
self.send_header('Content-Length', str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
self.respond(200 if self.path == '/healthz' else 404,
{'service': 'beszel-althing', 'delivery': 'verified per POST'})
def do_POST(self):
if self.path != '/beszel':
return self.respond(404, {'error': 'Unknown route'})
if self.client_address[0] not in os.environ['BESZEL_ALLOWED_SOURCES'].split(','):
return self.respond(403, {'error': 'Source not allowed'})
try:
length = int(self.headers.get('Content-Length', '0'))
if not 0 < length <= 65536:
raise ValueError('Invalid body size')
self.connection.settimeout(10)
payload = json.loads(self.rfile.read(length))
if not isinstance(payload, dict):
raise ValueError('Expected JSON object')
receipt = deliver(payload)
except (ValueError, TypeError) as exc:
return self.respond(400, {'error': str(exc)})
except (OSError, RuntimeError, subprocess.TimeoutExpired) as exc:
print(json.dumps({'event': 'delivery_failed', 'error': str(exc)}), flush=True)
return self.respond(502, {'error': 'Althing delivery failed; inspect service journal'})
self.respond(200, {'delivered': True, 'receipt': receipt})
if __name__ == '__main__':
ThreadingHTTPServer((os.environ['BESZEL_BIND_HOST'], int(os.environ.get('BESZEL_BIND_PORT', '8096'))), Handler).serve_forever()