feat: Add Advoware Filesystem Change Webhook for exploratory logging
This commit is contained in:
1
src/steps/advoware_docs/__init__.py
Normal file
1
src/steps/advoware_docs/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# Advoware Document Sync Steps
|
||||||
145
src/steps/advoware_docs/filesystem_webhook_step.py
Normal file
145
src/steps/advoware_docs/filesystem_webhook_step.py
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
"""
|
||||||
|
Advoware Filesystem Change Webhook
|
||||||
|
|
||||||
|
Empfängt Events vom Windows-Watcher (explorative Phase).
|
||||||
|
Aktuell nur Logging, keine Business-Logik.
|
||||||
|
"""
|
||||||
|
from typing import Dict, Any
|
||||||
|
from motia import http, FlowContext, ApiRequest, ApiResponse
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
config = {
|
||||||
|
"name": "Advoware Filesystem Change Webhook (Exploratory)",
|
||||||
|
"description": "Empfängt Filesystem-Events vom Windows-Watcher. Aktuell nur Logging für explorative Analyse.",
|
||||||
|
"flows": ["advoware-document-sync-exploratory"],
|
||||||
|
"triggers": [http("POST", "/advoware/filesystem/akte-changed")],
|
||||||
|
"enqueues": [] # Noch keine Events, nur Logging
|
||||||
|
}
|
||||||
|
|
||||||
|
async def handler(request: ApiRequest, ctx: FlowContext) -> ApiResponse:
|
||||||
|
"""
|
||||||
|
Handler für Filesystem-Events (explorative Phase)
|
||||||
|
|
||||||
|
Payload:
|
||||||
|
{
|
||||||
|
"aktennummer": "201900145",
|
||||||
|
"timestamp": "2026-03-20T10:15:30Z"
|
||||||
|
}
|
||||||
|
|
||||||
|
Aktuelles Verhalten:
|
||||||
|
- Validiere Auth-Token
|
||||||
|
- Logge alle Details
|
||||||
|
- Return 200 OK
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info("📥 ADVOWARE FILESYSTEM EVENT EMPFANGEN")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# 1. AUTH-TOKEN VALIDIERUNG
|
||||||
|
# ========================================================
|
||||||
|
auth_header = request.headers.get('Authorization', '')
|
||||||
|
expected_token = os.getenv('ADVOWARE_WATCHER_AUTH_TOKEN', 'CHANGE_ME')
|
||||||
|
|
||||||
|
ctx.logger.info(f"🔐 Auth-Header: {auth_header[:20]}..." if auth_header else "❌ Kein Auth-Header")
|
||||||
|
|
||||||
|
if not auth_header.startswith('Bearer ') or auth_header[7:] != expected_token:
|
||||||
|
ctx.logger.error("❌ Invalid auth token")
|
||||||
|
ctx.logger.error(f" Expected: Bearer {expected_token[:10]}...")
|
||||||
|
ctx.logger.error(f" Received: {auth_header[:30]}...")
|
||||||
|
return ApiResponse(status_code=401, body={"error": "Unauthorized"})
|
||||||
|
|
||||||
|
ctx.logger.info("✅ Auth-Token valid")
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# 2. PAYLOAD LOGGING
|
||||||
|
# ========================================================
|
||||||
|
payload = request.body
|
||||||
|
|
||||||
|
ctx.logger.info(f"📦 Payload Type: {type(payload)}")
|
||||||
|
ctx.logger.info(f"📦 Payload Keys: {list(payload.keys()) if isinstance(payload, dict) else 'N/A'}")
|
||||||
|
ctx.logger.info(f"📦 Payload Content:")
|
||||||
|
|
||||||
|
# Detailliertes Logging aller Felder
|
||||||
|
if isinstance(payload, dict):
|
||||||
|
for key, value in payload.items():
|
||||||
|
ctx.logger.info(f" {key}: {value} (type: {type(value).__name__})")
|
||||||
|
else:
|
||||||
|
ctx.logger.info(f" {payload}")
|
||||||
|
|
||||||
|
# Aktennummer extrahieren
|
||||||
|
aktennummer = payload.get('aktennummer') if isinstance(payload, dict) else None
|
||||||
|
timestamp = payload.get('timestamp') if isinstance(payload, dict) else None
|
||||||
|
|
||||||
|
if not aktennummer:
|
||||||
|
ctx.logger.error("❌ Missing 'aktennummer' in payload")
|
||||||
|
return ApiResponse(status_code=400, body={"error": "Missing aktennummer"})
|
||||||
|
|
||||||
|
ctx.logger.info(f"📂 Aktennummer: {aktennummer}")
|
||||||
|
ctx.logger.info(f"⏰ Timestamp: {timestamp}")
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# 3. REQUEST HEADERS LOGGING
|
||||||
|
# ========================================================
|
||||||
|
ctx.logger.info("📋 Request Headers:")
|
||||||
|
for header_name, header_value in request.headers.items():
|
||||||
|
# Kürze Authorization-Token für Logs
|
||||||
|
if header_name.lower() == 'authorization':
|
||||||
|
header_value = header_value[:20] + "..." if len(header_value) > 20 else header_value
|
||||||
|
ctx.logger.info(f" {header_name}: {header_value}")
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# 4. REQUEST METADATA LOGGING
|
||||||
|
# ========================================================
|
||||||
|
ctx.logger.info("🔍 Request Metadata:")
|
||||||
|
ctx.logger.info(f" Method: {request.method}")
|
||||||
|
ctx.logger.info(f" Path: {request.path}")
|
||||||
|
ctx.logger.info(f" Query Params: {request.query_params}")
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# 5. TODO: Business-Logik (später)
|
||||||
|
# ========================================================
|
||||||
|
ctx.logger.info("💡 TODO: Hier später Business-Logik implementieren:")
|
||||||
|
ctx.logger.info(" 1. Redis SADD pending_aktennummern")
|
||||||
|
ctx.logger.info(" 2. Optional: Emit Queue-Event")
|
||||||
|
ctx.logger.info(" 3. Optional: Sofort-Trigger für Batch-Sync")
|
||||||
|
|
||||||
|
# ========================================================
|
||||||
|
# 6. ERFOLG
|
||||||
|
# ========================================================
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
ctx.logger.info(f"✅ Event verarbeitet: Akte {aktennummer}")
|
||||||
|
ctx.logger.info("=" * 80)
|
||||||
|
|
||||||
|
return ApiResponse(
|
||||||
|
status_code=200,
|
||||||
|
body={
|
||||||
|
"success": True,
|
||||||
|
"aktennummer": aktennummer,
|
||||||
|
"received_at": datetime.now().isoformat(),
|
||||||
|
"message": "Event logged successfully (exploratory mode)"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error(f"❌ ERROR in Filesystem Webhook: {e}")
|
||||||
|
ctx.logger.error("=" * 80)
|
||||||
|
ctx.logger.error(f"Exception Type: {type(e).__name__}")
|
||||||
|
ctx.logger.error(f"Exception Message: {str(e)}")
|
||||||
|
|
||||||
|
# Traceback
|
||||||
|
import traceback
|
||||||
|
ctx.logger.error("Traceback:")
|
||||||
|
ctx.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
return ApiResponse(
|
||||||
|
status_code=500,
|
||||||
|
body={
|
||||||
|
"success": False,
|
||||||
|
"error": str(e),
|
||||||
|
"error_type": type(e).__name__
|
||||||
|
}
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user