Compare commits

..

4 Commits

48 changed files with 2434 additions and 739 deletions

View File

@@ -172,6 +172,27 @@ HexaHost-Frontend/
### Produktion ### Produktion
Für den Produktivbetrieb `public/` als Webroot konfigurieren. Für den Produktivbetrieb `public/` als Webroot konfigurieren.
### Production-Build & Veröffentlichung
Der Quellcode bleibt auf `dev`, der veröffentlichte Stand liegt auf `main` (ohne Kommentare, obfuskiertes JS).
**Voraussetzungen:** Node.js 18+ (inkl. npm), PHP 8+ CLI, Git
```powershell
# Windows
.\scripts\run-build.ps1
.\scripts\publish-to-main.ps1 -Push
```
```bash
# Linux / macOS
chmod +x scripts/*.sh
./scripts/run-build.sh
./scripts/publish-to-main.sh --push
```
Details: `scripts/build/README.md`
## 🔗 Backend-Integration ## 🔗 Backend-Integration
Das Backend-Repository enthält folgende wiederverwendbare Komponenten: Das Backend-Repository enthält folgende wiederverwendbare Komponenten:

8
backend/.htaccess Normal file
View File

@@ -0,0 +1,8 @@
# Direkten Zugriff auf Backend-Dateien verhindern (Document Root = public/)
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>

View File

@@ -1,12 +1,14 @@
<?php <?php
/** /**
* HexaDNS - DNS Lookup API * HexaDNS - DNS Lookup API
* *
* Führt echte DNS-Abfragen durch und gibt die Ergebnisse als JSON zurück. * Führt echte DNS-Abfragen durch und gibt die Ergebnisse als JSON zurück.
* *
* Verwendung: GET /api/dns-lookup.php?domain=example.com * Verwendung: GET /api/dns-lookup.php?domain=example.com
*/ */
require_once __DIR__ . '/../includes/api-helpers.php';
// CORS Headers für Frontend-Zugriff // CORS Headers für Frontend-Zugriff
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Origin: *');
@@ -19,26 +21,21 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit; exit;
} }
// Nur GET-Anfragen erlauben
if ($_SERVER['REQUEST_METHOD'] !== 'GET') { if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405); http_response_code(405);
echo json_encode(['error' => 'Nur GET-Anfragen erlaubt']); echo json_encode(['error' => 'Nur GET-Anfragen erlaubt']);
exit; exit;
} }
// Domain-Parameter prüfen if (!checkApiRateLimit('dns-lookup')) {
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : ''; rejectApiRateLimit();
if (empty($domain)) {
http_response_code(400);
echo json_encode(['error' => 'Domain-Parameter fehlt']);
exit;
} }
// Domain validieren (einfache Prüfung) $domain = getValidatedDomainParam();
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
if ($domain === null) {
http_response_code(400); http_response_code(400);
echo json_encode(['error' => 'Ungültiges Domain-Format']); echo json_encode(['error' => empty($_GET['domain']) ? 'Domain-Parameter fehlt' : 'Ungültiges Domain-Format']);
exit; exit;
} }

View File

@@ -1,12 +1,14 @@
<?php <?php
/** /**
* HexaDNS - DNS Propagation Check API * HexaDNS - DNS Propagation Check API
* *
* Prüft DNS-Records bei verschiedenen öffentlichen DNS-Servern * Prüft DNS-Records bei verschiedenen öffentlichen DNS-Servern
* *
* Verwendung: GET /api/dns-propagation.php?domain=example.com&type=A * Verwendung: GET /api/dns-propagation.php?domain=example.com&type=A
*/ */
require_once __DIR__ . '/../includes/api-helpers.php';
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS'); header('Access-Control-Allow-Methods: GET, OPTIONS');
@@ -17,6 +19,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit; exit;
} }
if (!checkApiRateLimit('dns-propagation')) {
rejectApiRateLimit();
}
// Öffentliche DNS-Server für Propagation-Check // Öffentliche DNS-Server für Propagation-Check
$dnsServers = [ $dnsServers = [
['name' => 'Google', 'ip' => '8.8.8.8', 'location' => 'Global'], ['name' => 'Google', 'ip' => '8.8.8.8', 'location' => 'Global'],
@@ -29,18 +35,12 @@ $dnsServers = [
['name' => 'Level3', 'ip' => '4.2.2.1', 'location' => 'USA'], ['name' => 'Level3', 'ip' => '4.2.2.1', 'location' => 'USA'],
]; ];
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : ''; $domain = getValidatedDomainParam();
$type = isset($_GET['type']) ? strtoupper(trim($_GET['type'])) : 'A'; $type = isset($_GET['type']) ? strtoupper(trim($_GET['type'])) : 'A';
if (empty($domain)) { if ($domain === null) {
http_response_code(400); http_response_code(400);
echo json_encode(['error' => 'Domain-Parameter fehlt']); echo json_encode(['error' => empty($_GET['domain']) ? 'Domain-Parameter fehlt' : 'Ungültiges Domain-Format']);
exit;
}
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
http_response_code(400);
echo json_encode(['error' => 'Ungültiges Domain-Format']);
exit; exit;
} }
@@ -100,7 +100,12 @@ function queryDnsServer(string $domain, string $type, string $server): array {
$records = []; $records = [];
// Versuche zuerst dig zu verwenden (genauer) // Versuche zuerst dig zu verwenden (genauer)
$digResult = @shell_exec("dig @{$server} {$domain} {$type} +short +time=2 +tries=1 2>/dev/null"); $digResult = @shell_exec(
'dig @' . escapeshellarg($server) . ' '
. escapeshellarg($domain) . ' '
. escapeshellarg($type)
. ' +short +time=2 +tries=1 2>/dev/null'
);
if ($digResult !== null && trim($digResult) !== '') { if ($digResult !== null && trim($digResult) !== '') {
$lines = array_filter(explode("\n", trim($digResult))); $lines = array_filter(explode("\n", trim($digResult)));

View File

@@ -7,6 +7,8 @@
* Verwendung: GET /api/ping-check.php?domain=example.com * Verwendung: GET /api/ping-check.php?domain=example.com
*/ */
require_once __DIR__ . '/../includes/api-helpers.php';
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS'); header('Access-Control-Allow-Methods: GET, OPTIONS');
@@ -17,21 +19,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit; exit;
} }
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : ''; if (!checkApiRateLimit('ping-check')) {
rejectApiRateLimit();
if (empty($domain)) {
http_response_code(400);
echo json_encode(['error' => 'Domain-Parameter fehlt']);
exit;
} }
// Protokoll und Pfad entfernen $domain = getValidatedDomainParam();
$domain = preg_replace('/^(https?:\/\/)?/', '', $domain);
$domain = explode('/', $domain)[0];
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) { if ($domain === null) {
http_response_code(400); http_response_code(400);
echo json_encode(['error' => 'Ungültiges Domain-Format']); echo json_encode(['error' => empty($_GET['domain']) ? 'Domain-Parameter fehlt' : 'Ungültiges Domain-Format']);
exit; exit;
} }
@@ -99,7 +95,8 @@ function checkIcmpPing(string $domain): array {
]; ];
// Versuche ping-Kommando // Versuche ping-Kommando
$pingResult = @shell_exec("ping -c 3 -W 2 {$domain} 2>/dev/null"); $safeDomain = escapeshellarg($domain);
$pingResult = @shell_exec("ping -c 3 -W 2 {$safeDomain} 2>/dev/null");
if ($pingResult) { if ($pingResult) {
// Prüfe auf erfolgreiche Antworten // Prüfe auf erfolgreiche Antworten

View File

@@ -1,12 +1,14 @@
<?php <?php
/** /**
* HexaDNS - Reverse DNS Lookup API * HexaDNS - Reverse DNS Lookup API
* *
* Löst eine IP-Adresse zu einem Hostnamen auf * Löst eine IP-Adresse zu einem Hostnamen auf
* *
* Verwendung: GET /api/reverse-dns.php?ip=8.8.8.8 * Verwendung: GET /api/reverse-dns.php?ip=8.8.8.8
*/ */
require_once __DIR__ . '/../includes/api-helpers.php';
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS'); header('Access-Control-Allow-Methods: GET, OPTIONS');
@@ -17,6 +19,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit; exit;
} }
if (!checkApiRateLimit('reverse-dns')) {
rejectApiRateLimit();
}
$ip = isset($_GET['ip']) ? trim($_GET['ip']) : ''; $ip = isset($_GET['ip']) ? trim($_GET['ip']) : '';
if (empty($ip)) { if (empty($ip)) {

View File

@@ -1,12 +1,14 @@
<?php <?php
/** /**
* HexaDNS - SSL Certificate Check API * HexaDNS - SSL Certificate Check API
* *
* Prüft SSL-Zertifikat-Informationen einer Domain * Prüft SSL-Zertifikat-Informationen einer Domain
* *
* Verwendung: GET /api/ssl-check.php?domain=example.com * Verwendung: GET /api/ssl-check.php?domain=example.com
*/ */
require_once __DIR__ . '/../includes/api-helpers.php';
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS'); header('Access-Control-Allow-Methods: GET, OPTIONS');
@@ -17,22 +19,15 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit; exit;
} }
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : ''; if (!checkApiRateLimit('ssl-check')) {
rejectApiRateLimit();
if (empty($domain)) {
http_response_code(400);
echo json_encode(['error' => 'Domain-Parameter fehlt']);
exit;
} }
// Protokoll und Pfad entfernen $domain = getValidatedDomainParam();
$domain = preg_replace('/^(https?:\/\/)?/', '', $domain);
$domain = explode('/', $domain)[0];
$domain = explode(':', $domain)[0]; // Port entfernen
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) { if ($domain === null) {
http_response_code(400); http_response_code(400);
echo json_encode(['error' => 'Ungültiges Domain-Format']); echo json_encode(['error' => empty($_GET['domain']) ? 'Domain-Parameter fehlt' : 'Ungültiges Domain-Format']);
exit; exit;
} }

View File

@@ -1,12 +1,14 @@
<?php <?php
/** /**
* HexaDNS - WHOIS Lookup API * HexaDNS - WHOIS Lookup API
* *
* Ruft WHOIS-Informationen für eine Domain ab * Ruft WHOIS-Informationen für eine Domain ab
* *
* Verwendung: GET /api/whois-lookup.php?domain=example.com * Verwendung: GET /api/whois-lookup.php?domain=example.com
*/ */
require_once __DIR__ . '/../includes/api-helpers.php';
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, OPTIONS'); header('Access-Control-Allow-Methods: GET, OPTIONS');
@@ -17,7 +19,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
exit; exit;
} }
$domain = isset($_GET['domain']) ? trim($_GET['domain']) : ''; if (!checkApiRateLimit('whois-lookup')) {
rejectApiRateLimit();
}
$domain = isset($_GET['domain']) ? trim((string) $_GET['domain']) : '';
if (empty($domain)) { if (empty($domain)) {
http_response_code(400); http_response_code(400);

View File

@@ -1,17 +1,6 @@
<?php <?php
/**
* HexaHost.de Konfiguration
*
* HINWEIS: Diese Datei ist veraltet!
*
* Die Konfiguration wurde nach mail-config.php verschoben.
* Bitte verwenden Sie stattdessen:
*
* require_once 'config/mail-config.php';
*
* Diese Datei wird nur aus Kompatibilitätsgründen beibehalten.
*/
// Lade die neue Konfiguration
require_once __DIR__ . '/mail-config.php'; require_once __DIR__ . '/mail-config.php';
?> ?>

View File

@@ -0,0 +1,64 @@
<?php
/**
* Zentrale Betreff-Konfiguration für das Kontaktformular
*/
/**
* @return array<string, string> Betreff-Schlüssel => Anzeigename
*/
function getContactSubjectMap(): array {
return [
'allgemeine-anfrage' => 'Allgemeine Anfrage',
'vpc-anfrage' => 'Virtual Private Container Anfrage',
'vps-anfrage' => 'Virtual Private Server Anfrage',
'mail-gateway-anfrage' => 'Mail Gateway Anfrage',
'webhosting-anfrage' => 'Webhosting Anfrage',
'it-beratung' => 'IT-Beratung',
'it-support' => 'IT-Support & Fehlerbehebung',
'netzwerk-wlan' => 'Netzwerk & WLAN-Einrichtung',
'it-sicherheit-backup' => 'IT-Sicherheit & Backup',
'webseiten-hosting-service' => 'Webseiten- & Hosting-Service',
'wartung-betreuung' => 'Wartung & Betreuung',
'support' => 'Technischer Support',
'beratung' => 'Persönliche Beratung',
'migration' => 'Migration/Umzug',
'sonstiges' => 'Sonstige Anfrage',
];
}
/**
* @param string $subjectKey
*/
function isAllowedContactSubject(string $subjectKey): bool {
return array_key_exists($subjectKey, getContactSubjectMap());
}
/**
* Betreff aus ?product= oder ?package= für die Kontaktseite ableiten
*/
function getPreselectedContactSubject(): string {
$productMap = [
'vpc' => 'vpc-anfrage',
'vps' => 'vps-anfrage',
'mail-gateway' => 'mail-gateway-anfrage',
'webhosting' => 'webhosting-anfrage',
];
if (!empty($_GET['product'])) {
$product = strtolower(preg_replace('/[^a-z0-9-]/', '', (string) $_GET['product']));
if (isset($productMap[$product])) {
return $productMap[$product];
}
}
if (!empty($_GET['package'])) {
$package = strtolower(preg_replace('/[^a-z0-9-]/', '', (string) $_GET['package']));
foreach ($productMap as $productId => $subjectKey) {
if (str_starts_with($package, $productId . '-')) {
return $subjectKey;
}
}
}
return '';
}

View File

@@ -1,67 +1,31 @@
<?php <?php
/**
* HexaHost.de Mail Configuration
*
* Bitte passen Sie die folgenden SMTP-Einstellungen an Ihre E-Mail-Provider an.
*
* Beispiele für gängige Provider:
*
* Gmail:
* - SMTP_HOST = 'smtp.gmail.com'
* - SMTP_PORT = 587
* - SMTP_USERNAME = 'ihre-email@gmail.com'
* - SMTP_PASSWORD = 'ihr-app-passwort'
*
* Outlook/Hotmail:
* - SMTP_HOST = 'smtp-mail.outlook.com'
* - SMTP_PORT = 587
*
* GMX:
* - SMTP_HOST = 'mail.gmx.net'
* - SMTP_PORT = 587
*
* Web.de:
* - SMTP_HOST = 'smtp.web.de'
* - SMTP_PORT = 587
*
* 1&1:
* - SMTP_HOST = 'smtp.1und1.de'
* - SMTP_PORT = 587
*
* Strato:
* - SMTP_HOST = 'smtp.strato.de'
* - SMTP_PORT = 587
*
* Ionos:
* - SMTP_HOST = 'smtp.ionos.de'
* - SMTP_PORT = 587
*/
// SMTP Server Einstellungen
define('SMTP_HOST', 'smtp.ihre-domain.de'); // Ihr SMTP-Server
define('SMTP_PORT', 587); // SMTP-Port (meist 587 oder 465)
define('SMTP_USERNAME', 'kontakt@ihre-domain.de'); // Ihr SMTP-Benutzername
define('SMTP_PASSWORD', 'ihr-smtp-passwort'); // Ihr SMTP-Passwort
// E-Mail Adressen
define('SMTP_FROM_EMAIL', 'kontakt@hexahost.de'); // Absender-E-Mail (muss zu SMTP_USERNAME passen)
define('SMTP_TO_EMAIL', 'info@hexahost.de'); // Empfänger-E-Mail für Kontaktformular
// Sicherheitseinstellungen define('SMTP_HOST', 'smtp.ihre-domain.de');
define('ENABLE_CSRF_PROTECTION', true); // CSRF-Schutz aktivieren define('SMTP_PORT', 587);
define('ENABLE_RATE_LIMITING', true); // Rate-Limiting aktivieren define('SMTP_USERNAME', 'kontakt@ihre-domain.de');
define('MAX_REQUESTS_PER_HOUR', 10); // Max. Anfragen pro Stunde define('SMTP_PASSWORD', 'ihr-smtp-passwort');
// Spam-Schutz Einstellungen
define('ENABLE_SPAM_PROTECTION', true); // Spam-Schutz aktivieren
define('MAX_MESSAGE_LENGTH', 5000); // Max. Nachrichtenlänge
define('MIN_MESSAGE_LENGTH', 10); // Min. Nachrichtenlänge
// Debug-Einstellungen (nur für Entwicklung) define('SMTP_FROM_EMAIL', 'kontakt@hexahost.de');
define('DEBUG_MODE', false); // Debug-Modus (true/false) define('SMTP_TO_EMAIL', 'info@hexahost.de');
define('LOG_EMAILS', true); // E-Mails loggen (true/false)
define('ENABLE_CSRF_PROTECTION', true);
define('ENABLE_RATE_LIMITING', true);
define('MAX_REQUESTS_PER_HOUR', 10);
define('ENABLE_SPAM_PROTECTION', true);
define('MAX_MESSAGE_LENGTH', 5000);
define('MIN_MESSAGE_LENGTH', 10);
define('DEBUG_MODE', false);
define('LOG_EMAILS', true);
// Zusätzliche Sicherheitsheader
define('ADDITIONAL_HEADERS', [ define('ADDITIONAL_HEADERS', [
'X-Mailer' => 'HexaHost.de Contact Form', 'X-Mailer' => 'HexaHost.de Contact Form',
'X-Priority' => '3', 'X-Priority' => '3',
@@ -72,27 +36,27 @@ define('ADDITIONAL_HEADERS', [
'Precedence' => 'bulk' 'Precedence' => 'bulk'
]); ]);
// Erlaubte Domains für E-Mail-Adressen (optional)
define('ALLOWED_EMAIL_DOMAINS', [ define('ALLOWED_EMAIL_DOMAINS', [
// Leer lassen für alle Domains zu erlauben
// 'gmail.com',
// 'outlook.com',
// 'web.de',
// 'gmx.de'
]); ]);
// Blacklist für E-Mail-Adressen (optional)
define('BLACKLISTED_EMAILS', [ define('BLACKLISTED_EMAILS', [
// 'spam@example.com',
// 'test@test.com'
]); ]);
// Validierung der Konfiguration
if (!defined('SMTP_HOST') || !defined('SMTP_USERNAME') || !defined('SMTP_PASSWORD')) { if (!defined('SMTP_HOST') || !defined('SMTP_USERNAME') || !defined('SMTP_PASSWORD')) {
die('SMTP-Konfiguration ist unvollständig. Bitte überprüfen Sie die mail-config.php'); die('SMTP-Konfiguration ist unvollständig. Bitte überprüfen Sie die mail-config.php');
} }
// Überprüfung der E-Mail-Adressen
if (!filter_var(SMTP_FROM_EMAIL, FILTER_VALIDATE_EMAIL)) { if (!filter_var(SMTP_FROM_EMAIL, FILTER_VALIDATE_EMAIL)) {
die('Ungültige SMTP_FROM_EMAIL Adresse'); die('Ungültige SMTP_FROM_EMAIL Adresse');
} }
@@ -101,7 +65,7 @@ if (!filter_var(SMTP_TO_EMAIL, FILTER_VALIDATE_EMAIL)) {
die('Ungültige SMTP_TO_EMAIL Adresse'); die('Ungültige SMTP_TO_EMAIL Adresse');
} }
// Logging-Funktion
function logEmail($type, $data) { function logEmail($type, $data) {
if (!LOG_EMAILS) return; if (!LOG_EMAILS) return;
@@ -118,18 +82,18 @@ function logEmail($type, $data) {
file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX); file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
} }
// Hilfsfunktion für E-Mail-Validierung
function isValidEmail($email) { function isValidEmail($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return false; return false;
} }
// Prüfe Blacklist
if (in_array($email, BLACKLISTED_EMAILS)) { if (in_array($email, BLACKLISTED_EMAILS)) {
return false; return false;
} }
// Prüfe Domain-Whitelist (falls gesetzt)
if (!empty(ALLOWED_EMAIL_DOMAINS)) { if (!empty(ALLOWED_EMAIL_DOMAINS)) {
$domain = substr(strrchr($email, "@"), 1); $domain = substr(strrchr($email, "@"), 1);
if (!in_array($domain, ALLOWED_EMAIL_DOMAINS)) { if (!in_array($domain, ALLOWED_EMAIL_DOMAINS)) {
@@ -140,51 +104,30 @@ function isValidEmail($email) {
return true; return true;
} }
// CSRF Token generieren (wird in functions.php verwendet)
// Hinweis: Diese Funktion existiert auch in functions.php - hier nur als Fallback
if (!function_exists('generateCSRFToken')) {
function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
}
// CSRF Token validieren
if (!function_exists('validateCSRFToken')) {
function validateCSRFToken($token) {
return isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
}
/**
* Hilfsfunktion zum Abrufen der Konfiguration als Array
* Kompatibilität mit contact-handler.php
*
* @param string|null $key Optional: einzelner Schlüssel
* @return mixed Konfigurationsarray oder einzelner Wert
*/
function getHexaHostConfig($key = null) { function getHexaHostConfig($key = null) {
$config = [ $config = [
// SMTP Server-Einstellungen
'smtp_host' => SMTP_HOST, 'smtp_host' => SMTP_HOST,
'smtp_port' => SMTP_PORT, 'smtp_port' => SMTP_PORT,
'smtp_username' => SMTP_USERNAME, 'smtp_username' => SMTP_USERNAME,
'smtp_password' => SMTP_PASSWORD, 'smtp_password' => SMTP_PASSWORD,
'smtp_encryption' => 'tls', 'smtp_encryption' => 'tls',
// Absender/Empfänger
'from_email' => SMTP_FROM_EMAIL, 'from_email' => SMTP_FROM_EMAIL,
'from_name' => 'HexaHost.de Kontaktformular', 'from_name' => 'HexaHost.de Kontaktformular',
'to_email' => SMTP_TO_EMAIL, 'to_email' => SMTP_TO_EMAIL,
'to_name' => 'HexaHost Support', 'to_name' => 'HexaHost Support',
// Sicherheit
'max_requests_per_hour' => MAX_REQUESTS_PER_HOUR, 'max_requests_per_hour' => MAX_REQUESTS_PER_HOUR,
'honeypot_field' => 'website', 'honeypot_field' => 'website',
'enable_csrf' => ENABLE_CSRF_PROTECTION,
'min_message_length' => MIN_MESSAGE_LENGTH,
'max_message_length' => MAX_MESSAGE_LENGTH,
// Debug
'debug_mode' => DEBUG_MODE, 'debug_mode' => DEBUG_MODE,
'log_errors' => LOG_EMAILS, 'log_errors' => LOG_EMAILS,
]; ];

View File

@@ -1,18 +1,9 @@
<?php <?php
/**
* HexaHost.de Produkt-Konfiguration
*
* Hier können Sie alle Preise und Produktinformationen zentral verwalten.
* Nach Änderungen: npm run build && npm run deploy
*
* Verwendung in PHP-Seiten:
* require_once 'config/products-config.php';
* $packages = getProductPackages('vpc');
*/
// ============================================================================
// VIRTUAL PRIVATE CONTAINER (VPC)
// ============================================================================
$PRODUCTS['vpc'] = [ $PRODUCTS['vpc'] = [
'name' => 'Virtual Private Container', 'name' => 'Virtual Private Container',
'short_name' => 'VPC', 'short_name' => 'VPC',
@@ -112,9 +103,9 @@ $PRODUCTS['vpc'] = [
], ],
]; ];
// ============================================================================
// VIRTUAL PRIVATE SERVER (VPS)
// ============================================================================
$PRODUCTS['vps'] = [ $PRODUCTS['vps'] = [
'name' => 'Virtual Private Server', 'name' => 'Virtual Private Server',
'short_name' => 'VPS', 'short_name' => 'VPS',
@@ -214,9 +205,9 @@ $PRODUCTS['vps'] = [
], ],
]; ];
// ============================================================================
// MAIL GATEWAY
// ============================================================================
$PRODUCTS['mail-gateway'] = [ $PRODUCTS['mail-gateway'] = [
'name' => 'Mail Gateway', 'name' => 'Mail Gateway',
'short_name' => 'Mail', 'short_name' => 'Mail',
@@ -316,9 +307,9 @@ $PRODUCTS['mail-gateway'] = [
], ],
]; ];
// ============================================================================
// WEBHOSTING
// ============================================================================
$PRODUCTS['webhosting'] = [ $PRODUCTS['webhosting'] = [
'name' => 'Webhosting', 'name' => 'Webhosting',
'short_name' => 'Webhosting', 'short_name' => 'Webhosting',
@@ -418,68 +409,52 @@ $PRODUCTS['webhosting'] = [
], ],
]; ];
// ============================================================================
// HILFSFUNKTIONEN
// ============================================================================
/**
* Alle Produkte abrufen
*/
function getAllProducts() { function getAllProducts() {
global $PRODUCTS; global $PRODUCTS;
return $PRODUCTS; return $PRODUCTS;
} }
/**
* Ein Produkt abrufen
*/
function getProduct($productId) { function getProduct($productId) {
global $PRODUCTS; global $PRODUCTS;
return $PRODUCTS[$productId] ?? null; return $PRODUCTS[$productId] ?? null;
} }
/**
* Alle Pakete eines Produkts abrufen
*/
function getProductPackages($productId) { function getProductPackages($productId) {
global $PRODUCTS; global $PRODUCTS;
return $PRODUCTS[$productId]['packages'] ?? []; return $PRODUCTS[$productId]['packages'] ?? [];
} }
/**
* Ein bestimmtes Paket abrufen
*/
function getPackage($productId, $packageId) { function getPackage($productId, $packageId) {
global $PRODUCTS; global $PRODUCTS;
return $PRODUCTS[$productId]['packages'][$packageId] ?? null; return $PRODUCTS[$productId]['packages'][$packageId] ?? null;
} }
/**
* Preis eines Pakets abrufen
*/
function getPackagePrice($productId, $packageId) { function getPackagePrice($productId, $packageId) {
$package = getPackage($productId, $packageId); $package = getPackage($productId, $packageId);
return $package['price'] ?? null; return $package['price'] ?? null;
} }
/**
* Minimalen Preis eines Produkts abrufen
*/
function getMinPrice($productId) { function getMinPrice($productId) {
global $PRODUCTS; global $PRODUCTS;
return $PRODUCTS[$productId]['min_price'] ?? null; return $PRODUCTS[$productId]['min_price'] ?? null;
} }
/**
* Preis formatiert ausgeben
*/
function formatPrice($price, $withCurrency = true) { function formatPrice($price, $withCurrency = true) {
return $withCurrency ? $price . '€' : $price; return $withCurrency ? $price . '€' : $price;
} }
/**
* Generiert HTML für eine Paket-Karte
*/
function renderPackageCard($productId, $packageId, $package) { function renderPackageCard($productId, $packageId, $package) {
$featuredClass = $package['featured'] ? ' featured' : ''; $featuredClass = $package['featured'] ? ' featured' : '';
$featuredBadge = $package['featured'] ? '<div class="featured-badge">Beliebt</div>' : ''; $featuredBadge = $package['featured'] ? '<div class="featured-badge">Beliebt</div>' : '';
@@ -527,9 +502,7 @@ function renderPackageCard($productId, $packageId, $package) {
); );
} }
/**
* Generiert HTML für alle Pakete eines Produkts
*/
function renderAllPackages($productId) { function renderAllPackages($productId) {
$packages = getProductPackages($productId); $packages = getProductPackages($productId);
$html = ''; $html = '';

View File

@@ -0,0 +1,112 @@
<?php
/**
* Gemeinsame Hilfsfunktionen für öffentliche DNS/API-Endpunkte
*/
/**
* Client-IP für Rate-Limiting (Cloudflare-sicher, kein blindes X-Forwarded-For)
*/
function getApiClientIp(): string {
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])
&& filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP)) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$isTrustedProxy = filter_var(
$remoteAddr,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === false;
if ($isTrustedProxy) {
foreach (['HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR'] as $header) {
if (empty($_SERVER[$header])) {
continue;
}
$ip = trim(explode(',', $_SERVER[$header])[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return $remoteAddr;
}
/**
* Einfaches Rate-Limiting pro Endpunkt und IP
*/
function checkApiRateLimit(string $endpoint, int $maxPerHour = 120): bool {
$ip = getApiClientIp();
$cacheFile = sys_get_temp_dir() . '/hexahost_api_' . md5($endpoint . '_' . $ip) . '.txt';
$currentTime = time();
$data = ['requests' => []];
$handle = @fopen($cacheFile, 'c+');
if ($handle === false) {
return true;
}
try {
if (!flock($handle, LOCK_EX)) {
return true;
}
$contents = stream_get_contents($handle);
if ($contents !== false && $contents !== '') {
$decoded = json_decode($contents, true);
if (is_array($decoded) && isset($decoded['requests'])) {
$data = $decoded;
}
}
$data['requests'] = array_values(array_filter(
$data['requests'],
static fn($timestamp) => ($currentTime - (int) $timestamp) < 3600
));
if (count($data['requests']) >= $maxPerHour) {
return false;
}
$data['requests'][] = $currentTime;
ftruncate($handle, 0);
rewind($handle);
fwrite($handle, json_encode($data));
} finally {
flock($handle, LOCK_UN);
fclose($handle);
}
return true;
}
/**
* Domain aus GET-Parameter normalisieren und validieren
*/
function getValidatedDomainParam(string $param = 'domain'): ?string {
if (empty($_GET[$param])) {
return null;
}
$domain = trim((string) $_GET[$param]);
$domain = preg_replace('/^(https?:\/\/)?/', '', $domain);
$domain = explode('/', $domain)[0];
$domain = explode(':', $domain)[0];
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
return null;
}
return $domain;
}
/**
* Rate-Limit-JSON-Antwort senden und beenden
*/
function rejectApiRateLimit(): void {
http_response_code(429);
echo json_encode(['error' => 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.']);
exit;
}

View File

@@ -49,7 +49,7 @@
</div> </div>
</footer> </footer>
<!-- Cookie Consent Banner -->
<div id="cookieConsent" class="cookie-consent" role="dialog" aria-labelledby="cookieConsentTitle" aria-describedby="cookieConsentDesc"> <div id="cookieConsent" class="cookie-consent" role="dialog" aria-labelledby="cookieConsentTitle" aria-describedby="cookieConsentDesc">
<div class="cookie-consent-container"> <div class="cookie-consent-container">
<div class="cookie-consent-content"> <div class="cookie-consent-content">
@@ -79,7 +79,7 @@
</div> </div>
</div> </div>
<!-- Erweiterte Cookie-Einstellungen (standardmäßig versteckt) -->
<div id="cookieSettingsPanel" class="cookie-settings-panel" style="display: none;"> <div id="cookieSettingsPanel" class="cookie-settings-panel" style="display: none;">
<div class="cookie-settings-content"> <div class="cookie-settings-content">
<h4>Cookie-Einstellungen</h4> <h4>Cookie-Einstellungen</h4>
@@ -121,7 +121,7 @@
</div> </div>
</div> </div>
<!-- Google Analytics (GA4) mit Consent Mode -->
<script> <script>
window.dataLayer = window.dataLayer || []; window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} function gtag(){dataLayer.push(arguments);}
@@ -160,7 +160,7 @@
<script src="/assets/js/cookie-consent.js" defer></script> <script src="/assets/js/cookie-consent.js" defer></script>
<?php if (isset($additional_scripts)): ?> <?php if (isset($additional_scripts)): ?>
<?php foreach ($additional_scripts as $script): ?> <?php foreach ($additional_scripts as $script): ?>
<script src="<?php echo $script; ?>" defer></script> <script src="<?php echo htmlspecialchars($script, ENT_QUOTES, 'UTF-8'); ?>" defer></script>
<?php endforeach; ?> <?php endforeach; ?>
<?php endif; ?> <?php endif; ?>
</body> </body>

View File

@@ -1,11 +1,9 @@
<?php <?php
/**
* Helper functions for HexaHost.de
*/
// Sichere Session-Konfiguration
if (session_status() === PHP_SESSION_NONE) { if (session_status() === PHP_SESSION_NONE) {
// Session-Cookie-Sicherheit
ini_set('session.cookie_httponly', 1); ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) ? 1 : 0); ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) ? 1 : 0);
ini_set('session.cookie_samesite', 'Strict'); ini_set('session.cookie_samesite', 'Strict');
@@ -14,14 +12,14 @@ if (session_status() === PHP_SESSION_NONE) {
session_start(); session_start();
// Session-ID regenerieren bei Login/wichtigen Aktionen (Schutz vor Session Fixation)
if (!isset($_SESSION['initiated'])) { if (!isset($_SESSION['initiated'])) {
session_regenerate_id(true); session_regenerate_id(true);
$_SESSION['initiated'] = true; $_SESSION['initiated'] = true;
} }
} }
// PHP Error Display in Produktion deaktivieren
if (!defined('DEBUG_MODE') || !DEBUG_MODE) { if (!defined('DEBUG_MODE') || !DEBUG_MODE) {
ini_set('display_errors', 0); ini_set('display_errors', 0);
ini_set('display_startup_errors', 0); ini_set('display_startup_errors', 0);
@@ -29,18 +27,11 @@ if (!defined('DEBUG_MODE') || !DEBUG_MODE) {
ini_set('log_errors', 1); ini_set('log_errors', 1);
} }
/**
* Set page configuration and include header
*
* @param string $title The page title
* @param string $description The page description
* @param string $page The current page identifier
* @param array $scripts Additional scripts to include
*/
function includeHeader($title = '', $description = '', $page = '', $scripts = []) { function includeHeader($title = '', $description = '', $page = '', $scripts = []) {
global $page_title, $page_description, $current_page, $additional_scripts; global $page_title, $page_description, $current_page, $additional_scripts;
// Set page configuration from parameters
$page_title = !empty($title) $page_title = !empty($title)
? $title ? $title
: 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern'; : 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern';
@@ -55,28 +46,22 @@ function includeHeader($title = '', $description = '', $page = '', $scripts = []
include __DIR__ . '/header.php'; include __DIR__ . '/header.php';
} }
/**
* Include footer
*/
function includeFooter() { function includeFooter() {
include __DIR__ . '/footer.php'; include __DIR__ . '/footer.php';
} }
/**
* Generate breadcrumb navigation
*
* @param array $breadcrumbs Array of breadcrumb items [['title' => 'Home', 'url' => 'index.html'], ...]
*/
function generateBreadcrumbs($breadcrumbs) { function generateBreadcrumbs($breadcrumbs) {
echo '<div class="breadcrumb">'; echo '<div class="breadcrumb">';
$last_index = count($breadcrumbs) - 1; $last_index = count($breadcrumbs) - 1;
foreach ($breadcrumbs as $index => $item) { foreach ($breadcrumbs as $index => $item) {
if ($index === $last_index) { if ($index === $last_index) {
// Last item (current page)
echo '<span>' . htmlspecialchars($item['title']) . '</span>'; echo '<span>' . htmlspecialchars($item['title']) . '</span>';
} else { } else {
// Link to other pages
echo '<a href="' . htmlspecialchars($item['url']) . '">' . htmlspecialchars($item['title']) . '</a>'; echo '<a href="' . htmlspecialchars($item['url']) . '">' . htmlspecialchars($item['title']) . '</a>';
echo '<span>/</span>'; echo '<span>/</span>';
} }
@@ -84,15 +69,57 @@ function generateBreadcrumbs($breadcrumbs) {
echo '</div>'; echo '</div>';
} }
/**
* Generate CSRF token for form security
*
* @return string CSRF token
*/
function generateCSRFToken() { function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) { if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32)); $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
} }
return $_SESSION['csrf_token']; return $_SESSION['csrf_token'];
} }
function validateCSRFToken($token) {
if (!isset($_SESSION['csrf_token']) || !is_string($token)) {
return false;
}
if (!hash_equals($_SESSION['csrf_token'], $token)) {
return false;
}
unset($_SESSION['csrf_token']);
return true;
}
function sanitizeHeaderValue(string $value): string {
return str_replace(["\r", "\n", "\0"], '', trim($value));
}
function getClientIP(): string {
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])
&& filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP)) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$isTrustedProxy = filter_var(
$remoteAddr,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === false;
if ($isTrustedProxy) {
foreach (['HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR'] as $header) {
if (empty($_SERVER[$header])) {
continue;
}
$ip = trim(explode(',', $_SERVER[$header])[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return $remoteAddr;
}
?> ?>

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Performance: DNS Prefetch & Preconnect -->
<link rel="dns-prefetch" href="//fonts.googleapis.com"> <link rel="dns-prefetch" href="//fonts.googleapis.com">
<link rel="dns-prefetch" href="//fonts.gstatic.com"> <link rel="dns-prefetch" href="//fonts.gstatic.com">
<link rel="dns-prefetch" href="//cdn.hexahost.de"> <link rel="dns-prefetch" href="//cdn.hexahost.de">
@@ -12,37 +12,37 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdn.hexahost.de" crossorigin> <link rel="preconnect" href="https://cdn.hexahost.de" crossorigin>
<!-- Performance: Preload kritischer Ressourcen -->
<link rel="preload" href="/assets/css/style.css" as="style"> <link rel="preload" href="/assets/css/style.css" as="style">
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" as="style"> <link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" as="style">
<title><?php echo isset($page_title) ? htmlspecialchars($page_title) : 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern'; ?></title> <title><?php echo isset($page_title) ? htmlspecialchars($page_title) : 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern'; ?></title>
<!-- SEO Meta Tags -->
<meta name="description" content="<?php echo isset($page_description) ? htmlspecialchars($page_description) : 'HexaHost.de - Zuverlässiges und preiswertes Hosting aus Niederbayern. VPS, VPC, Mail Gateway und Webhosting Lösungen.'; ?>"> <meta name="description" content="<?php echo isset($page_description) ? htmlspecialchars($page_description) : 'HexaHost.de - Zuverlässiges und preiswertes Hosting aus Niederbayern. VPS, VPC, Mail Gateway und Webhosting Lösungen.'; ?>">
<meta name="robots" content="index, follow"> <meta name="robots" content="index, follow">
<meta name="author" content="HexaHost.de"> <meta name="author" content="HexaHost.de">
<meta name="theme-color" content="#0d0821"> <meta name="theme-color" content="#0d0821">
<!-- Open Graph / Social Media -->
<meta property="og:type" content="website"> <meta property="og:type" content="website">
<meta property="og:site_name" content="HexaHost.de"> <meta property="og:site_name" content="HexaHost.de">
<meta property="og:title" content="<?php echo isset($page_title) ? htmlspecialchars($page_title) : 'HexaHost.de'; ?>"> <meta property="og:title" content="<?php echo isset($page_title) ? htmlspecialchars($page_title) : 'HexaHost.de'; ?>">
<meta property="og:description" content="<?php echo isset($page_description) ? htmlspecialchars($page_description) : 'Zuverlässiges Hosting aus Niederbayern'; ?>"> <meta property="og:description" content="<?php echo isset($page_description) ? htmlspecialchars($page_description) : 'Zuverlässiges Hosting aus Niederbayern'; ?>">
<meta property="og:locale" content="de_DE"> <meta property="og:locale" content="de_DE">
<!-- Main Stylesheets -->
<link rel="stylesheet" href="/assets/css/style.css"> <link rel="stylesheet" href="/assets/css/style.css">
<link rel="stylesheet" href="/assets/css/custom.css"> <link rel="stylesheet" href="/assets/css/custom.css">
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Russo+One&family=Source+Sans+Pro:wght@300;400;600;700&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Russo+One&family=Source+Sans+Pro:wght@300;400;600;700&display=swap" rel="stylesheet">
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg"> <link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="apple-touch-icon" href="/favicon.svg"> <link rel="apple-touch-icon" href="/favicon.svg">
<!-- Canonical URL (falls gesetzt) -->
<?php if (isset($canonical_url)): ?> <?php if (isset($canonical_url)): ?>
<link rel="canonical" href="<?php echo htmlspecialchars($canonical_url); ?>"> <link rel="canonical" href="<?php echo htmlspecialchars($canonical_url); ?>">
<?php endif; ?> <?php endif; ?>

View File

@@ -1,15 +1,15 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = '404 - Seite nicht gefunden | HexaHost.de'; $page_title = '404 - Seite nicht gefunden | HexaHost.de';
$page_description = 'Die angeforderte Seite wurde nicht gefunden.'; $page_description = 'Die angeforderte Seite wurde nicht gefunden.';
$current_page = '404'; $current_page = '404';
// Set 404 header
http_response_code(404); http_response_code(404);
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>

View File

@@ -1,15 +1,15 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = '500 - Serverfehler | HexaHost.de'; $page_title = '500 - Serverfehler | HexaHost.de';
$page_description = 'Ein interner Serverfehler ist aufgetreten.'; $page_description = 'Ein interner Serverfehler ist aufgetreten.';
$current_page = '500'; $current_page = '500';
// Set 500 header
http_response_code(500); http_response_code(500);
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>

View File

@@ -1,17 +1,17 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = 'Über mich - HexaHost.de | Hosting aus Niederbayern'; $page_title = 'Über mich - HexaHost.de | Hosting aus Niederbayern';
$page_description = 'Erfahren Sie mehr über HexaHost.de - Ihr zuverlässiger Hosting-Partner aus Niederbayern. Moderne Technologie mit persönlichem Service.'; $page_description = 'Erfahren Sie mehr über HexaHost.de - Ihr zuverlässiger Hosting-Partner aus Niederbayern. Moderne Technologie mit persönlichem Service.';
$current_page = 'about'; $current_page = 'about';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- About Hero -->
<section class="about-hero"> <section class="about-hero">
<div class="container"> <div class="container">
<div class="about-hero-content"> <div class="about-hero-content">
@@ -33,7 +33,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Company Story -->
<section class="company-story"> <section class="company-story">
<div class="container"> <div class="container">
<div class="story-content"> <div class="story-content">
@@ -78,7 +78,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Values -->
<section class="values"> <section class="values">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -128,53 +128,9 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!--
<section class="team">
<div class="container">
<div class="section-header">
<h2 class="section-title">Mein Team</h2>
<p class="section-description">
Die Menschen, die hinter HexaHost.de stehen
</p>
</div>
<div class="team-content">
<div class="team-text">
<p>
Mein Team besteht aus erfahrenen IT-Experten, die langjährige Erfahrung
im Bereich Hosting und Server-Management. Ich bin leidenschaftlich
daran interessiert, Ihnen die bestmöglichen Hosting- und IT-Lösungen zu bieten.
</p>
<p>
Als regionales Unternehmen aus Niederbayern kennen wir die lokalen
Bedürfnisse und bieten persönlichen Support in deutscher Sprache.
Unser Ziel ist es, Ihnen nicht nur technische Lösungen, sondern
auch eine echte Partnerschaft zu bieten.
</p>
</div>
<div class="team-stats glass-card">
<div class="stat-item">
<span class="stat-number">5+</span>
<span class="stat-label">Jahre Erfahrung</span>
</div>
<div class="stat-item">
<span class="stat-number">500+</span>
<span class="stat-label">Zufriedene Kunden</span>
</div>
<div class="stat-item">
<span class="stat-number">99.9%</span>
<span class="stat-label">Uptime</span>
</div>
<div class="stat-item">
<span class="stat-number">24/7</span>
<span class="stat-label">Support</span>
</div>
</div>
</div>
</div>
</section>
-->
<!-- Technology -->
<section class="technology"> <section class="technology">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -229,7 +185,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- CTA Section -->
<section class="cta"> <section class="cta">
<div class="container"> <div class="container">
<div class="cta-content glass-card"> <div class="cta-content glass-card">
@@ -245,6 +201,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -1,17 +1,17 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = 'Allgemeine Geschäftsbedingungen - HexaHost.de | AGB'; $page_title = 'Allgemeine Geschäftsbedingungen - HexaHost.de | AGB';
$page_description = 'Allgemeine Geschäftsbedingungen (AGB) von HexaHost.de für Hosting-Dienstleistungen.'; $page_description = 'Allgemeine Geschäftsbedingungen (AGB) von HexaHost.de für Hosting-Dienstleistungen.';
$current_page = 'agb'; $current_page = 'agb';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- AGB Hero -->
<section class="legal-hero"> <section class="legal-hero">
<div class="container"> <div class="container">
<div class="legal-hero-content"> <div class="legal-hero-content">
@@ -29,12 +29,12 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- AGB Content -->
<section class="legal-content"> <section class="legal-content">
<div class="container"> <div class="container">
<div class="legal-container"> <div class="legal-container">
<!-- § 1 Geltungsbereich -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 1 Geltungsbereich</h2> <h2>§ 1 Geltungsbereich</h2>
<div class="legal-block"> <div class="legal-block">
@@ -67,7 +67,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 2 Vertragsschluss -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 2 Vertragsschluss</h2> <h2>§ 2 Vertragsschluss</h2>
<div class="legal-block"> <div class="legal-block">
@@ -98,7 +98,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 3 Leistungsbeschreibung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 3 Leistungsbeschreibung</h2> <h2>§ 3 Leistungsbeschreibung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -129,7 +129,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 4 Preise und Zahlung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 4 Preise und Zahlungsbedingungen</h2> <h2>§ 4 Preise und Zahlungsbedingungen</h2>
<div class="legal-block"> <div class="legal-block">
@@ -168,7 +168,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 5 Vertragslaufzeit und Kündigung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 5 Vertragslaufzeit und Kündigung</h2> <h2>§ 5 Vertragslaufzeit und Kündigung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -207,7 +207,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 6 Widerrufsrecht für Verbraucher -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 6 Widerrufsrecht für Verbraucher</h2> <h2>§ 6 Widerrufsrecht für Verbraucher</h2>
<div class="legal-block"> <div class="legal-block">
@@ -252,7 +252,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 7 Pflichten des Kunden -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 7 Pflichten des Kunden</h2> <h2>§ 7 Pflichten des Kunden</h2>
<div class="legal-block"> <div class="legal-block">
@@ -286,7 +286,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 8 Verbotene Nutzung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 8 Verbotene Nutzung</h2> <h2>§ 8 Verbotene Nutzung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -320,7 +320,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 9 Sperrung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 9 Sperrung von Diensten</h2> <h2>§ 9 Sperrung von Diensten</h2>
<div class="legal-block"> <div class="legal-block">
@@ -350,7 +350,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 10 Haftung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 10 Haftung</h2> <h2>§ 10 Haftung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -384,7 +384,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 11 Datensicherung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 11 Datensicherung</h2> <h2>§ 11 Datensicherung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -406,7 +406,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 12 Datenschutz -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 12 Datenschutz</h2> <h2>§ 12 Datenschutz</h2>
<div class="legal-block"> <div class="legal-block">
@@ -430,7 +430,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 13 Geheimhaltung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 13 Geheimhaltung</h2> <h2>§ 13 Geheimhaltung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -455,7 +455,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 14 Änderungen der AGB -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 14 Änderungen der AGB</h2> <h2>§ 14 Änderungen der AGB</h2>
<div class="legal-block"> <div class="legal-block">
@@ -478,7 +478,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- § 15 Schlussbestimmungen -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>§ 15 Schlussbestimmungen</h2> <h2>§ 15 Schlussbestimmungen</h2>
<div class="legal-block"> <div class="legal-block">
@@ -506,7 +506,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Kontakt -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Kontakt bei Fragen</h2> <h2>Kontakt bei Fragen</h2>
<div class="legal-block"> <div class="legal-block">
@@ -527,6 +527,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -1,17 +1 @@
.btn-tertiary { .btn-tertiary{color:var(--text-primary);background:0 0;border:1px solid rgba(255,255,255,.25);transition:.3s}.btn-tertiary:hover{border-color:var(--primary-color);color:var(--primary-color);background:rgba(255,81,249,.08)}.it-services-actions{justify-content:center;margin-top:2rem}
color: var(--text-primary);
background: transparent;
border: 1px solid rgba(255, 255, 255, 0.25);
transition: all 0.3s ease;
}
.btn-tertiary:hover {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(255, 81, 249, 0.08);
}
.it-services-actions {
justify-content: center;
margin-top: 2rem;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

6
public/config/config.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
require_once __DIR__ . '/mail-config.php';
?>

View File

@@ -0,0 +1,64 @@
<?php
/**
* Zentrale Betreff-Konfiguration für das Kontaktformular
*/
/**
* @return array<string, string> Betreff-Schlüssel => Anzeigename
*/
function getContactSubjectMap(): array {
return [
'allgemeine-anfrage' => 'Allgemeine Anfrage',
'vpc-anfrage' => 'Virtual Private Container Anfrage',
'vps-anfrage' => 'Virtual Private Server Anfrage',
'mail-gateway-anfrage' => 'Mail Gateway Anfrage',
'webhosting-anfrage' => 'Webhosting Anfrage',
'it-beratung' => 'IT-Beratung',
'it-support' => 'IT-Support & Fehlerbehebung',
'netzwerk-wlan' => 'Netzwerk & WLAN-Einrichtung',
'it-sicherheit-backup' => 'IT-Sicherheit & Backup',
'webseiten-hosting-service' => 'Webseiten- & Hosting-Service',
'wartung-betreuung' => 'Wartung & Betreuung',
'support' => 'Technischer Support',
'beratung' => 'Persönliche Beratung',
'migration' => 'Migration/Umzug',
'sonstiges' => 'Sonstige Anfrage',
];
}
/**
* @param string $subjectKey
*/
function isAllowedContactSubject(string $subjectKey): bool {
return array_key_exists($subjectKey, getContactSubjectMap());
}
/**
* Betreff aus ?product= oder ?package= für die Kontaktseite ableiten
*/
function getPreselectedContactSubject(): string {
$productMap = [
'vpc' => 'vpc-anfrage',
'vps' => 'vps-anfrage',
'mail-gateway' => 'mail-gateway-anfrage',
'webhosting' => 'webhosting-anfrage',
];
if (!empty($_GET['product'])) {
$product = strtolower(preg_replace('/[^a-z0-9-]/', '', (string) $_GET['product']));
if (isset($productMap[$product])) {
return $productMap[$product];
}
}
if (!empty($_GET['package'])) {
$package = strtolower(preg_replace('/[^a-z0-9-]/', '', (string) $_GET['package']));
foreach ($productMap as $productId => $subjectKey) {
if (str_starts_with($package, $productId . '-')) {
return $subjectKey;
}
}
}
return '';
}

View File

@@ -0,0 +1,141 @@
<?php
define('SMTP_HOST', 'smtp.ihre-domain.de');
define('SMTP_PORT', 587);
define('SMTP_USERNAME', 'kontakt@ihre-domain.de');
define('SMTP_PASSWORD', 'ihr-smtp-passwort');
define('SMTP_FROM_EMAIL', 'kontakt@hexahost.de');
define('SMTP_TO_EMAIL', 'info@hexahost.de');
define('ENABLE_CSRF_PROTECTION', true);
define('ENABLE_RATE_LIMITING', true);
define('MAX_REQUESTS_PER_HOUR', 10);
define('ENABLE_SPAM_PROTECTION', true);
define('MAX_MESSAGE_LENGTH', 5000);
define('MIN_MESSAGE_LENGTH', 10);
define('DEBUG_MODE', false);
define('LOG_EMAILS', true);
define('ADDITIONAL_HEADERS', [
'X-Mailer' => 'HexaHost.de Contact Form',
'X-Priority' => '3',
'X-MSMail-Priority' => 'Normal',
'Importance' => 'Normal',
'X-Report-Abuse' => 'Please report abuse here: abuse@hexahost.de',
'List-Unsubscribe' => '<mailto:unsubscribe@hexahost.de>',
'Precedence' => 'bulk'
]);
define('ALLOWED_EMAIL_DOMAINS', [
]);
define('BLACKLISTED_EMAILS', [
]);
if (!defined('SMTP_HOST') || !defined('SMTP_USERNAME') || !defined('SMTP_PASSWORD')) {
die('SMTP-Konfiguration ist unvollständig. Bitte überprüfen Sie die mail-config.php');
}
if (!filter_var(SMTP_FROM_EMAIL, FILTER_VALIDATE_EMAIL)) {
die('Ungültige SMTP_FROM_EMAIL Adresse');
}
if (!filter_var(SMTP_TO_EMAIL, FILTER_VALIDATE_EMAIL)) {
die('Ungültige SMTP_TO_EMAIL Adresse');
}
function logEmail($type, $data) {
if (!LOG_EMAILS) return;
$logFile = __DIR__ . '/../logs/email.log';
$logDir = dirname($logFile);
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[$timestamp] $type: " . json_encode($data) . "\n";
file_put_contents($logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
function isValidEmail($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return false;
}
if (in_array($email, BLACKLISTED_EMAILS)) {
return false;
}
if (!empty(ALLOWED_EMAIL_DOMAINS)) {
$domain = substr(strrchr($email, "@"), 1);
if (!in_array($domain, ALLOWED_EMAIL_DOMAINS)) {
return false;
}
}
return true;
}
function getHexaHostConfig($key = null) {
$config = [
'smtp_host' => SMTP_HOST,
'smtp_port' => SMTP_PORT,
'smtp_username' => SMTP_USERNAME,
'smtp_password' => SMTP_PASSWORD,
'smtp_encryption' => 'tls',
'from_email' => SMTP_FROM_EMAIL,
'from_name' => 'HexaHost.de Kontaktformular',
'to_email' => SMTP_TO_EMAIL,
'to_name' => 'HexaHost Support',
'max_requests_per_hour' => MAX_REQUESTS_PER_HOUR,
'honeypot_field' => 'website',
'enable_csrf' => ENABLE_CSRF_PROTECTION,
'min_message_length' => MIN_MESSAGE_LENGTH,
'max_message_length' => MAX_MESSAGE_LENGTH,
'debug_mode' => DEBUG_MODE,
'log_errors' => LOG_EMAILS,
];
if ($key === null) {
return $config;
}
return $config[$key] ?? null;
}
?>

View File

@@ -0,0 +1,514 @@
<?php
$PRODUCTS['vpc'] = [
'name' => 'Virtual Private Container',
'short_name' => 'VPC',
'description' => 'Effiziente LXC-Container auf Proxmox-Basis',
'min_price' => '4,99',
'hero_highlight' => 'auf Proxmox LXC',
'hero_description' => 'Erleben Sie die Effizienz von Linux-Containern mit der Zuverlässigkeit von Proxmox. Unsere VPC-Lösungen bieten optimale Performance bei minimalem Ressourcenverbrauch.',
'packages_title' => 'VPC Pakete',
'packages_description' => 'Wählen Sie das perfekte Container-Paket für Ihre Anforderungen',
'cta_title' => 'Bereit für Ihren VPC?',
'cta_description' => 'Starten Sie noch heute mit einem Virtual Private Container',
'page_title' => 'Virtual Private Container - Effiziente LXC Container | HexaHost.de',
'page_description' => 'Virtual Private Container auf Proxmox LXC-Basis. Effiziente und preiswerte Container-Lösungen ab 4,99€/Monat bei HexaHost.de',
'packages' => [
'starter' => [
'name' => 'VPC Starter',
'price' => '4,99',
'featured' => false,
'specs' => [
['label' => 'CPU Kerne', 'value' => '1 vCore'],
['label' => 'RAM', 'value' => '1 GB'],
['label' => 'SSD Speicher', 'value' => '20 GB'],
['label' => 'Traffic', 'value' => '1 TB'],
['label' => 'IPv4 Adressen', 'value' => '1'],
],
'features' => [
'Proxmox LXC Container',
'Root-Zugriff',
'SSH-Zugang',
'Backup inklusive',
'24/7 Monitoring',
],
],
'business' => [
'name' => 'VPC Business',
'price' => '9,99',
'featured' => true,
'specs' => [
['label' => 'CPU Kerne', 'value' => '2 vCores'],
['label' => 'RAM', 'value' => '4 GB'],
['label' => 'SSD Speicher', 'value' => '80 GB'],
['label' => 'Traffic', 'value' => '3 TB'],
['label' => 'IPv4 Adressen', 'value' => '1'],
],
'features' => [
'Proxmox LXC Container',
'Root-Zugriff',
'SSH-Zugang',
'Tägliches Backup',
'24/7 Monitoring',
'Snapshot-Funktion',
],
],
'professional' => [
'name' => 'VPC Professional',
'price' => '19,99',
'featured' => false,
'specs' => [
['label' => 'CPU Kerne', 'value' => '4 vCores'],
['label' => 'RAM', 'value' => '8 GB'],
['label' => 'SSD Speicher', 'value' => '160 GB'],
['label' => 'Traffic', 'value' => '5 TB'],
['label' => 'IPv4 Adressen', 'value' => '2'],
],
'features' => [
'Proxmox LXC Container',
'Root-Zugriff',
'SSH-Zugang',
'Stündliches Backup',
'24/7 Monitoring',
'Snapshot-Funktion',
'Priority Support',
],
],
'enterprise' => [
'name' => 'VPC Enterprise',
'price' => '39,99',
'featured' => false,
'specs' => [
['label' => 'CPU Kerne', 'value' => '8 vCores'],
['label' => 'RAM', 'value' => '16 GB'],
['label' => 'SSD Speicher', 'value' => '320 GB'],
['label' => 'Traffic', 'value' => '10 TB'],
['label' => 'IPv4 Adressen', 'value' => '3'],
],
'features' => [
'Proxmox LXC Container',
'Root-Zugriff',
'SSH-Zugang',
'Stündliches Backup',
'24/7 Monitoring',
'Snapshot-Funktion',
'Priority Support',
'Individuelle Konfiguration',
],
],
],
];
$PRODUCTS['vps'] = [
'name' => 'Virtual Private Server',
'short_name' => 'VPS',
'description' => 'Vollwertige KVM-Virtualisierung mit Root-Zugriff',
'min_price' => '9,99',
'hero_highlight' => 'auf Proxmox KVM',
'hero_description' => 'Maximale Flexibilität und Kontrolle mit vollwertiger KVM-Virtualisierung. Installieren Sie jedes Betriebssystem und genießen Sie vollständigen Root-Zugriff.',
'packages_title' => 'VPS Pakete',
'packages_description' => 'Wählen Sie das perfekte VPS-Paket für Ihre Anforderungen',
'cta_title' => 'Bereit für Ihren VPS?',
'cta_description' => 'Starten Sie noch heute mit einem Virtual Private Server',
'page_title' => 'Virtual Private Server - KVM Virtualisierung | HexaHost.de',
'page_description' => 'Virtual Private Server auf Proxmox KVM-Basis. Vollwertige Virtualisierung mit Root-Zugriff ab 9,99€/Monat bei HexaHost.de',
'packages' => [
'starter' => [
'name' => 'VPS Starter',
'price' => '9,99',
'featured' => false,
'specs' => [
['label' => 'CPU Kerne', 'value' => '1 vCore'],
['label' => 'RAM', 'value' => '2 GB'],
['label' => 'SSD Speicher', 'value' => '40 GB'],
['label' => 'Traffic', 'value' => '2 TB'],
['label' => 'IPv4 Adressen', 'value' => '1'],
],
'features' => [
'Proxmox KVM Virtualisierung',
'Root-Zugriff',
'SSH-Zugang',
'Backup inklusive',
'24/7 Monitoring',
],
],
'business' => [
'name' => 'VPS Business',
'price' => '19,99',
'featured' => true,
'specs' => [
['label' => 'CPU Kerne', 'value' => '2 vCores'],
['label' => 'RAM', 'value' => '4 GB'],
['label' => 'SSD Speicher', 'value' => '80 GB'],
['label' => 'Traffic', 'value' => '4 TB'],
['label' => 'IPv4 Adressen', 'value' => '1'],
],
'features' => [
'Proxmox KVM Virtualisierung',
'Root-Zugriff',
'SSH-Zugang',
'Tägliches Backup',
'24/7 Monitoring',
'Snapshot-Funktion',
],
],
'professional' => [
'name' => 'VPS Professional',
'price' => '39,99',
'featured' => false,
'specs' => [
['label' => 'CPU Kerne', 'value' => '4 vCores'],
['label' => 'RAM', 'value' => '8 GB'],
['label' => 'SSD Speicher', 'value' => '160 GB'],
['label' => 'Traffic', 'value' => '8 TB'],
['label' => 'IPv4 Adressen', 'value' => '2'],
],
'features' => [
'Proxmox KVM Virtualisierung',
'Root-Zugriff',
'SSH-Zugang',
'Stündliches Backup',
'24/7 Monitoring',
'Snapshot-Funktion',
'Priority Support',
],
],
'enterprise' => [
'name' => 'VPS Enterprise',
'price' => '79,99',
'featured' => false,
'specs' => [
['label' => 'CPU Kerne', 'value' => '8 vCores'],
['label' => 'RAM', 'value' => '16 GB'],
['label' => 'SSD Speicher', 'value' => '320 GB'],
['label' => 'Traffic', 'value' => '15 TB'],
['label' => 'IPv4 Adressen', 'value' => '3'],
],
'features' => [
'Proxmox KVM Virtualisierung',
'Root-Zugriff',
'SSH-Zugang',
'Stündliches Backup',
'24/7 Monitoring',
'Snapshot-Funktion',
'Priority Support',
'Individuelle Konfiguration',
],
],
],
];
$PRODUCTS['mail-gateway'] = [
'name' => 'Mail Gateway',
'short_name' => 'Mail',
'description' => 'Professioneller E-Mail-Schutz für Unternehmen',
'min_price' => '4,99',
'hero_highlight' => 'für Unternehmen',
'hero_description' => 'Professionelle E-Mail-Infrastruktur mit maximalem Schutz vor Spam und Malware. Sichern Sie Ihre geschäftliche Kommunikation mit unseren Mail Gateway Lösungen.',
'packages_title' => 'Mail Gateway Pakete',
'packages_description' => 'Wählen Sie das passende Mail Gateway Paket für Ihr Unternehmen',
'cta_title' => 'Bereit für professionelle E-Mail-Kommunikation?',
'cta_description' => 'Starten Sie noch heute mit unserem Mail Gateway',
'page_title' => 'Mail Gateway - Professionelle E-Mail-Lösungen | HexaHost.de',
'page_description' => 'Professionelle Mail Gateway Lösungen für Unternehmen. Spam-Schutz, E-Mail-Archivierung und sichere E-Mail-Kommunikation bei HexaHost.de',
'packages' => [
'starter' => [
'name' => 'Mail Starter',
'price' => '4,99',
'featured' => false,
'specs' => [
['label' => 'Postfächer', 'value' => '5'],
['label' => 'Speicher/Postfach', 'value' => '5 GB'],
['label' => 'Domains', 'value' => '1'],
['label' => 'E-Mails/Tag', 'value' => '500'],
],
'features' => [
'Spam-Filter',
'Virus-Schutz',
'Webmail',
'IMAP/POP3',
'SSL/TLS Verschlüsselung',
],
],
'business' => [
'name' => 'Mail Business',
'price' => '14,99',
'featured' => true,
'specs' => [
['label' => 'Postfächer', 'value' => '25'],
['label' => 'Speicher/Postfach', 'value' => '10 GB'],
['label' => 'Domains', 'value' => '3'],
['label' => 'E-Mails/Tag', 'value' => '2.000'],
],
'features' => [
'Spam-Filter (erweitert)',
'Virus-Schutz',
'Webmail',
'IMAP/POP3',
'SSL/TLS Verschlüsselung',
'E-Mail Archivierung (30 Tage)',
'Kalender & Kontakte',
],
],
'professional' => [
'name' => 'Mail Professional',
'price' => '29,99',
'featured' => false,
'specs' => [
['label' => 'Postfächer', 'value' => '100'],
['label' => 'Speicher/Postfach', 'value' => '25 GB'],
['label' => 'Domains', 'value' => '10'],
['label' => 'E-Mails/Tag', 'value' => '10.000'],
],
'features' => [
'Spam-Filter (KI-gestützt)',
'Virus-Schutz',
'Webmail',
'IMAP/POP3',
'SSL/TLS Verschlüsselung',
'E-Mail Archivierung (1 Jahr)',
'Kalender & Kontakte',
'ActiveSync für Mobile',
],
],
'enterprise' => [
'name' => 'Mail Enterprise',
'price' => '59,99',
'featured' => false,
'specs' => [
['label' => 'Postfächer', 'value' => 'Unbegrenzt'],
['label' => 'Speicher/Postfach', 'value' => '50 GB'],
['label' => 'Domains', 'value' => 'Unbegrenzt'],
['label' => 'E-Mails/Tag', 'value' => 'Unbegrenzt'],
],
'features' => [
'Spam-Filter (KI-gestützt)',
'Virus-Schutz',
'Webmail',
'IMAP/POP3',
'SSL/TLS Verschlüsselung',
'E-Mail Archivierung (10 Jahre)',
'Kalender & Kontakte',
'ActiveSync für Mobile',
'Dedizierte IP',
'Priority Support',
],
],
],
];
$PRODUCTS['webhosting'] = [
'name' => 'Webhosting',
'short_name' => 'Webhosting',
'description' => 'Klassisches Hosting mit PHP, MySQL und SSL',
'min_price' => '1,99',
'hero_highlight' => 'Alles für Ihre Website',
'hero_description' => 'Klassisches Webhosting mit allem, was Sie für eine erfolgreiche Website benötigen. PHP, MySQL, SSL-Zertifikate und E-Mail-Postfächer - alles inklusive.',
'packages_title' => 'Webhosting Pakete',
'packages_description' => 'Von der ersten Website bis zum professionellen Online-Shop',
'cta_title' => 'Bereit für Ihr Webhosting?',
'cta_description' => 'Starten Sie noch heute mit professionellem Webhosting',
'page_title' => 'Webhosting - Klassisches Hosting für Websites | HexaHost.de',
'page_description' => 'Webhosting mit PHP, MySQL und SSL-Zertifikaten. Klassisches Hosting für Websites ab 1,99€/Monat bei HexaHost.de',
'packages' => [
'starter' => [
'name' => 'Webhosting Starter',
'price' => '1,99',
'featured' => false,
'specs' => [
['label' => 'Webspace', 'value' => '5 GB'],
['label' => 'Domains', 'value' => '1'],
['label' => 'E-Mail-Postfächer', 'value' => '5'],
['label' => 'Datenbanken', 'value' => '1 MySQL'],
['label' => 'Traffic', 'value' => '10 GB'],
],
'features' => [
'cPanel/Webmin',
'PHP 8.1',
'SSL-Zertifikat',
'E-Mail-Postfächer',
'MySQL Datenbank',
],
],
'business' => [
'name' => 'Webhosting Business',
'price' => '4,99',
'featured' => true,
'specs' => [
['label' => 'Webspace', 'value' => '20 GB'],
['label' => 'Domains', 'value' => '5'],
['label' => 'E-Mail-Postfächer', 'value' => '25'],
['label' => 'Datenbanken', 'value' => '5 MySQL'],
['label' => 'Traffic', 'value' => '50 GB'],
],
'features' => [
'cPanel/Webmin',
'PHP 8.1',
'SSL-Zertifikat',
'E-Mail-Postfächer',
'MySQL Datenbanken',
'Backup-Service',
],
],
'professional' => [
'name' => 'Webhosting Professional',
'price' => '9,99',
'featured' => false,
'specs' => [
['label' => 'Webspace', 'value' => '50 GB'],
['label' => 'Domains', 'value' => 'Unbegrenzt'],
['label' => 'E-Mail-Postfächer', 'value' => '100'],
['label' => 'Datenbanken', 'value' => 'Unbegrenzt'],
['label' => 'Traffic', 'value' => '200 GB'],
],
'features' => [
'cPanel/Webmin',
'PHP 8.1',
'SSL-Zertifikat',
'E-Mail-Postfächer',
'MySQL Datenbanken',
'Backup-Service',
'Priority Support',
],
],
'enterprise' => [
'name' => 'Webhosting Enterprise',
'price' => '19,99',
'featured' => false,
'specs' => [
['label' => 'Webspace', 'value' => '100 GB'],
['label' => 'Domains', 'value' => 'Unbegrenzt'],
['label' => 'E-Mail-Postfächer', 'value' => 'Unbegrenzt'],
['label' => 'Datenbanken', 'value' => 'Unbegrenzt'],
['label' => 'Traffic', 'value' => '500 GB'],
],
'features' => [
'cPanel/Webmin',
'PHP 8.1',
'SSL-Zertifikat',
'E-Mail-Postfächer',
'MySQL Datenbanken',
'Backup-Service',
'Priority Support',
'Individuelle Konfiguration',
],
],
],
];
function getAllProducts() {
global $PRODUCTS;
return $PRODUCTS;
}
function getProduct($productId) {
global $PRODUCTS;
return $PRODUCTS[$productId] ?? null;
}
function getProductPackages($productId) {
global $PRODUCTS;
return $PRODUCTS[$productId]['packages'] ?? [];
}
function getPackage($productId, $packageId) {
global $PRODUCTS;
return $PRODUCTS[$productId]['packages'][$packageId] ?? null;
}
function getPackagePrice($productId, $packageId) {
$package = getPackage($productId, $packageId);
return $package['price'] ?? null;
}
function getMinPrice($productId) {
global $PRODUCTS;
return $PRODUCTS[$productId]['min_price'] ?? null;
}
function formatPrice($price, $withCurrency = true) {
return $withCurrency ? $price . '€' : $price;
}
function renderPackageCard($productId, $packageId, $package) {
$featuredClass = $package['featured'] ? ' featured' : '';
$featuredBadge = $package['featured'] ? '<div class="featured-badge">Beliebt</div>' : '';
$specsHtml = '';
foreach ($package['specs'] as $spec) {
$specsHtml .= sprintf(
'<div class="spec-item"><span class="spec-label">%s:</span><span class="spec-value">%s</span></div>',
htmlspecialchars($spec['label']),
htmlspecialchars($spec['value'])
);
}
$featuresHtml = '';
foreach ($package['features'] as $feature) {
$featuresHtml .= sprintf('<div class="feature">✓ %s</div>', htmlspecialchars($feature));
}
return sprintf('
<div class="package-card glass-card%s">
%s
<div class="package-header">
<h3 class="package-name">%s</h3>
<div class="package-price">
<span class="price">%s€</span>
<span class="period">/Monat</span>
</div>
</div>
<div class="package-specs">
%s
</div>
<div class="package-features">
%s
</div>
<a href="contact.php?package=%s-%s" class="btn btn-primary">Jetzt bestellen</a>
</div>',
$featuredClass,
$featuredBadge,
htmlspecialchars($package['name']),
$package['price'],
$specsHtml,
$featuresHtml,
$productId,
$packageId
);
}
function renderAllPackages($productId) {
$packages = getProductPackages($productId);
$html = '';
foreach ($packages as $packageId => $package) {
$html .= renderPackageCard($productId, $packageId, $package);
}
return $html;
}
?>

View File

@@ -4,55 +4,28 @@
* E-Mail-Verarbeitung mit SMTP-Integration und Spam-Schutz * E-Mail-Verarbeitung mit SMTP-Integration und Spam-Schutz
*/ */
// Session starten für CSRF-Validierung require_once __DIR__ . '/../backend/includes/functions.php';
if (session_status() === PHP_SESSION_NONE) { require_once __DIR__ . '/../backend/config/mail-config.php';
session_start(); require_once __DIR__ . '/../backend/config/contact-config.php';
}
// Konfiguration laden
require_once 'config/mail-config.php';
// PHPMailer Autoload (falls via Composer installiert) // PHPMailer Autoload (falls via Composer installiert)
if (file_exists(__DIR__ . '/vendor/autoload.php')) { if (file_exists(__DIR__ . '/vendor/autoload.php')) {
require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/vendor/autoload.php';
} }
// Konfiguration verwenden
$config = getHexaHostConfig(); $config = getHexaHostConfig();
// Betreff-Mapping (zentral definiert)
const SUBJECT_MAP = [
'allgemeine-anfrage' => 'Allgemeine Anfrage',
'vpc-anfrage' => 'Virtual Private Container Anfrage',
'vps-anfrage' => 'Virtual Private Server Anfrage',
'mail-gateway-anfrage' => 'Mail Gateway Anfrage',
'webhosting-anfrage' => 'Webhosting Anfrage',
'support' => 'Technischer Support',
'beratung' => 'Persönliche Beratung',
'migration' => 'Migration/Umzug',
'sonstiges' => 'Sonstige Anfrage'
];
// CSRF-Token validieren und invalidieren (verhindert Replay-Attacks)
function validateCSRFToken($token) {
if (isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token)) {
// Token nach erfolgreicher Validierung invalidieren
unset($_SESSION['csrf_token']);
return true;
}
return false;
}
// CORS Headers für AJAX-Requests (nur eigene Domain erlauben) // CORS Headers für AJAX-Requests (nur eigene Domain erlauben)
$allowed_origins = [ $allowed_origins = [
'https://hexahost.de', 'https://hexahost.de',
'https://www.hexahost.de', 'https://www.hexahost.de',
'http://localhost', // Für Entwicklung 'https://dev.hexahost.de',
'http://127.0.0.1' // Für Entwicklung 'http://localhost',
'http://127.0.0.1',
]; ];
$origin = $_SERVER['HTTP_ORIGIN'] ?? ''; $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
if (in_array($origin, $allowed_origins)) { if (in_array($origin, $allowed_origins, true)) {
header('Access-Control-Allow-Origin: ' . $origin); header('Access-Control-Allow-Origin: ' . $origin);
} }
@@ -60,104 +33,82 @@ header('Access-Control-Allow-Methods: POST');
header('Access-Control-Allow-Headers: Content-Type'); header('Access-Control-Allow-Headers: Content-Type');
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
// Nur POST-Requests erlauben
if ($_SERVER['REQUEST_METHOD'] !== 'POST') { if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405); http_response_code(405);
echo json_encode(['success' => false, 'message' => 'Method not allowed']); echo json_encode(['success' => false, 'message' => 'Method not allowed']);
exit; exit;
} }
// Rate Limiting
function checkRateLimit($ip) { function checkRateLimit($ip) {
global $config; global $config;
$cache_file = sys_get_temp_dir() . '/hexahost_contact_' . md5($ip) . '.txt'; $cache_file = sys_get_temp_dir() . '/hexahost_contact_' . md5($ip) . '.txt';
$current_time = time(); $current_time = time();
$data = ['requests' => []];
if (file_exists($cache_file)) {
$data = json_decode(file_get_contents($cache_file), true); $handle = @fopen($cache_file, 'c+');
if ($data && isset($data['requests'])) { if ($handle === false) {
// Entferne alte Einträge (älter als 1 Stunde) return true;
$data['requests'] = array_filter($data['requests'], function($timestamp) use ($current_time) { }
return ($current_time - $timestamp) < 3600;
}); try {
if (!flock($handle, LOCK_EX)) {
if (count($data['requests']) >= $config['max_requests_per_hour']) { return true;
return false; }
$contents = stream_get_contents($handle);
if ($contents !== false && $contents !== '') {
$decoded = json_decode($contents, true);
if (is_array($decoded) && isset($decoded['requests'])) {
$data = $decoded;
} }
} }
$data['requests'] = array_values(array_filter(
$data['requests'],
static fn($timestamp) => ($current_time - (int) $timestamp) < 3600
));
if (count($data['requests']) >= $config['max_requests_per_hour']) {
return false;
}
$data['requests'][] = $current_time;
ftruncate($handle, 0);
rewind($handle);
fwrite($handle, json_encode($data));
} finally {
flock($handle, LOCK_UN);
fclose($handle);
} }
// Füge aktuellen Request hinzu
$data = isset($data) ? $data : ['requests' => []];
$data['requests'][] = $current_time;
file_put_contents($cache_file, json_encode($data));
return true; return true;
} }
// Honeypot Check
function checkHoneypot($data) { function checkHoneypot($data) {
global $config; global $config;
$honeypot_field = $config['honeypot_field']; $honeypot_field = $config['honeypot_field'];
return empty($data[$honeypot_field]);
// Das Honeypot-Feld sollte leer sein (verstecktes Feld)
if (!empty($data[$honeypot_field])) {
return false;
}
return true;
} }
// E-Mail-Validierung function sanitizeFormField($input) {
function validateEmail($email) { return strip_tags(trim((string) $input));
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
} }
// Input-Sanitization function getSubjectLabel($subjectKey) {
function sanitizeInput($input) { $map = getContactSubjectMap();
return htmlspecialchars(strip_tags(trim($input)), ENT_QUOTES, 'UTF-8'); return $map[$subjectKey] ?? 'Neue Kontaktanfrage';
} }
// Sichere IP-Adressen-Erkennung (auch hinter Proxies/Cloudflare)
function getClientIP() {
$ip_keys = [
'HTTP_CF_CONNECTING_IP', // Cloudflare
'HTTP_X_FORWARDED_FOR', // Proxy
'HTTP_X_REAL_IP', // Nginx Proxy
'REMOTE_ADDR' // Standard
];
foreach ($ip_keys as $key) {
if (!empty($_SERVER[$key])) {
// Bei X-Forwarded-For kann eine Liste von IPs kommen
$ip = explode(',', $_SERVER[$key])[0];
$ip = trim($ip);
// Validiere IP-Format
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
}
// Fallback auf REMOTE_ADDR (auch private IPs für lokale Entwicklung)
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
// SMTP E-Mail-Versand mit PHPMailer
function sendEmail($data) { function sendEmail($data) {
global $config; global $config;
// PHPMailer laden (falls verfügbar)
if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) { if (!class_exists('PHPMailer\PHPMailer\PHPMailer')) {
// Fallback: Native PHP mail() Funktion
return sendEmailNative($data); return sendEmailNative($data);
} }
try { try {
$mail = new PHPMailer\PHPMailer\PHPMailer(true); $mail = new PHPMailer\PHPMailer\PHPMailer(true);
// Server-Einstellungen
$mail->isSMTP(); $mail->isSMTP();
$mail->Host = $config['smtp_host']; $mail->Host = $config['smtp_host'];
$mail->SMTPAuth = true; $mail->SMTPAuth = true;
@@ -166,73 +117,60 @@ function sendEmail($data) {
$mail->SMTPSecure = $config['smtp_encryption']; $mail->SMTPSecure = $config['smtp_encryption'];
$mail->Port = $config['smtp_port']; $mail->Port = $config['smtp_port'];
$mail->CharSet = 'UTF-8'; $mail->CharSet = 'UTF-8';
// Absender
$mail->setFrom($config['from_email'], $config['from_name']); $mail->setFrom($config['from_email'], $config['from_name']);
$mail->addReplyTo($data['email'], $data['firstName'] . ' ' . $data['lastName']); $mail->addReplyTo(
sanitizeHeaderValue($data['email']),
// Empfänger sanitizeHeaderValue($data['firstName'] . ' ' . $data['lastName'])
);
$mail->addAddress($config['to_email'], $config['to_name']); $mail->addAddress($config['to_email'], $config['to_name']);
// Betreff (nutzt zentrale SUBJECT_MAP Konstante) $subject = getSubjectLabel($data['subject']);
$subject = SUBJECT_MAP[$data['subject']] ?? 'Neue Kontaktanfrage';
$mail->Subject = '[HexaHost.de] ' . $subject; $mail->Subject = '[HexaHost.de] ' . $subject;
// HTML E-Mail-Inhalt
$html_content = generateEmailHTML($data);
$mail->isHTML(true); $mail->isHTML(true);
$mail->Body = $html_content; $mail->Body = generateEmailHTML($data);
$mail->AltBody = generateEmailText($data); $mail->AltBody = generateEmailText($data);
// Anti-Spam Headers
$mail->addCustomHeader('X-Mailer', 'HexaHost Contact Form'); $mail->addCustomHeader('X-Mailer', 'HexaHost Contact Form');
$mail->addCustomHeader('X-Priority', '3'); $mail->addCustomHeader('X-Priority', '3');
$mail->addCustomHeader('X-MSMail-Priority', 'Normal'); $mail->addCustomHeader('X-MSMail-Priority', 'Normal');
$mail->addCustomHeader('Importance', 'Normal'); $mail->addCustomHeader('Importance', 'Normal');
$mail->addCustomHeader('X-Report-Abuse', 'Please report abuse here: abuse@hexahost.de'); $mail->addCustomHeader('X-Report-Abuse', 'Please report abuse here: abuse@hexahost.de');
// DKIM, SPF, DMARC werden über DNS konfiguriert
$mail->send(); $mail->send();
return true; return true;
} catch (Exception $e) { } catch (Exception $e) {
error_log('HexaHost Contact Form Error: ' . $e->getMessage()); error_log('HexaHost Contact Form Error: ' . $e->getMessage());
return false; return false;
} }
} }
// Fallback: Native PHP mail() Funktion
function sendEmailNative($data) { function sendEmailNative($data) {
global $config; global $config;
// Betreff (nutzt zentrale SUBJECT_MAP Konstante) $subject = '[HexaHost.de] ' . getSubjectLabel($data['subject']);
$subject = SUBJECT_MAP[$data['subject']] ?? 'Neue Kontaktanfrage'; $replyName = sanitizeHeaderValue($data['firstName'] . ' ' . $data['lastName']);
$subject = '[HexaHost.de] ' . $subject; $replyEmail = sanitizeHeaderValue($data['email']);
// Headers für Spam-Schutz
$headers = [ $headers = [
'From: ' . $config['from_name'] . ' <' . $config['from_email'] . '>', 'From: ' . $config['from_name'] . ' <' . $config['from_email'] . '>',
'Reply-To: ' . $data['firstName'] . ' ' . $data['lastName'] . ' <' . $data['email'] . '>', 'Reply-To: ' . $replyName . ' <' . $replyEmail . '>',
'MIME-Version: 1.0', 'MIME-Version: 1.0',
'Content-Type: text/html; charset=UTF-8', 'Content-Type: text/html; charset=UTF-8',
'X-Mailer: HexaHost Contact Form', 'X-Mailer: HexaHost Contact Form',
'X-Priority: 3', 'X-Priority: 3',
'X-MSMail-Priority: Normal', 'X-MSMail-Priority: Normal',
'Importance: Normal', 'Importance: Normal',
'X-Report-Abuse: Please report abuse here: abuse@hexahost.de' 'X-Report-Abuse: Please report abuse here: abuse@hexahost.de',
]; ];
$message = generateEmailHTML($data); return mail($config['to_email'], $subject, generateEmailHTML($data), implode("\r\n", $headers));
return mail($config['to_email'], $subject, $message, implode("\r\n", $headers));
} }
// HTML E-Mail-Template
function generateEmailHTML($data) { function generateEmailHTML($data) {
// Betreff (nutzt zentrale SUBJECT_MAP Konstante) $subject_text = htmlspecialchars(getSubjectLabel($data['subject']), ENT_QUOTES, 'UTF-8');
$subject_text = SUBJECT_MAP[$data['subject']] ?? 'Neue Kontaktanfrage';
$html = ' $html = '
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
@@ -267,39 +205,39 @@ function generateEmailHTML($data) {
<div class="field"> <div class="field">
<div class="label">Name:</div> <div class="label">Name:</div>
<div class="value">' . $data['firstName'] . ' ' . $data['lastName'] . '</div> <div class="value">' . htmlspecialchars($data['firstName'] . ' ' . $data['lastName'], ENT_QUOTES, 'UTF-8') . '</div>
</div> </div>
<div class="field"> <div class="field">
<div class="label">E-Mail:</div> <div class="label">E-Mail:</div>
<div class="value">' . $data['email'] . '</div> <div class="value">' . htmlspecialchars($data['email'], ENT_QUOTES, 'UTF-8') . '</div>
</div>'; </div>';
if (!empty($data['phone'])) { if (!empty($data['phone'])) {
$html .= ' $html .= '
<div class="field"> <div class="field">
<div class="label">Telefon:</div> <div class="label">Telefon:</div>
<div class="value">' . $data['phone'] . '</div> <div class="value">' . htmlspecialchars($data['phone'], ENT_QUOTES, 'UTF-8') . '</div>
</div>'; </div>';
} }
if (!empty($data['company'])) { if (!empty($data['company'])) {
$html .= ' $html .= '
<div class="field"> <div class="field">
<div class="label">Unternehmen:</div> <div class="label">Unternehmen:</div>
<div class="value">' . $data['company'] . '</div> <div class="value">' . htmlspecialchars($data['company'], ENT_QUOTES, 'UTF-8') . '</div>
</div>'; </div>';
} }
$html .= ' $html .= '
<div class="field"> <div class="field">
<div class="label">Nachricht:</div> <div class="label">Nachricht:</div>
<div class="message">' . nl2br($data['message']) . '</div> <div class="message">' . nl2br(htmlspecialchars($data['message'], ENT_QUOTES, 'UTF-8')) . '</div>
</div> </div>
<div class="field"> <div class="field">
<div class="label">IP-Adresse:</div> <div class="label">IP-Adresse:</div>
<div class="value">' . htmlspecialchars(getClientIP()) . '</div> <div class="value">' . htmlspecialchars(getClientIP(), ENT_QUOTES, 'UTF-8') . '</div>
</div> </div>
<div class="field"> <div class="field">
@@ -315,138 +253,159 @@ function generateEmailHTML($data) {
</div> </div>
</body> </body>
</html>'; </html>';
return $html; return $html;
} }
// Text-Version der E-Mail
function generateEmailText($data) { function generateEmailText($data) {
// Betreff (nutzt zentrale SUBJECT_MAP Konstante)
$subject_text = SUBJECT_MAP[$data['subject']] ?? 'Neue Kontaktanfrage';
$text = "NEUE KONTAKTANFRAGE - HexaHost.de\n"; $text = "NEUE KONTAKTANFRAGE - HexaHost.de\n";
$text .= "=====================================\n\n"; $text .= "=====================================\n\n";
$text .= "Betreff: " . $subject_text . "\n"; $text .= "Betreff: " . getSubjectLabel($data['subject']) . "\n";
$text .= "Name: " . $data['firstName'] . " " . $data['lastName'] . "\n"; $text .= "Name: " . $data['firstName'] . " " . $data['lastName'] . "\n";
$text .= "E-Mail: " . $data['email'] . "\n"; $text .= "E-Mail: " . $data['email'] . "\n";
if (!empty($data['phone'])) { if (!empty($data['phone'])) {
$text .= "Telefon: " . $data['phone'] . "\n"; $text .= "Telefon: " . $data['phone'] . "\n";
} }
if (!empty($data['company'])) { if (!empty($data['company'])) {
$text .= "Unternehmen: " . $data['company'] . "\n"; $text .= "Unternehmen: " . $data['company'] . "\n";
} }
$text .= "\nNachricht:\n"; $text .= "\nNachricht:\n";
$text .= "----------\n"; $text .= "----------\n";
$text .= $data['message'] . "\n\n"; $text .= $data['message'] . "\n\n";
$text .= "IP-Adresse: " . getClientIP() . "\n"; $text .= "IP-Adresse: " . getClientIP() . "\n";
$text .= "Zeitstempel: " . date('d.m.Y H:i:s') . "\n\n"; $text .= "Zeitstempel: " . date('d.m.Y H:i:s') . "\n\n";
$text .= "---\n"; $text .= "---\n";
$text .= "Diese E-Mail wurde automatisch vom HexaHost.de Kontaktformular generiert.\n"; $text .= "Diese E-Mail wurde automatisch vom HexaHost.de Kontaktformular generiert.\n";
$text .= "© " . date('Y') . " HexaHost.de - Alle Rechte vorbehalten"; $text .= "© " . date('Y') . " HexaHost.de - Alle Rechte vorbehalten";
return $text; return $text;
} }
// Hauptverarbeitung
try { try {
// CSRF-Token validieren if (!empty($config['enable_csrf'])) {
if (empty($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) { if (empty($_POST['csrf_token']) || !validateCSRFToken($_POST['csrf_token'])) {
http_response_code(403); http_response_code(403);
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => 'Ungültige Sitzung. Bitte laden Sie die Seite neu und versuchen Sie es erneut.' 'message' => 'Ungültige Sitzung. Bitte laden Sie die Seite neu und versuchen Sie es erneut.',
]); ]);
exit; exit;
}
} }
// Rate Limiting Check if (!checkRateLimit(getClientIP())) {
$client_ip = getClientIP();
if (!checkRateLimit($client_ip)) {
http_response_code(429); http_response_code(429);
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.' 'message' => 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.',
]); ]);
exit; exit;
} }
// Honeypot Check
if (!checkHoneypot($_POST)) { if (!checkHoneypot($_POST)) {
http_response_code(400); http_response_code(400);
echo json_encode([ echo json_encode(['success' => false, 'message' => 'Ungültige Anfrage.']);
'success' => false,
'message' => 'Ungültige Anfrage.'
]);
exit; exit;
} }
// Pflichtfelder prüfen
$required_fields = ['firstName', 'lastName', 'email', 'subject', 'message', 'privacy']; $required_fields = ['firstName', 'lastName', 'email', 'subject', 'message', 'privacy'];
$missing_fields = []; $missing_fields = [];
foreach ($required_fields as $field) { foreach ($required_fields as $field) {
if (empty($_POST[$field])) { if (empty($_POST[$field])) {
$missing_fields[] = $field; $missing_fields[] = $field;
} }
} }
if (!empty($missing_fields)) { if (!empty($missing_fields)) {
http_response_code(400); http_response_code(400);
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => 'Bitte füllen Sie alle Pflichtfelder aus.', 'message' => 'Bitte füllen Sie alle Pflichtfelder aus.',
'missing_fields' => $missing_fields 'missing_fields' => $missing_fields,
]); ]);
exit; exit;
} }
// E-Mail-Validierung $subjectKey = trim((string) $_POST['subject']);
if (!validateEmail($_POST['email'])) { if (!isAllowedContactSubject($subjectKey)) {
http_response_code(400); http_response_code(400);
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => 'Bitte geben Sie eine gültige E-Mail-Adresse ein.' 'message' => 'Bitte wählen Sie einen gültigen Betreff.',
]); ]);
exit; exit;
} }
// Daten sanitieren $email = trim((string) $_POST['email']);
$data = [ if (!isValidEmail($email)) {
'firstName' => sanitizeInput($_POST['firstName']), http_response_code(400);
'lastName' => sanitizeInput($_POST['lastName']),
'email' => sanitizeInput($_POST['email']),
'phone' => sanitizeInput($_POST['phone'] ?? ''),
'company' => sanitizeInput($_POST['company'] ?? ''),
'subject' => sanitizeInput($_POST['subject']),
'message' => sanitizeInput($_POST['message']),
'privacy' => isset($_POST['privacy']) ? true : false
];
// E-Mail senden
if (sendEmail($data)) {
echo json_encode([ echo json_encode([
'success' => true, 'success' => false,
'message' => 'Ihre Nachricht wurde erfolgreich gesendet! Wir melden uns in Kürze bei Ihnen.' 'message' => 'Bitte geben Sie eine gültige E-Mail-Adresse ein.',
]);
exit;
}
$message = trim((string) $_POST['message']);
$messageLength = mb_strlen($message, 'UTF-8');
if ($messageLength < $config['min_message_length']) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'Ihre Nachricht ist zu kurz.',
]);
exit;
}
if ($messageLength > $config['max_message_length']) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'Ihre Nachricht ist zu lang.',
]);
exit;
}
$data = [
'firstName' => sanitizeFormField($_POST['firstName']),
'lastName' => sanitizeFormField($_POST['lastName']),
'email' => sanitizeHeaderValue($email),
'phone' => sanitizeFormField($_POST['phone'] ?? ''),
'company' => sanitizeFormField($_POST['company'] ?? ''),
'subject' => $subjectKey,
'message' => sanitizeFormField($message),
'privacy' => isset($_POST['privacy']),
];
if (sendEmail($data)) {
if (LOG_EMAILS) {
logEmail('sent', [
'subject' => $subjectKey,
'email' => $data['email'],
'ip' => getClientIP(),
]);
}
echo json_encode([
'success' => true,
'message' => 'Ihre Nachricht wurde erfolgreich gesendet! Wir melden uns in Kürze bei Ihnen.',
]); ]);
} else { } else {
http_response_code(500); http_response_code(500);
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => 'Beim Senden der Nachricht ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.' 'message' => 'Beim Senden der Nachricht ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.',
]); ]);
} }
} catch (Exception $e) { } catch (Exception $e) {
error_log('HexaHost Contact Form Error: ' . $e->getMessage()); error_log('HexaHost Contact Form Error: ' . $e->getMessage());
http_response_code(500); http_response_code(500);
echo json_encode([ echo json_encode([
'success' => false, 'success' => false,
'message' => 'Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.' 'message' => 'Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.',
]); ]);
} }
?>

View File

@@ -1,18 +1,21 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
require_once __DIR__ . '/../backend/config/contact-config.php';
$preselected_subject = getPreselectedContactSubject();
// Page configuration
$page_title = 'Kontakt - HexaHost.de | Hosting aus Niederbayern'; $page_title = 'Kontakt - HexaHost.de | Hosting aus Niederbayern';
$page_description = 'Kontaktieren Sie HexaHost.de - Ihr Hosting-Partner aus Niederbayern. Persönlicher Support und kompetente Beratung.'; $page_description = 'Kontaktieren Sie HexaHost.de - Ihr Hosting-Partner aus Niederbayern. Persönlicher Support und kompetente Beratung.';
$current_page = 'contact'; $current_page = 'contact';
$additional_scripts = ['assets/js/contact.js']; $additional_scripts = ['assets/js/contact.js'];
// Include header
includeHeader($page_title, $page_description, $current_page, $additional_scripts); includeHeader($page_title, $page_description, $current_page, $additional_scripts);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Contact Hero -->
<section class="contact-hero"> <section class="contact-hero">
<div class="container"> <div class="container">
<div class="contact-hero-content"> <div class="contact-hero-content">
@@ -33,7 +36,7 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
</div> </div>
</section> </section>
<!-- Contact Options -->
<section class="contact-options"> <section class="contact-options">
<div class="container"> <div class="container">
<div class="contact-grid"> <div class="contact-grid">
@@ -85,7 +88,7 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
</div> </div>
</section> </section>
<!-- Contact Form -->
<section class="contact-form-section"> <section class="contact-form-section">
<div class="container"> <div class="container">
<div class="form-container"> <div class="form-container">
@@ -97,7 +100,7 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
</div> </div>
<form class="contact-form glass-card" id="contactForm" action="contact-handler.php" method="POST"> <form class="contact-form glass-card" id="contactForm" action="contact-handler.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>"> <input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<!-- Honeypot-Feld für Bot-Schutz (versteckt via CSS) -->
<div style="position: absolute; left: -9999px;" aria-hidden="true"> <div style="position: absolute; left: -9999px;" aria-hidden="true">
<input type="text" name="website" tabindex="-1" autocomplete="off"> <input type="text" name="website" tabindex="-1" autocomplete="off">
</div> </div>
@@ -129,21 +132,9 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
<label for="subject">Betreff *</label> <label for="subject">Betreff *</label>
<select id="subject" name="subject" required> <select id="subject" name="subject" required>
<option value="">Bitte wählen...</option> <option value="">Bitte wählen...</option>
<option value="allgemeine-anfrage">Allgemeine Anfrage</option> <?php foreach (getContactSubjectMap() as $subjectKey => $subjectLabel): ?>
<option value="vpc-anfrage">Virtual Private Container</option> <option value="<?php echo htmlspecialchars($subjectKey, ENT_QUOTES, 'UTF-8'); ?>"<?php echo $preselected_subject === $subjectKey ? ' selected' : ''; ?>><?php echo htmlspecialchars($subjectLabel, ENT_QUOTES, 'UTF-8'); ?></option>
<option value="vps-anfrage">Virtual Private Server</option> <?php endforeach; ?>
<option value="mail-gateway-anfrage">Mail Gateway</option>
<option value="webhosting-anfrage">Webhosting</option>
<option value="it-beratung">IT-Beratung</option>
<option value="it-support">IT-Support & Fehlerbehebung</option>
<option value="netzwerk-wlan">Netzwerk & WLAN-Einrichtung</option>
<option value="it-sicherheit-backup">IT-Sicherheit & Backup</option>
<option value="webseiten-hosting-service">Webseiten- & Hosting-Service</option>
<option value="wartung-betreuung">Wartung & Betreuung</option>
<option value="support">Technischer Support</option>
<option value="beratung">Persönliche Beratung</option>
<option value="migration">Migration/Umzug</option>
<option value="sonstiges">Sonstiges</option>
</select> </select>
</div> </div>
<div class="form-group"> <div class="form-group">
@@ -165,7 +156,7 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
</div> </div>
</section> </section>
<!-- FAQ Section -->
<section class="faq-section"> <section class="faq-section">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -233,7 +224,7 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
</div> </div>
</section> </section>
<!-- Response Time -->
<section class="response-time"> <section class="response-time">
<div class="container"> <div class="container">
<div class="response-content glass-card"> <div class="response-content glass-card">
@@ -261,6 +252,6 @@ includeHeader($page_title, $page_description, $current_page, $additional_scripts
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -1,17 +1,17 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = 'Datenschutzerklärung - HexaHost.de | Datenschutz'; $page_title = 'Datenschutzerklärung - HexaHost.de | Datenschutz';
$page_description = 'Datenschutzerklärung von HexaHost.de - Informationen zum Schutz Ihrer personenbezogenen Daten.'; $page_description = 'Datenschutzerklärung von HexaHost.de - Informationen zum Schutz Ihrer personenbezogenen Daten.';
$current_page = 'datenschutz'; $current_page = 'datenschutz';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Datenschutz Hero -->
<section class="legal-hero"> <section class="legal-hero">
<div class="container"> <div class="container">
<div class="legal-hero-content"> <div class="legal-hero-content">
@@ -29,12 +29,12 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Datenschutz Content -->
<section class="legal-content"> <section class="legal-content">
<div class="container"> <div class="container">
<div class="legal-container"> <div class="legal-container">
<!-- Verantwortlicher -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>1. Verantwortlicher</h2> <h2>1. Verantwortlicher</h2>
<div class="legal-block"> <div class="legal-block">
@@ -53,7 +53,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Allgemeine Hinweise -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>2. Allgemeine Hinweise zur Datenverarbeitung</h2> <h2>2. Allgemeine Hinweise zur Datenverarbeitung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -75,7 +75,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Rechtsgrundlagen -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>3. Rechtsgrundlagen der Verarbeitung</h2> <h2>3. Rechtsgrundlagen der Verarbeitung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -89,7 +89,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Server-Logfiles -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>4. Server-Logfiles</h2> <h2>4. Server-Logfiles</h2>
<div class="legal-block"> <div class="legal-block">
@@ -119,7 +119,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Cookies -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>5. Cookies</h2> <h2>5. Cookies</h2>
<div class="legal-block"> <div class="legal-block">
@@ -146,7 +146,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Kontaktformular -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>6. Kontaktformular</h2> <h2>6. Kontaktformular</h2>
<div class="legal-block"> <div class="legal-block">
@@ -168,7 +168,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Hosting und Vertragsabwicklung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>7. Hosting-Dienstleistungen und Vertragsabwicklung</h2> <h2>7. Hosting-Dienstleistungen und Vertragsabwicklung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -193,7 +193,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- SSL-Verschlüsselung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>8. SSL- bzw. TLS-Verschlüsselung</h2> <h2>8. SSL- bzw. TLS-Verschlüsselung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -212,7 +212,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Speicherdauer -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>9. Speicherdauer</h2> <h2>9. Speicherdauer</h2>
<div class="legal-block"> <div class="legal-block">
@@ -229,7 +229,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Betroffenenrechte -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>10. Ihre Rechte als betroffene Person</h2> <h2>10. Ihre Rechte als betroffene Person</h2>
<div class="legal-block"> <div class="legal-block">
@@ -281,7 +281,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Beschwerderecht -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>11. Beschwerderecht bei einer Aufsichtsbehörde</h2> <h2>11. Beschwerderecht bei einer Aufsichtsbehörde</h2>
<div class="legal-block"> <div class="legal-block">
@@ -302,7 +302,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Datensicherheit -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>12. Datensicherheit</h2> <h2>12. Datensicherheit</h2>
<div class="legal-block"> <div class="legal-block">
@@ -320,7 +320,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Änderungen -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>13. Aktualität und Änderung dieser Datenschutzerklärung</h2> <h2>13. Aktualität und Änderung dieser Datenschutzerklärung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -344,6 +344,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -1,17 +1,17 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = 'Impressum - HexaHost.de | Rechtliche Angaben'; $page_title = 'Impressum - HexaHost.de | Rechtliche Angaben';
$page_description = 'Impressum und rechtliche Angaben von HexaHost.de - Hosting aus Niederbayern.'; $page_description = 'Impressum und rechtliche Angaben von HexaHost.de - Hosting aus Niederbayern.';
$current_page = 'impressum'; $current_page = 'impressum';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Impressum Hero -->
<section class="legal-hero"> <section class="legal-hero">
<div class="container"> <div class="container">
<div class="legal-hero-content"> <div class="legal-hero-content">
@@ -29,12 +29,12 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Impressum Content -->
<section class="legal-content"> <section class="legal-content">
<div class="container"> <div class="container">
<div class="legal-container"> <div class="legal-container">
<!-- Anbieterkennung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Angaben gemäß § 5 DDG</h2> <h2>Angaben gemäß § 5 DDG</h2>
@@ -50,7 +50,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Kontakt -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Kontakt</h2> <h2>Kontakt</h2>
<div class="legal-block"> <div class="legal-block">
@@ -61,7 +61,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Umsatzsteuer-ID (falls vorhanden) -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Umsatzsteuer-ID</h2> <h2>Umsatzsteuer-ID</h2>
<div class="legal-block"> <div class="legal-block">
@@ -72,7 +72,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Verantwortlich für den Inhalt -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Redaktionell verantwortlich</h2> <h2>Redaktionell verantwortlich</h2>
<div class="legal-block"> <div class="legal-block">
@@ -85,7 +85,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- EU-Streitschlichtung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>EU-Streitschlichtung</h2> <h2>EU-Streitschlichtung</h2>
<div class="legal-block"> <div class="legal-block">
@@ -101,7 +101,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Verbraucherstreitbeilegung -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Verbraucherstreitbeilegung / Universalschlichtungsstelle</h2> <h2>Verbraucherstreitbeilegung / Universalschlichtungsstelle</h2>
<div class="legal-block"> <div class="legal-block">
@@ -112,7 +112,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Haftungsausschluss -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Haftung für Inhalte</h2> <h2>Haftung für Inhalte</h2>
<div class="legal-block"> <div class="legal-block">
@@ -133,7 +133,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Haftung für Links -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Haftung für Links</h2> <h2>Haftung für Links</h2>
<div class="legal-block"> <div class="legal-block">
@@ -153,7 +153,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</div> </div>
<!-- Urheberrecht -->
<div class="legal-section glass-card"> <div class="legal-section glass-card">
<h2>Urheberrecht</h2> <h2>Urheberrecht</h2>
<div class="legal-block"> <div class="legal-block">
@@ -180,6 +180,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -0,0 +1,112 @@
<?php
/**
* Gemeinsame Hilfsfunktionen für öffentliche DNS/API-Endpunkte
*/
/**
* Client-IP für Rate-Limiting (Cloudflare-sicher, kein blindes X-Forwarded-For)
*/
function getApiClientIp(): string {
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])
&& filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP)) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$isTrustedProxy = filter_var(
$remoteAddr,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === false;
if ($isTrustedProxy) {
foreach (['HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR'] as $header) {
if (empty($_SERVER[$header])) {
continue;
}
$ip = trim(explode(',', $_SERVER[$header])[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return $remoteAddr;
}
/**
* Einfaches Rate-Limiting pro Endpunkt und IP
*/
function checkApiRateLimit(string $endpoint, int $maxPerHour = 120): bool {
$ip = getApiClientIp();
$cacheFile = sys_get_temp_dir() . '/hexahost_api_' . md5($endpoint . '_' . $ip) . '.txt';
$currentTime = time();
$data = ['requests' => []];
$handle = @fopen($cacheFile, 'c+');
if ($handle === false) {
return true;
}
try {
if (!flock($handle, LOCK_EX)) {
return true;
}
$contents = stream_get_contents($handle);
if ($contents !== false && $contents !== '') {
$decoded = json_decode($contents, true);
if (is_array($decoded) && isset($decoded['requests'])) {
$data = $decoded;
}
}
$data['requests'] = array_values(array_filter(
$data['requests'],
static fn($timestamp) => ($currentTime - (int) $timestamp) < 3600
));
if (count($data['requests']) >= $maxPerHour) {
return false;
}
$data['requests'][] = $currentTime;
ftruncate($handle, 0);
rewind($handle);
fwrite($handle, json_encode($data));
} finally {
flock($handle, LOCK_UN);
fclose($handle);
}
return true;
}
/**
* Domain aus GET-Parameter normalisieren und validieren
*/
function getValidatedDomainParam(string $param = 'domain'): ?string {
if (empty($_GET[$param])) {
return null;
}
$domain = trim((string) $_GET[$param]);
$domain = preg_replace('/^(https?:\/\/)?/', '', $domain);
$domain = explode('/', $domain)[0];
$domain = explode(':', $domain)[0];
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}$/', $domain)) {
return null;
}
return $domain;
}
/**
* Rate-Limit-JSON-Antwort senden und beenden
*/
function rejectApiRateLimit(): void {
http_response_code(429);
echo json_encode(['error' => 'Zu viele Anfragen. Bitte versuchen Sie es später erneut.']);
exit;
}

167
public/includes/footer.php Normal file
View File

@@ -0,0 +1,167 @@
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h4>HexaHost.de</h4>
<p>Zuverlässiges Hosting aus Niederbayern</p>
<div class="footer-location">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
<circle cx="12" cy="10" r="3"/>
</svg>
<span>Niederbayern, Deutschland</span>
</div>
</div>
<div class="footer-section">
<h4>Produkte</h4>
<ul>
<li><a href="/vpc">Virtual Private Container</a></li>
<li><a href="/vps">Virtual Private Server</a></li>
<li><a href="/mail-gateway">Mail Gateway</a></li>
<li><a href="/webhosting">Webhosting</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Unternehmen</h4>
<ul>
<li><a href="/about">Über mich</a></li>
<li><a href="/contact">Kontakt</a></li>
<li><a href="/impressum">Impressum</a></li>
<li><a href="/datenschutz">Datenschutz</a></li>
<li><a href="/agb">AGB</a></li>
<li><a href="/widerruf">Widerrufsbelehrung</a></li>
<li><a href="#" id="openCookieSettings" onclick="CookieConsent.resetConsent(); return false;">Cookie-Einstellungen</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Support</h4>
<ul>
<li><a href="https://shop.hexahost.de/clientarea.php">Kunden-Center</a></li>
<li><a href="https://shop.hexahost.de/serverstatus.php">Status</a></li>
<li><a href="https://shop.hexahost.de/supporttickets.php">Support-Ticket</a></li>
<li><a href="#">FAQ</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; <?php echo date('Y'); ?> HexaHost.de - Alle Rechte vorbehalten</p>
</div>
</div>
</footer>
<div id="cookieConsent" class="cookie-consent" role="dialog" aria-labelledby="cookieConsentTitle" aria-describedby="cookieConsentDesc">
<div class="cookie-consent-container">
<div class="cookie-consent-content">
<div class="cookie-consent-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="10"/>
<circle cx="8" cy="9" r="1" fill="currentColor"/>
<circle cx="15" cy="8" r="1" fill="currentColor"/>
<circle cx="10" cy="14" r="1" fill="currentColor"/>
<circle cx="16" cy="13" r="1" fill="currentColor"/>
<circle cx="13" cy="17" r="1" fill="currentColor"/>
</svg>
</div>
<div class="cookie-consent-text">
<h3 id="cookieConsentTitle">Cookie-Einstellungen</h3>
<p id="cookieConsentDesc">
Wir verwenden Cookies, um Ihnen die bestmögliche Erfahrung auf unserer Website zu bieten.
Technisch notwendige Cookies sind für die Funktionalität erforderlich.
<a href="/datenschutz">Mehr erfahren</a>
</p>
</div>
</div>
<div class="cookie-consent-actions">
<button type="button" id="cookieAcceptAll" class="btn btn-primary">Alle akzeptieren</button>
<button type="button" id="cookieAcceptEssential" class="btn btn-secondary">Nur notwendige</button>
<button type="button" id="cookieSettings" class="btn btn-text">Einstellungen</button>
</div>
</div>
<div id="cookieSettingsPanel" class="cookie-settings-panel" style="display: none;">
<div class="cookie-settings-content">
<h4>Cookie-Einstellungen</h4>
<div class="cookie-option">
<div class="cookie-option-info">
<strong>Notwendige Cookies</strong>
<p>Diese Cookies sind für die Grundfunktionen der Website erforderlich.</p>
</div>
<label class="cookie-toggle disabled">
<input type="checkbox" checked disabled>
<span class="cookie-toggle-slider"></span>
</label>
</div>
<div class="cookie-option">
<div class="cookie-option-info">
<strong>Analyse-Cookies</strong>
<p>Helfen uns zu verstehen, wie Besucher unsere Website nutzen.</p>
</div>
<label class="cookie-toggle">
<input type="checkbox" id="cookieAnalytics">
<span class="cookie-toggle-slider"></span>
</label>
</div>
<div class="cookie-option">
<div class="cookie-option-info">
<strong>Marketing-Cookies</strong>
<p>Werden verwendet, um relevante Werbung anzuzeigen.</p>
</div>
<label class="cookie-toggle">
<input type="checkbox" id="cookieMarketing">
<span class="cookie-toggle-slider"></span>
</label>
</div>
<div class="cookie-settings-actions">
<button type="button" id="cookieSaveSettings" class="btn btn-primary">Einstellungen speichern</button>
<button type="button" id="cookieCloseSettings" class="btn btn-secondary">Abbrechen</button>
</div>
</div>
</div>
</div>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// Standard: keine Analyse/Marketing-Cookies bis zur Einwilligung
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied'
});
gtag('js', new Date());
gtag('config', 'G-EF0E9VPMTD', {
anonymize_ip: true
});
// Übergibt Consent-Änderungen aus dem eigenen Cookie-Banner an GA
window.addEventListener('cookieConsentUpdated', function (event) {
var payload = event && event.detail ? event.detail : {};
var consent = payload.consent ? payload.consent : payload;
var analyticsGranted = !!(consent && consent.analytics);
var marketingGranted = !!(consent && consent.marketing);
gtag('consent', 'update', {
analytics_storage: analyticsGranted ? 'granted' : 'denied',
ad_storage: marketingGranted ? 'granted' : 'denied',
ad_user_data: marketingGranted ? 'granted' : 'denied',
ad_personalization: marketingGranted ? 'granted' : 'denied'
});
});
</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-EF0E9VPMTD"></script>
<script src="/assets/js/main.js" defer></script>
<script src="/assets/js/cookie-consent.js" defer></script>
<?php if (isset($additional_scripts)): ?>
<?php foreach ($additional_scripts as $script): ?>
<script src="<?php echo htmlspecialchars($script, ENT_QUOTES, 'UTF-8'); ?>" defer></script>
<?php endforeach; ?>
<?php endif; ?>
</body>
</html>

View File

@@ -0,0 +1,125 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', isset($_SERVER['HTTPS']) ? 1 : 0);
ini_set('session.cookie_samesite', 'Strict');
ini_set('session.use_strict_mode', 1);
ini_set('session.use_only_cookies', 1);
session_start();
if (!isset($_SESSION['initiated'])) {
session_regenerate_id(true);
$_SESSION['initiated'] = true;
}
}
if (!defined('DEBUG_MODE') || !DEBUG_MODE) {
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(E_ALL);
ini_set('log_errors', 1);
}
function includeHeader($title = '', $description = '', $page = '', $scripts = []) {
global $page_title, $page_description, $current_page, $additional_scripts;
$page_title = !empty($title)
? $title
: 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern';
$page_description = !empty($description)
? $description
: 'HexaHost.de - Zuverlässiges und preiswertes Hosting aus Niederbayern. VPS, VPC, Mail Gateway und Webhosting Lösungen.';
$current_page = $page;
$additional_scripts = $scripts;
include __DIR__ . '/header.php';
}
function includeFooter() {
include __DIR__ . '/footer.php';
}
function generateBreadcrumbs($breadcrumbs) {
echo '<div class="breadcrumb">';
$last_index = count($breadcrumbs) - 1;
foreach ($breadcrumbs as $index => $item) {
if ($index === $last_index) {
echo '<span>' . htmlspecialchars($item['title']) . '</span>';
} else {
echo '<a href="' . htmlspecialchars($item['url']) . '">' . htmlspecialchars($item['title']) . '</a>';
echo '<span>/</span>';
}
}
echo '</div>';
}
function generateCSRFToken() {
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function validateCSRFToken($token) {
if (!isset($_SESSION['csrf_token']) || !is_string($token)) {
return false;
}
if (!hash_equals($_SESSION['csrf_token'], $token)) {
return false;
}
unset($_SESSION['csrf_token']);
return true;
}
function sanitizeHeaderValue(string $value): string {
return str_replace(["\r", "\n", "\0"], '', trim($value));
}
function getClientIP(): string {
if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])
&& filter_var($_SERVER['HTTP_CF_CONNECTING_IP'], FILTER_VALIDATE_IP)) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
$remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
$isTrustedProxy = filter_var(
$remoteAddr,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === false;
if ($isTrustedProxy) {
foreach (['HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR'] as $header) {
if (empty($_SERVER[$header])) {
continue;
}
$ip = trim(explode(',', $_SERVER[$header])[0]);
if (filter_var($ip, FILTER_VALIDATE_IP)) {
return $ip;
}
}
}
return $remoteAddr;
}
?>

View File

@@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="dns-prefetch" href="//fonts.googleapis.com">
<link rel="dns-prefetch" href="//fonts.gstatic.com">
<link rel="dns-prefetch" href="//cdn.hexahost.de">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://cdn.hexahost.de" crossorigin>
<link rel="preload" href="/assets/css/style.css" as="style">
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" as="style">
<title><?php echo isset($page_title) ? htmlspecialchars($page_title) : 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern'; ?></title>
<meta name="description" content="<?php echo isset($page_description) ? htmlspecialchars($page_description) : 'HexaHost.de - Zuverlässiges und preiswertes Hosting aus Niederbayern. VPS, VPC, Mail Gateway und Webhosting Lösungen.'; ?>">
<meta name="robots" content="index, follow">
<meta name="author" content="HexaHost.de">
<meta name="theme-color" content="#0d0821">
<meta property="og:type" content="website">
<meta property="og:site_name" content="HexaHost.de">
<meta property="og:title" content="<?php echo isset($page_title) ? htmlspecialchars($page_title) : 'HexaHost.de'; ?>">
<meta property="og:description" content="<?php echo isset($page_description) ? htmlspecialchars($page_description) : 'Zuverlässiges Hosting aus Niederbayern'; ?>">
<meta property="og:locale" content="de_DE">
<link rel="stylesheet" href="/assets/css/style.css">
<link rel="stylesheet" href="/assets/css/custom.css">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Russo+One&family=Source+Sans+Pro:wght@300;400;600;700&display=swap" rel="stylesheet">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="apple-touch-icon" href="/favicon.svg">
<?php if (isset($canonical_url)): ?>
<link rel="canonical" href="<?php echo htmlspecialchars($canonical_url); ?>">
<?php endif; ?>
</head>
<body>
<header class="header">
<nav class="nav">
<div class="nav-container">
<div class="nav-logo">
<a href="/">
<img src="https://cdn.hexahost.de/assets/img/logo/8iFs123BynHQWHI5.png" alt="HexaHost.de Logo" class="logo-image">
</a>
</div>
<ul class="nav-menu">
<li><a href="/" class="nav-link <?php echo ($current_page === 'home') ? 'active' : ''; ?>">Home</a></li>
<li class="nav-dropdown">
<a href="#" class="nav-link <?php echo (in_array($current_page, ['vpc', 'vps', 'mail-gateway', 'webhosting'])) ? 'active' : ''; ?>">Produkte</a>
<ul class="dropdown-menu">
<li><a href="/vpc" class="<?php echo ($current_page === 'vpc') ? 'active' : ''; ?>">Virtual Private Container</a></li>
<li><a href="/vps" class="<?php echo ($current_page === 'vps') ? 'active' : ''; ?>">Virtual Private Server</a></li>
<li><a href="/mail-gateway" class="<?php echo ($current_page === 'mail-gateway') ? 'active' : ''; ?>">Mail Gateway</a></li>
<li><a href="/webhosting" class="<?php echo ($current_page === 'webhosting') ? 'active' : ''; ?>">Webhosting</a></li>
</ul>
</li>
<li><a href="/it-dienstleistungen" class="nav-link <?php echo ($current_page === 'it-dienstleistungen') ? 'active' : ''; ?>">IT-Dienstleistungen</a></li>
<li><a href="/about" class="nav-link <?php echo ($current_page === 'about') ? 'active' : ''; ?>">Über mich</a></li>
<li><a href="/contact" class="nav-link <?php echo ($current_page === 'contact') ? 'active' : ''; ?>">Kontakt</a></li>
</ul>
<div class="nav-toggle">
<span></span>
<span></span>
<span></span>
</div>
</div>
</nav>
</header>

View File

@@ -1,17 +1,17 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern'; $page_title = 'HexaHost.de - Zuverlässiges Hosting aus Niederbayern';
$page_description = 'HexaHost.de - Zuverlässiges und preiswertes Hosting aus Niederbayern. VPS, VPC, Mail Gateway und Webhosting Lösungen.'; $page_description = 'HexaHost.de - Zuverlässiges und preiswertes Hosting aus Niederbayern. VPS, VPC, Mail Gateway und Webhosting Lösungen.';
$current_page = 'home'; $current_page = 'home';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Hero Section -->
<section class="hero"> <section class="hero">
<div class="hero-container"> <div class="hero-container">
<div class="hero-content"> <div class="hero-content">
@@ -44,7 +44,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Products Section -->
<section id="products" class="products"> <section id="products" class="products">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -150,7 +150,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- IT Services Section -->
<section class="features"> <section class="features">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -192,7 +192,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Features Section -->
<section class="features"> <section class="features">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -248,7 +248,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- CTA Section -->
<section class="cta"> <section class="cta">
<div class="container"> <div class="container">
<div class="cta-content glass-card"> <div class="cta-content glass-card">
@@ -265,6 +265,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -1,17 +1,17 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = 'IT-Dienstleistungen - HexaHost.de | Privat & Gewerblich'; $page_title = 'IT-Dienstleistungen - HexaHost.de | Privat & Gewerblich';
$page_description = 'IT-Dienstleistungen von HexaHost.de mit Fokus auf Privatkunden und ergänzend für gewerbliche Anforderungen.'; $page_description = 'IT-Dienstleistungen von HexaHost.de mit Fokus auf Privatkunden und ergänzend für gewerbliche Anforderungen.';
$current_page = 'it-dienstleistungen'; $current_page = 'it-dienstleistungen';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Services Hero -->
<section class="about-hero"> <section class="about-hero">
<div class="container"> <div class="container">
<div class="about-hero-content"> <div class="about-hero-content">
@@ -32,7 +32,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Target Groups -->
<section class="values"> <section class="values">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -60,7 +60,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Services Grid -->
<section class="products"> <section class="products">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -128,7 +128,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- CTA -->
<section class="cta"> <section class="cta">
<div class="container"> <div class="container">
<div class="cta-content glass-card"> <div class="cta-content glass-card">
@@ -143,6 +143,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -2,21 +2,21 @@
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
require_once __DIR__ . '/../backend/config/products-config.php'; require_once __DIR__ . '/../backend/config/products-config.php';
// Produkt-Daten aus Config laden
$product = getProduct('mail-gateway'); $product = getProduct('mail-gateway');
$packages = getProductPackages('mail-gateway'); $packages = getProductPackages('mail-gateway');
// Page configuration
$page_title = $product['page_title']; $page_title = $product['page_title'];
$page_description = $product['page_description']; $page_description = $product['page_description'];
$current_page = 'mail-gateway'; $current_page = 'mail-gateway';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Product Hero -->
<section class="product-hero"> <section class="product-hero">
<div class="container"> <div class="container">
<div class="product-hero-content"> <div class="product-hero-content">
@@ -59,7 +59,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Mail Gateway Packages -->
<section class="packages"> <section class="packages">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -74,7 +74,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Security Features -->
<section class="technical-details"> <section class="technical-details">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -129,7 +129,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Use Cases -->
<section class="use-cases"> <section class="use-cases">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -159,7 +159,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- CTA Section -->
<section class="cta"> <section class="cta">
<div class="container"> <div class="container">
<div class="cta-content glass-card"> <div class="cta-content glass-card">
@@ -175,6 +175,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -2,21 +2,21 @@
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
require_once __DIR__ . '/../backend/config/products-config.php'; require_once __DIR__ . '/../backend/config/products-config.php';
// Produkt-Daten aus Config laden
$product = getProduct('vpc'); $product = getProduct('vpc');
$packages = getProductPackages('vpc'); $packages = getProductPackages('vpc');
// Page configuration
$page_title = $product['page_title']; $page_title = $product['page_title'];
$page_description = $product['page_description']; $page_description = $product['page_description'];
$current_page = 'vpc'; $current_page = 'vpc';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Product Hero -->
<section class="product-hero"> <section class="product-hero">
<div class="container"> <div class="container">
<div class="product-hero-content"> <div class="product-hero-content">
@@ -58,7 +58,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- VPC Packages -->
<section class="packages"> <section class="packages">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -73,7 +73,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Technical Details -->
<section class="technical-details"> <section class="technical-details">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -129,7 +129,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Use Cases -->
<section class="use-cases"> <section class="use-cases">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -159,7 +159,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- CTA Section -->
<section class="cta"> <section class="cta">
<div class="container"> <div class="container">
<div class="cta-content glass-card"> <div class="cta-content glass-card">
@@ -175,6 +175,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -2,21 +2,21 @@
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
require_once __DIR__ . '/../backend/config/products-config.php'; require_once __DIR__ . '/../backend/config/products-config.php';
// Produkt-Daten aus Config laden
$product = getProduct('vps'); $product = getProduct('vps');
$packages = getProductPackages('vps'); $packages = getProductPackages('vps');
// Page configuration
$page_title = $product['page_title']; $page_title = $product['page_title'];
$page_description = $product['page_description']; $page_description = $product['page_description'];
$current_page = 'vps'; $current_page = 'vps';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Product Hero -->
<section class="product-hero"> <section class="product-hero">
<div class="container"> <div class="container">
<div class="product-hero-content"> <div class="product-hero-content">
@@ -63,7 +63,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- VPS Packages -->
<section class="packages"> <section class="packages">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -78,7 +78,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Technical Details -->
<section class="technical-details"> <section class="technical-details">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -134,7 +134,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Use Cases -->
<section class="use-cases"> <section class="use-cases">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -164,7 +164,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- CTA Section -->
<section class="cta"> <section class="cta">
<div class="container"> <div class="container">
<div class="cta-content glass-card"> <div class="cta-content glass-card">
@@ -180,6 +180,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -2,21 +2,21 @@
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
require_once __DIR__ . '/../backend/config/products-config.php'; require_once __DIR__ . '/../backend/config/products-config.php';
// Produkt-Daten aus Config laden
$product = getProduct('webhosting'); $product = getProduct('webhosting');
$packages = getProductPackages('webhosting'); $packages = getProductPackages('webhosting');
// Page configuration
$page_title = $product['page_title']; $page_title = $product['page_title'];
$page_description = $product['page_description']; $page_description = $product['page_description'];
$current_page = 'webhosting'; $current_page = 'webhosting';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
<main id="main-content"> <main id="main-content">
<!-- Product Hero -->
<section class="product-hero"> <section class="product-hero">
<div class="container"> <div class="container">
<div class="product-hero-content"> <div class="product-hero-content">
@@ -60,7 +60,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Webhosting Packages -->
<section class="packages"> <section class="packages">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -75,7 +75,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Technical Details -->
<section class="technical-details"> <section class="technical-details">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -133,7 +133,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- Use Cases -->
<section class="use-cases"> <section class="use-cases">
<div class="container"> <div class="container">
<div class="section-header"> <div class="section-header">
@@ -163,7 +163,7 @@ includeHeader($page_title, $page_description, $current_page);
</div> </div>
</section> </section>
<!-- CTA Section -->
<section class="cta"> <section class="cta">
<div class="container"> <div class="container">
<div class="cta-content glass-card"> <div class="cta-content glass-card">
@@ -179,6 +179,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

View File

@@ -1,12 +1,12 @@
<?php <?php
require_once __DIR__ . '/../backend/includes/functions.php'; require_once __DIR__ . '/../backend/includes/functions.php';
// Page configuration
$page_title = 'Widerrufsbelehrung - HexaHost.de'; $page_title = 'Widerrufsbelehrung - HexaHost.de';
$page_description = 'Widerrufsbelehrung und Muster-Widerrufsformular von HexaHost Inh. Samuel Müller.'; $page_description = 'Widerrufsbelehrung und Muster-Widerrufsformular von HexaHost Inh. Samuel Müller.';
$current_page = 'widerruf'; $current_page = 'widerruf';
// Include header
includeHeader($page_title, $page_description, $current_page); includeHeader($page_title, $page_description, $current_page);
?> ?>
@@ -131,6 +131,6 @@ includeHeader($page_title, $page_description, $current_page);
</main> </main>
<?php <?php
// Include footer
includeFooter(); includeFooter();
?> ?>

185
scripts/publish-to-main.ps1 Normal file
View File

@@ -0,0 +1,185 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Erstellt einen Production-Build und veroeffentlicht ihn auf den Branch main.
.DESCRIPTION
1. Wechselt auf main und setzt ihn auf den Stand von dev
2. Entfernt Kommentare, minifiziert CSS, obfuskiert JavaScript
3. Committet und pusht main (optional)
4. Wechselt zurueck auf dev (Quellcode bleibt unveraendert)
.PARAMETER Push
Pusht main nach origin (Standard: nur lokaler Commit)
.PARAMETER DryRun
Fuehrt Git-Schritte nur simuliert aus (Build wird trotzdem erstellt)
.PARAMETER AllowDirty
Erlaubt uncommittete Aenderungen
.PARAMETER Message
Commit-Nachricht fuer den Production-Build
.EXAMPLE
.\scripts\publish-to-main.ps1
.EXAMPLE
.\scripts\publish-to-main.ps1 -Push
#>
[CmdletBinding()]
param(
[switch]$Push,
[switch]$DryRun,
[switch]$AllowDirty,
[string]$Message = ""
)
$ErrorActionPreference = "Stop"
$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$BuildDir = Join-Path $Root "scripts\build"
$OriginalBranch = ""
function Write-Step([string]$Text) {
Write-Host ""
Write-Host "==> $Text" -ForegroundColor Cyan
}
function Ensure-GitClean {
$status = git -C $Root status --porcelain
if ($status) {
throw "Uncommittete Aenderungen im Repository. Bitte zuerst committen oder stashen."
}
}
function Resolve-NodeTool([string]$ToolName) {
$command = Get-Command $ToolName -ErrorAction SilentlyContinue
if ($command) {
return $command.Source
}
$candidates = @(
(Join-Path $env:ProgramFiles "nodejs\$ToolName.cmd"),
(Join-Path ${env:ProgramFiles(x86)} "nodejs\$ToolName.cmd"),
(Join-Path $env:LOCALAPPDATA "Programs\nodejs\$ToolName.cmd"),
"c:\Program Files\cursor\resources\app\resources\helpers\node.exe"
)
foreach ($candidate in $candidates) {
if ($ToolName -eq "node" -and (Test-Path $candidate)) {
return $candidate
}
if ($ToolName -ne "node" -and (Test-Path $candidate)) {
return $candidate
}
}
return $null
}
function Ensure-Node {
$script:NodeExe = Resolve-NodeTool "node"
$script:NpmExe = Resolve-NodeTool "npm"
if (-not $script:NodeExe) {
throw "Node.js ist nicht installiert. Bitte Node.js 18+ installieren: https://nodejs.org/"
}
if (-not $script:NpmExe) {
throw "npm wurde nicht gefunden. Bitte Node.js inkl. npm installieren und PATH setzen."
}
}
try {
Set-Location $Root
Ensure-Node
if (-not $AllowDirty) {
Ensure-GitClean
} else {
Write-Warning "AllowDirty aktiv - uncommittete Aenderungen werden mit veroeffentlicht."
}
$OriginalBranch = (git branch --show-current).Trim()
if ($OriginalBranch -ne "dev") {
Write-Warning "Empfohlen: Auf Branch 'dev' starten (aktuell: $OriginalBranch)"
}
if ([string]::IsNullOrWhiteSpace($Message)) {
$Message = "chore(release): production build $(Get-Date -Format 'yyyy-MM-dd HH:mm')"
}
Write-Step "Installiere Build-Abhaengigkeiten"
Set-Location $BuildDir
if (-not $DryRun) {
& $NpmExe ci --no-fund --no-audit
if ($LASTEXITCODE -ne 0) { throw "npm ci fehlgeschlagen" }
}
Write-Step "Wechsle auf main und synchronisiere mit dev"
Set-Location $Root
if ($DryRun) {
Write-Host '[DryRun] git checkout main'
Write-Host '[DryRun] git reset --hard dev'
} else {
git checkout main
git reset --hard dev
}
Write-Step "Production-Build (Kommentare entfernen, JS obfuscaten)"
Set-Location $BuildDir
if ($DryRun) {
Write-Host '[DryRun] npm run build:in-place'
} else {
& $NpmExe run build:in-place
if ($LASTEXITCODE -ne 0) { throw "Production-Build fehlgeschlagen" }
}
Write-Step "Production-Build committen"
Set-Location $Root
if ($DryRun) {
Write-Host '[DryRun] git add -A'
Write-Host ('[DryRun] git commit -m "' + $Message + '"')
} else {
git add -A
$null = git diff --cached --quiet
if ($LASTEXITCODE -eq 0) {
Write-Warning "Keine Build-Aenderungen - nichts zu committen."
} else {
git commit -m $Message
}
}
if ($Push) {
Write-Step "Push nach origin/main"
if ($DryRun) {
Write-Host '[DryRun] git push origin main'
} else {
git push origin main
}
} else {
Write-Host "Hinweis: Ohne -Push wurde nur lokal auf main gebaut." -ForegroundColor Yellow
}
Write-Step "Zurueck auf $OriginalBranch"
if (-not $DryRun) {
if ([string]::IsNullOrWhiteSpace($OriginalBranch)) {
git checkout dev
} else {
git checkout $OriginalBranch
}
}
Write-Host ""
Write-Host "Production-Release abgeschlossen." -ForegroundColor Green
if (-not $Push -and -not $DryRun) {
Write-Host "Zum Veroeffentlichen: git push origin main" -ForegroundColor Yellow
}
}
catch {
Write-Host ""
Write-Host ('FEHLER: ' + $_.Exception.Message) -ForegroundColor Red
Set-Location $Root
if ($OriginalBranch -and -not $DryRun) {
git checkout $OriginalBranch 2>$null
}
exit 1
}

187
scripts/publish-to-main.sh Normal file
View File

@@ -0,0 +1,187 @@
#!/usr/bin/env bash
#
# Erstellt einen Production-Build und veröffentlicht ihn auf den Branch main.
#
# 1. Wechselt auf main und setzt ihn auf den Stand von dev
# 2. Entfernt Kommentare, minifiziert CSS, obfuskiert JavaScript
# 3. Committet und pusht main (optional)
# 4. Wechselt zurück auf den ursprünglichen Branch (dev bleibt unverändert)
#
# Nutzung:
# ./scripts/publish-to-main.sh
# ./scripts/publish-to-main.sh --push
# ./scripts/publish-to-main.sh --dry-run
# ./scripts/publish-to-main.sh --allow-dirty --message "chore(release): v1.2"
#
set -euo pipefail
PUSH=false
DRY_RUN=false
ALLOW_DIRTY=false
MESSAGE=""
usage() {
cat <<'EOF'
Usage: publish-to-main.sh [OPTIONS]
Options:
--push Push nach origin/main
--dry-run Git-Schritte nur anzeigen (Build wird ausgeführt)
--allow-dirty Uncommittete Änderungen erlauben
--message TEXT Commit-Nachricht
-h, --help Hilfe anzeigen
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--push)
PUSH=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
--allow-dirty)
ALLOW_DIRTY=true
shift
;;
--message)
MESSAGE="${2:-}"
shift 2
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unbekannte Option: $1" >&2
usage >&2
exit 1
;;
esac
done
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
BUILD_DIR="$ROOT/scripts/build"
ORIGINAL_BRANCH=""
step() {
echo ""
echo "==> $1"
}
require_command() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "FEHLER: '$1' nicht gefunden. Bitte installieren und PATH setzen." >&2
exit 1
fi
}
ensure_git_clean() {
if [[ -n "$(git -C "$ROOT" status --porcelain)" ]]; then
echo "FEHLER: Uncommittete Änderungen. Bitte zuerst committen oder stashen." >&2
exit 1
fi
}
cleanup_on_error() {
echo ""
echo "FEHLER: Abgebrochen." >&2
cd "$ROOT" || true
if [[ -n "$ORIGINAL_BRANCH" && "$DRY_RUN" == false ]]; then
git checkout "$ORIGINAL_BRANCH" 2>/dev/null || true
fi
}
trap cleanup_on_error ERR
require_command node
require_command npm
require_command git
cd "$ROOT"
if [[ "$ALLOW_DIRTY" == false ]]; then
ensure_git_clean
else
echo "WARNUNG: --allow-dirty aktiv uncommittete Änderungen werden mit veröffentlicht." >&2
fi
ORIGINAL_BRANCH="$(git branch --show-current | tr -d '[:space:]')"
if [[ "$ORIGINAL_BRANCH" != "dev" ]]; then
echo "WARNUNG: Empfohlen auf Branch 'dev' zu starten (aktuell: ${ORIGINAL_BRANCH:-detached})" >&2
fi
if [[ -z "$MESSAGE" ]]; then
MESSAGE="chore(release): production build $(date '+%Y-%m-%d %H:%M')"
fi
step "Installiere Build-Abhängigkeiten"
cd "$BUILD_DIR"
if [[ "$DRY_RUN" == false ]]; then
npm ci --no-fund --no-audit
fi
step "Wechsle auf main und synchronisiere mit dev"
cd "$ROOT"
if [[ "$DRY_RUN" == true ]]; then
echo "[DryRun] git checkout main"
echo "[DryRun] git reset --hard dev"
else
git checkout main
git reset --hard dev
fi
step "Production-Build (Kommentare entfernen, JS obfuscaten)"
cd "$BUILD_DIR"
if [[ "$DRY_RUN" == true ]]; then
echo "[DryRun] npm run build:in-place"
else
npm run build:in-place
fi
step "Production-Build committen"
cd "$ROOT"
if [[ "$DRY_RUN" == true ]]; then
echo "[DryRun] git add -A"
echo "[DryRun] git commit -m \"$MESSAGE\""
else
git add -A
if git diff --cached --quiet; then
echo "WARNUNG: Keine Build-Änderungen nichts zu committen." >&2
else
git commit -m "$MESSAGE"
fi
fi
if [[ "$PUSH" == true ]]; then
step "Push nach origin/main"
if [[ "$DRY_RUN" == true ]]; then
echo "[DryRun] git push origin main"
else
git push origin main
fi
else
echo "Hinweis: Ohne --push wurde nur lokal auf main gebaut."
fi
step "Zurück auf ${ORIGINAL_BRANCH:-dev}"
if [[ "$DRY_RUN" == false ]]; then
if [[ -n "$ORIGINAL_BRANCH" ]]; then
git checkout "$ORIGINAL_BRANCH"
else
git checkout dev
fi
fi
trap - ERR
echo ""
echo "Production-Release abgeschlossen."
if [[ "$PUSH" == false && "$DRY_RUN" == false ]]; then
echo "Zum Veröffentlichen: git push origin main"
fi

46
scripts/run-build.ps1 Normal file
View File

@@ -0,0 +1,46 @@
#Requires -Version 5.1
<#
.SYNOPSIS
Erstellt ein Production-Bundle unter dist/ (ohne Branch-Wechsel).
#>
[CmdletBinding()]
param(
[switch]$InPlace
)
$ErrorActionPreference = "Stop"
$Root = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$BuildDir = Join-Path $Root "scripts\build"
function Resolve-NodeTool([string]$ToolName) {
$command = Get-Command $ToolName -ErrorAction SilentlyContinue
if ($command) { return $command.Source }
$candidates = @(
(Join-Path $env:ProgramFiles "nodejs\$ToolName.cmd"),
(Join-Path ${env:ProgramFiles(x86)} "nodejs\$ToolName.cmd")
)
foreach ($candidate in $candidates) {
if (Test-Path $candidate) { return $candidate }
}
return $null
}
$npm = Resolve-NodeTool "npm"
if (-not $npm) {
throw "npm nicht gefunden. Bitte Node.js installieren."
}
Set-Location $BuildDir
& $npm ci --no-fund --no-audit
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
if ($InPlace) {
& $npm run build:in-place
} else {
& $npm run build
}
exit $LASTEXITCODE

58
scripts/run-build.sh Normal file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env bash
#
# Erstellt ein Production-Bundle unter dist/ (ohne Branch-Wechsel).
#
# Nutzung:
# ./scripts/run-build.sh
# ./scripts/run-build.sh --in-place
#
set -euo pipefail
IN_PLACE=false
usage() {
cat <<'EOF'
Usage: run-build.sh [OPTIONS]
Options:
--in-place Build direkt im Repository (statt dist/)
-h, --help Hilfe anzeigen
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--in-place)
IN_PLACE=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unbekannte Option: $1" >&2
usage >&2
exit 1
;;
esac
done
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
BUILD_DIR="$ROOT/scripts/build"
if ! command -v npm >/dev/null 2>&1; then
echo "FEHLER: npm nicht gefunden. Bitte Node.js installieren." >&2
exit 1
fi
cd "$BUILD_DIR"
npm ci --no-fund --no-audit
if [[ "$IN_PLACE" == true ]]; then
npm run build:in-place
else
npm run build
fi

View File

@@ -1,72 +1,48 @@
<?php <?php
/** /**
* HexaHost.de E-Mail Test * HexaHost.de E-Mail Test (nur CLI oder lokale Entwicklung)
* Testet die E-Mail-Funktionalität ohne PHPMailer
*/ */
// Konfiguration laden if (PHP_SAPI !== 'cli') {
require_once 'config.php'; $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';
$isLocal = in_array($remoteAddr, ['127.0.0.1', '::1'], true)
|| filter_var($remoteAddr, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
if (!$isLocal) {
http_response_code(403);
exit('Forbidden');
}
}
require_once __DIR__ . '/../backend/config/mail-config.php';
// Test-E-Mail senden
function testEmail() { function testEmail() {
$config = getHexaHostConfig(); $config = getHexaHostConfig();
// Test-Daten
$test_data = [
'firstName' => 'Test',
'lastName' => 'Benutzer',
'email' => 'test@example.com',
'phone' => '+49 123 456789',
'company' => 'Test GmbH',
'subject' => 'test-email',
'message' => 'Dies ist eine Test-E-Mail vom HexaHost.de Kontaktformular.'
];
// E-Mail-Inhalt erstellen
$subject = '[HexaHost.de] Test-E-Mail'; $subject = '[HexaHost.de] Test-E-Mail';
$message = "Test-E-Mail von HexaHost.de\n\n"; $message = "Test-E-Mail von HexaHost.de\n\n";
$message .= "Name: " . $test_data['firstName'] . " " . $test_data['lastName'] . "\n";
$message .= "E-Mail: " . $test_data['email'] . "\n";
$message .= "Telefon: " . $test_data['phone'] . "\n";
$message .= "Unternehmen: " . $test_data['company'] . "\n";
$message .= "Nachricht: " . $test_data['message'] . "\n\n";
$message .= "Zeitstempel: " . date('d.m.Y H:i:s') . "\n"; $message .= "Zeitstempel: " . date('d.m.Y H:i:s') . "\n";
$message .= "IP-Adresse: " . $_SERVER['REMOTE_ADDR'] . "\n";
// Headers
$headers = [ $headers = [
'From: ' . $config['from_name'] . ' <' . $config['from_email'] . '>', 'From: ' . $config['from_name'] . ' <' . $config['from_email'] . '>',
'Reply-To: ' . $test_data['firstName'] . ' ' . $test_data['lastName'] . ' <' . $test_data['email'] . '>',
'MIME-Version: 1.0', 'MIME-Version: 1.0',
'Content-Type: text/plain; charset=UTF-8', 'Content-Type: text/plain; charset=UTF-8',
'X-Mailer: HexaHost Test Email' 'X-Mailer: HexaHost Test Email',
]; ];
// E-Mail senden return mail($config['to_email'], $subject, $message, implode("\r\n", $headers));
$result = mail($config['to_email'], $subject, $message, implode("\r\n", $headers));
return $result;
} }
// Test ausführen if (PHP_SAPI === 'cli') {
if (isset($_GET['test'])) { echo testEmail() ? "Test-E-Mail gesendet.\n" : "Fehler beim Senden.\n";
$result = testEmail(); exit;
}
if ($result) {
echo "✅ Test-E-Mail wurde erfolgreich gesendet!"; if (isset($_GET['test'])) {
} else { echo testEmail()
echo "❌ Fehler beim Senden der Test-E-Mail."; ? 'Test-E-Mail wurde gesendet.'
} : 'Fehler beim Senden der Test-E-Mail.';
} else { } else {
echo "<h1>HexaHost.de E-Mail Test</h1>"; echo '<h1>HexaHost.de E-Mail Test</h1>';
echo "<p>Klicken Sie auf den Link, um eine Test-E-Mail zu senden:</p>"; echo '<p><a href="?test=1">Test-E-Mail senden</a></p>';
echo "<a href='?test=1'>Test-E-Mail senden</a>";
// Konfiguration anzeigen
echo "<h2>Aktuelle Konfiguration:</h2>";
$config = getHexaHostConfig();
echo "<pre>";
print_r($config);
echo "</pre>";
} }
?>