Files
FeedGine/scripts/add/send.php
2026-04-22 10:57:37 +02:00

197 lines
7.2 KiB
PHP

<?php
/**
* Feedgine Web API Handler
* ALL SENSITIVE DATA IS IN config.php — NOT HERE
* Handles: contact forms, KI chat, cookie consent, tracking events
*/
require_once dirname(__DIR__, 2) . '/config.php';
// ============================================================
// SECURITY HEADERS
// ============================================================
header('Content-Type: application/json');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');
// CORS
$allowedOrigins = unserialize(ALLOWED_ORIGINS);
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowedOrigins)) {
header("Access-Control-Allow-Origin: $origin");
} else {
header('Access-Control-Allow-Origin: *');
}
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
header('Access-Control-Max-Age: 86400');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit();
}
// ============================================================
// HELPER FUNCTIONS
// ============================================================
function sendResponse($success, $message, $data = null, $statusCode = 200) {
http_response_code($statusCode);
$response = ['success' => $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);
}