fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
135
backend/alerts_agent/actions/webhook_action.py
Normal file
135
backend/alerts_agent/actions/webhook_action.py
Normal file
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
Webhook Action für Alerts Agent.
|
||||
|
||||
Sendet HTTP-Webhooks für Alerts.
|
||||
"""
|
||||
import logging
|
||||
from typing import Dict, Any, List
|
||||
import httpx
|
||||
|
||||
from .base import ActionHandler, ActionResult, ActionType, AlertContext
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WebhookAction(ActionHandler):
|
||||
"""
|
||||
Webhook-Benachrichtigungen für Alerts.
|
||||
|
||||
Konfiguration:
|
||||
- url: Webhook-URL
|
||||
- method: HTTP-Methode (default: POST)
|
||||
- headers: Zusätzliche Headers
|
||||
- include_full_context: Vollen Alert-Kontext senden (default: true)
|
||||
"""
|
||||
|
||||
@property
|
||||
def action_type(self) -> ActionType:
|
||||
return ActionType.WEBHOOK
|
||||
|
||||
def get_required_config_fields(self) -> List[str]:
|
||||
return ["url"]
|
||||
|
||||
def validate_config(self, config: Dict[str, Any]) -> bool:
|
||||
url = config.get("url", "")
|
||||
return url.startswith("http://") or url.startswith("https://")
|
||||
|
||||
async def execute(
|
||||
self,
|
||||
context: AlertContext,
|
||||
config: Dict[str, Any],
|
||||
) -> ActionResult:
|
||||
"""
|
||||
Sendet einen Webhook.
|
||||
|
||||
Args:
|
||||
context: Alert-Kontext
|
||||
config: Webhook-Konfiguration (url, method, headers)
|
||||
|
||||
Returns:
|
||||
ActionResult
|
||||
"""
|
||||
try:
|
||||
url = config.get("url")
|
||||
method = config.get("method", "POST").upper()
|
||||
headers = config.get("headers", {})
|
||||
timeout = config.get("timeout", 30)
|
||||
|
||||
# Payload erstellen
|
||||
payload = self._build_payload(context, config)
|
||||
|
||||
# Standard-Headers
|
||||
headers.setdefault("Content-Type", "application/json")
|
||||
headers.setdefault("User-Agent", "BreakPilot-AlertsAgent/1.0")
|
||||
|
||||
# Request senden
|
||||
async with httpx.AsyncClient(timeout=timeout) as client:
|
||||
if method == "POST":
|
||||
response = await client.post(url, json=payload, headers=headers)
|
||||
elif method == "PUT":
|
||||
response = await client.put(url, json=payload, headers=headers)
|
||||
else:
|
||||
response = await client.get(url, params=payload, headers=headers)
|
||||
|
||||
# Erfolg prüfen
|
||||
success = 200 <= response.status_code < 300
|
||||
|
||||
return ActionResult(
|
||||
success=success,
|
||||
action_type=self.action_type,
|
||||
message=f"Webhook {method} {url} - Status {response.status_code}",
|
||||
details={
|
||||
"url": url,
|
||||
"method": method,
|
||||
"status_code": response.status_code,
|
||||
"response_length": len(response.text),
|
||||
},
|
||||
error=None if success else f"HTTP {response.status_code}",
|
||||
)
|
||||
|
||||
except httpx.TimeoutException:
|
||||
logger.error(f"Webhook timeout: {config.get('url')}")
|
||||
return ActionResult(
|
||||
success=False,
|
||||
action_type=self.action_type,
|
||||
message="Webhook Timeout",
|
||||
error="Request timed out",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Webhook error: {e}")
|
||||
return ActionResult(
|
||||
success=False,
|
||||
action_type=self.action_type,
|
||||
message="Webhook-Fehler",
|
||||
error=str(e),
|
||||
)
|
||||
|
||||
def _build_payload(
|
||||
self,
|
||||
context: AlertContext,
|
||||
config: Dict[str, Any],
|
||||
) -> Dict[str, Any]:
|
||||
"""Erstellt den Webhook-Payload."""
|
||||
|
||||
if config.get("include_full_context", True):
|
||||
# Voller Kontext
|
||||
return {
|
||||
"event": "alert.matched",
|
||||
"alert": context.to_dict(),
|
||||
"timestamp": self._get_timestamp(),
|
||||
}
|
||||
else:
|
||||
# Minimal-Payload
|
||||
return {
|
||||
"event": "alert.matched",
|
||||
"alert_id": context.alert_id,
|
||||
"title": context.title,
|
||||
"url": context.url,
|
||||
"timestamp": self._get_timestamp(),
|
||||
}
|
||||
|
||||
def _get_timestamp(self) -> str:
|
||||
"""Gibt aktuellen ISO-Timestamp zurück."""
|
||||
from datetime import datetime
|
||||
return datetime.utcnow().isoformat() + "Z"
|
||||
Reference in New Issue
Block a user