$success, 'message' => $message, 'timestamp' => date('c')]; if ($data !== null && (!USE_PRODUCTION || DEBUG_MODE)) { $response['data'] = $data; } echo json_encode($response); exit(); } function sanitizeInput($input) { return htmlspecialchars(trim($input ?? ''), ENT_QUOTES, 'UTF-8'); } function getClientIP() { foreach (['HTTP_CF_CONNECTING_IP','HTTP_X_FORWARDED_FOR','HTTP_X_REAL_IP','REMOTE_ADDR'] as $key) { if (!empty($_SERVER[$key])) { $ip = explode(',', $_SERVER[$key])[0]; return filter_var(trim($ip), FILTER_VALIDATE_IP) ?: 'unknown'; } } return 'unknown'; } function checkRateLimit($ip) { $file = __DIR__ . '/data/rate_limits.json'; $limits = file_exists($file) ? json_decode(file_get_contents($file), true) : []; $now = time(); // Clean old entries foreach ($limits as $k => $v) { if ($now - $v['first'] > RATE_LIMIT_WINDOW) unset($limits[$k]); } if (!isset($limits[$ip])) { $limits[$ip] = ['count' => 1, 'first' => $now]; } else { $limits[$ip]['count']++; if ($limits[$ip]['count'] > RATE_LIMIT_REQUESTS) { file_put_contents($file, json_encode($limits)); return false; } } file_put_contents($file, json_encode($limits)); return true; } function sendToWebhook($data, $webhookUrl) { $payload = json_encode($data); if (function_exists('curl_init')) { $ch = curl_init($webhookUrl); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $payload, CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Content-Length: ' . strlen($payload)], CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 15, CURLOPT_SSL_VERIFYPEER => false, ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); return ['success' => $httpCode >= 200 && $httpCode < 300, 'http_code' => $httpCode, 'response' => $response, 'error' => $error]; } // Fallback $ctx = stream_context_create(['http' => [ 'method' => 'POST', 'header' => "Content-Type: application/json\r\nContent-Length: " . strlen($payload) . "\r\n", 'content' => $payload, 'timeout' => 15, ]]); $result = @file_get_contents($webhookUrl, false, $ctx); return ['success' => $result !== false, 'response' => $result, 'method' => 'file_get_contents']; } function storeLead($data) { $file = __DIR__ . '/data/leads.json'; $leads = file_exists($file) ? json_decode(file_get_contents($file), true) : []; $leads[] = array_merge($data, ['timestamp' => date('c'), 'ip' => getClientIP()]); if (count($leads) > 200) $leads = array_slice($leads, -200); file_put_contents($file, json_encode($leads, JSON_PRETTY_PRINT)); } // ============================================================ // REQUEST HANDLERS // ============================================================ function handleContactForm($data) { $name = sanitizeInput($data['name'] ?? ''); $contact = sanitizeInput($data['contact'] ?? ''); $message = sanitizeInput($data['message'] ?? ''); $company = sanitizeInput($data['company'] ?? ''); if (empty($name) || empty($contact)) { sendResponse(false, 'Name und Kontakt sind Pflichtfelder.', null, 400); } $payload = ['source' => 'feedgine.de', 'name' => $name, 'contact' => $contact, 'message' => $message, 'company' => $company, 'timestamp' => date('c'), 'ip' => getClientIP()]; storeLead($payload); sendToWebhook($payload, FEEDGINE_WEBHOOK_URL); sendResponse(true, 'Anfrage erfolgreich übermittelt. Wir melden uns in Kürze.'); } function handleChatMessage($data) { $message = sanitizeInput($data['message'] ?? ''); $session_id = sanitizeInput($data['session_id'] ?? ''); if (empty($message)) { sendResponse(false, 'Nachricht darf nicht leer sein.', null, 400); } $payload = ['message' => $message, 'session_id' => $session_id, 'source' => 'feedgine.de']; $result = sendToWebhook($payload, KI_CHAT_WEBHOOK_URL); $botReply = 'Vielen Dank für Ihre Nachricht. Unser Team meldet sich schnellstmöglich.'; if ($result['success'] && !empty($result['response'])) { $decoded = json_decode($result['response'], true); foreach (['message','output','text','response','answer','result'] as $key) { if (!empty($decoded[$key])) { $botReply = $decoded[$key]; break; } } } sendResponse(true, 'OK', ['message' => $botReply, 'session_id' => $session_id]); } function handleCookieConsent($data) { $file = __DIR__ . '/data/cookie_consent.json'; $records = file_exists($file) ? json_decode(file_get_contents($file), true) : []; $records[] = ['consent' => $data, 'timestamp' => date('c'), 'ip' => getClientIP()]; if (count($records) > 1000) $records = array_slice($records, -1000); file_put_contents($file, json_encode($records)); sendResponse(true, 'Consent gespeichert.'); } // ============================================================ // ROUTER // ============================================================ $ip = getClientIP(); if (!checkRateLimit($ip)) { sendResponse(false, 'Zu viele Anfragen. Bitte warten.', null, 429); } $raw = file_get_contents('php://input'); $body = json_decode($raw, true); if (json_last_error() !== JSON_ERROR_NONE || empty($body)) { sendResponse(false, 'Ungültige Anfrage.', null, 400); } $type = sanitizeInput($body['type'] ?? ''); switch ($type) { case 'contact': handleContactForm($body); break; case 'chat': handleChatMessage($body); break; case 'cookie_consent': handleCookieConsent($body); break; default: sendResponse(false, "Unbekannter Typ: $type", null, 400); }