Initial commit — Superfice.de website

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-30 16:11:39 +02:00
commit 75beb3607e
63 changed files with 11514 additions and 0 deletions

112
api/helpers.php Normal file
View File

@@ -0,0 +1,112 @@
<?php
/**
* Superfice API Helpers
*
* Shared utility functions for API endpoints.
* Not directly accessible (no public entry point).
*/
// -------------------------------------------------------
// CORS
// -------------------------------------------------------
function cors_headers(): void {
$allowed = defined('SITE_ORIGIN') ? SITE_ORIGIN : '*';
header('Access-Control-Allow-Origin: ' . $allowed);
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
}
// -------------------------------------------------------
// JSON error response
// -------------------------------------------------------
function json_error(string $msg, int $code = 400): never {
http_response_code($code);
echo json_encode(['error' => $msg]);
exit;
}
// -------------------------------------------------------
// Client IP (respects proxy headers cautiously)
// -------------------------------------------------------
function client_ip(): string {
// Only trust X-Forwarded-For if behind a known proxy
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
return trim($ips[0]);
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
// -------------------------------------------------------
// File-based rate limiting
// -------------------------------------------------------
function rate_limit(string $key, int $maxRequests, int $window): void {
$dir = defined('RATE_LIMIT_DIR') ? RATE_LIMIT_DIR : sys_get_temp_dir() . '/sf_rl';
if (!is_dir($dir)) {
@mkdir($dir, 0700, true);
}
$file = $dir . '/' . hash('sha256', $key) . '.json';
$now = time();
$data = ['count' => 0, 'reset' => $now + $window];
if (file_exists($file)) {
$stored = json_decode(file_get_contents($file), true);
if (is_array($stored) && $stored['reset'] > $now) {
$data = $stored;
}
}
$data['count']++;
file_put_contents($file, json_encode($data), LOCK_EX);
if ($data['count'] > $maxRequests) {
http_response_code(429);
echo json_encode(['error' => 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.']);
exit;
}
}
// -------------------------------------------------------
// Haversine distance (km)
// -------------------------------------------------------
function haversine(float $lat1, float $lon1, float $lat2, float $lon2): int {
$R = 6371;
$dLat = deg2rad($lat2 - $lat1);
$dLon = deg2rad($lon2 - $lon1);
$a = sin($dLat / 2) ** 2
+ cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon / 2) ** 2;
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return (int) round($R * $c);
}
// -------------------------------------------------------
// SMTP send (basic, without PHPMailer)
// For production, install PHPMailer via Composer and
// replace this function body accordingly.
// -------------------------------------------------------
function send_smtp(string $subject, string $body, string $headers): void {
// Placeholder integrate PHPMailer here:
//
// require __DIR__ . '/../vendor/autoload.php';
// $mail = new PHPMailer\PHPMailer\PHPMailer(true);
// $mail->isSMTP();
// $mail->Host = SMTP_HOST;
// $mail->SMTPAuth = true;
// $mail->Username = SMTP_USER;
// $mail->Password = SMTP_PASS;
// $mail->SMTPSecure = SMTP_SECURE;
// $mail->Port = SMTP_PORT;
// $mail->setFrom(MAIL_FROM, MAIL_FROM_NAME);
// $mail->addAddress(MAIL_TO);
// $mail->Subject = $subject;
// $mail->Body = $body;
// $mail->send();
// Fallback to mail() until PHPMailer is integrated
$sent = @mail(MAIL_TO, $subject, $body, $headers);
if (!$sent) {
error_log('Superfice SMTP fallback mail() failed');
json_error('Beim Senden ist ein Fehler aufgetreten. Bitte schreiben Sie uns direkt.');
}
}