diff --git a/src/steps/advoware_docs/__init__.py b/src/steps/advoware_docs/__init__.py new file mode 100644 index 0000000..9c22765 --- /dev/null +++ b/src/steps/advoware_docs/__init__.py @@ -0,0 +1 @@ +# Advoware Document Sync Steps diff --git a/src/steps/advoware_docs/filesystem_webhook_step.py b/src/steps/advoware_docs/filesystem_webhook_step.py new file mode 100644 index 0000000..08b2220 --- /dev/null +++ b/src/steps/advoware_docs/filesystem_webhook_step.py @@ -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__ + } + )