Fix notification System

This commit is contained in:
2026-03-27 15:13:14 +01:00
parent a3247a69b6
commit e390d344ff
13 changed files with 1692 additions and 46 deletions
+125
View File
@@ -0,0 +1,125 @@
"""
Webhook sender POST HTTP con firma HMAC-SHA256.
Config non sensibile (config):
{ "url": "https://...", "content_type": "application/json" }
Config sensibile (config_enc → config_secret):
{ "webhook_secret": "..." } # opzionale usato per firma HMAC
Header inviati:
Content-Type: application/json
X-PEChub-Event: {event_type}
X-Hub-Signature-256: sha256={hex} (solo se webhook_secret configurato)
X-Delivery: {uuid}
User-Agent: PEChub-Webhook/1.0
"""
import hashlib
import hmac
import json
import uuid as uuid_mod
from datetime import datetime
import httpx
DEFAULT_TIMEOUT = 10.0
class WebhookError(Exception):
"""Errore durante l'invio di una notifica webhook."""
def __init__(self, message: str, http_status: int | None = None):
super().__init__(message)
self.http_status = http_status
async def send_webhook(
url: str,
payload: dict,
event_type: str = "new_message",
webhook_secret: str | None = None,
timeout: float = DEFAULT_TIMEOUT,
) -> dict:
"""
Invia un payload JSON a un webhook URL.
Args:
url: URL destinatario del webhook
payload: dict serializzato come JSON nel body
event_type: valore dell'header X-PEChub-Event
webhook_secret: segreto per firma HMAC-SHA256 (opzionale)
timeout: timeout HTTP in secondi
Returns:
dict con http_status, response_text, delivery_id
Raises:
WebhookError: in caso di timeout, errore di rete o HTTP >= 400
"""
body = json.dumps(payload, ensure_ascii=False, default=str).encode("utf-8")
delivery_id = str(uuid_mod.uuid4())
headers = {
"Content-Type": "application/json",
"X-PEChub-Event": event_type,
"X-Delivery": delivery_id,
"User-Agent": "PEChub-Webhook/1.0",
}
if webhook_secret:
sig = hmac.new(
webhook_secret.encode("utf-8"),
body,
hashlib.sha256,
).hexdigest()
headers["X-Hub-Signature-256"] = f"sha256={sig}"
async with httpx.AsyncClient(timeout=timeout) as client:
try:
response = await client.post(url, content=body, headers=headers)
except httpx.TimeoutException as exc:
raise WebhookError(
f"Timeout webhook dopo {timeout}s"
) from exc
except httpx.RequestError as exc:
raise WebhookError(f"Errore di rete webhook: {exc}") from exc
if response.status_code >= 400:
raise WebhookError(
f"Webhook ha risposto con HTTP {response.status_code}: "
f"{response.text[:200]}",
http_status=response.status_code,
)
return {
"http_status": response.status_code,
"response_text": response.text[:500],
"delivery_id": delivery_id,
}
async def send_test_webhook(
url: str,
webhook_secret: str | None = None,
channel_name: str = "PEChub",
) -> dict:
"""Invia un payload di test al webhook per verificare la configurazione."""
payload = {
"event": "test",
"channel": channel_name,
"timestamp": datetime.now().isoformat(),
"message": "Notifica di test da PEChub",
"data": {
"subject": "[TEST] PEC di prova",
"from_address": "test@pec.example.it",
"pec_type": "posta_certificata",
"direction": "inbound",
},
}
return await send_webhook(
url=url,
payload=payload,
event_type="test",
webhook_secret=webhook_secret,
)