From 50852d6970358855a967b53398e24c956390c044 Mon Sep 17 00:00:00 2001 From: Metacube Date: Tue, 4 Nov 2025 20:08:24 +0100 Subject: [PATCH 1/3] Add HyperTracer 3D experience in route2 --- route.php | 513 +++++++++++++++++++++++++++++++++++++ route2.php | 722 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1235 insertions(+) create mode 100644 route.php create mode 100644 route2.php diff --git a/route.php b/route.php new file mode 100644 index 0000000..932c9f4 --- /dev/null +++ b/route.php @@ -0,0 +1,513 @@ +&1'; + $rawOutput = shell_exec($command); + if ($rawOutput === null) { + $error = 'Traceroute konnte nicht ausgeführt werden. Ist das Kommando verfügbar?'; + } else { + $traceData = parseTraceroute($rawOutput); + if (empty($traceData)) { + $error = 'Keine Hops gefunden. Prüfen Sie den Hostnamen oder versuchen Sie es später erneut.'; + } + } + } +} + +if (empty($traceData)) { + $traceData = getSampleTrace(); + if ($error === '') { + $error = 'Es werden Beispieldaten angezeigt. Starten Sie eine Abfrage, um echte Traceroute-Daten zu sehen.'; + } +} + +function parseTraceroute(string $raw): array +{ + $lines = preg_split('/\r?\n/', trim($raw)); + if (!$lines) { + return []; + } + + $hops = []; + foreach ($lines as $line) { + if (preg_match('/^\s*\d+\s+/', $line) !== 1) { + continue; + } + + preg_match_all('/(\d+\.\d+)\s+ms/', $line, $latencyMatches); + $latencies = array_map('floatval', $latencyMatches[1] ?? []); + $avgLatency = !empty($latencies) ? array_sum($latencies) / count($latencies) : null; + + if (preg_match('/^\s*(\d+)\s+([0-9\.\*]+)/', $line, $parts) !== 1) { + continue; + } + + $hopNumber = (int) $parts[1]; + $ip = $parts[2]; + if ($ip === '*') { + $ip = 'Zeitüberschreitung'; + } + + $hops[] = [ + 'hop' => $hopNumber, + 'ip' => $ip, + 'avgLatency' => $avgLatency, + 'raw' => trim($line), + ]; + } + + return $hops; +} + +function getSampleTrace(): array +{ + return [ + ['hop' => 1, 'ip' => '192.168.0.1', 'avgLatency' => 1.2, 'raw' => '1 192.168.0.1 1.123 ms 1.234 ms 1.301 ms'], + ['hop' => 2, 'ip' => '10.12.34.1', 'avgLatency' => 9.4, 'raw' => '2 10.12.34.1 9.123 ms 9.567 ms 9.400 ms'], + ['hop' => 3, 'ip' => '172.16.5.4', 'avgLatency' => 18.7, 'raw' => '3 172.16.5.4 18.432 ms 18.913 ms 18.787 ms'], + ['hop' => 4, 'ip' => '203.0.113.5', 'avgLatency' => 32.9, 'raw' => '4 203.0.113.5 32.113 ms 33.441 ms 33.212 ms'], + ['hop' => 5, 'ip' => '93.184.216.34', 'avgLatency' => 48.2, 'raw' => '5 93.184.216.34 48.112 ms 48.501 ms 48.032 ms'], + ]; +} + +function generatePositions(array $trace): array +{ + $positions = []; + $radius = 25; + $spacing = 10; + foreach ($trace as $index => $hop) { + $angle = $index * 0.9; + $positions[] = [ + 'x' => cos($angle) * $radius, + 'y' => $index * $spacing, + 'z' => sin($angle) * $radius, + ]; + } + return $positions; +} + +$positions = generatePositions($traceData); +?> + + + + + + 3D Traceroute Visualisierung + + + +
+

3D Traceroute Explorer

+

Visualisieren Sie Netzwerkpfade im dreidimensionalen Raum und erkunden Sie die einzelnen Hops.

+
+
+
+ + +
+ +
+ + + + + + diff --git a/route2.php b/route2.php new file mode 100644 index 0000000..66a4032 --- /dev/null +++ b/route2.php @@ -0,0 +1,722 @@ +&1'; + $rawOutput = shell_exec($command); + if ($rawOutput === null) { + $error = 'Traceroute konnte nicht ausgeführt werden. Ist das Kommando verfügbar?'; + } else { + $traceData = parseTraceroute($rawOutput); + if (empty($traceData)) { + $error = 'Keine Hops gefunden. Prüfen Sie den Hostnamen oder versuchen Sie es später erneut.'; + } + } + } +} + +if (empty($traceData)) { + $traceData = getSampleTrace(); + if ($error === '') { + $error = 'Es werden Beispieldaten angezeigt. Starten Sie eine Abfrage, um echte Traceroute-Daten zu sehen.'; + } +} + +$positions = generateGalaxyLayout(count($traceData)); +$traceWithPositions = array_map(function ($hop, $index) use ($positions) { + return $hop + [ + 'position' => $positions[$index] ?? ['x' => 0, 'y' => 0, 'z' => 0], + ]; +}, $traceData, array_keys($traceData)); + +function parseTraceroute(string $raw): array +{ + $lines = preg_split('/\r?\n/', trim($raw)); + if (!$lines) { + return []; + } + + $hops = []; + foreach ($lines as $line) { + if (!preg_match('/^\s*(\d+)\s+(.+)/', $line, $parts)) { + continue; + } + + $hopNumber = (int) $parts[1]; + $rest = $parts[2]; + + preg_match('/([0-9a-fA-F:\.\-]+|\*)/', $rest, $ipMatch); + $ip = $ipMatch[1] ?? '*'; + $ip = $ip === '*' ? 'Zeitüberschreitung' : $ip; + + preg_match_all('/(\d+\.\d+)\s+ms/', $rest, $latencyMatches); + $latencies = array_map('floatval', $latencyMatches[1] ?? []); + $avgLatency = !empty($latencies) ? round(array_sum($latencies) / count($latencies), 2) : null; + + $hops[] = [ + 'hop' => $hopNumber, + 'ip' => $ip, + 'avgLatency' => $avgLatency, + 'raw' => trim($line), + ]; + } + + return $hops; +} + +function getSampleTrace(): array +{ + return [ + ['hop' => 1, 'ip' => '192.168.0.1', 'avgLatency' => 1.23, 'raw' => '1 192.168.0.1 1.12 ms 1.20 ms 1.38 ms'], + ['hop' => 2, 'ip' => '10.12.34.1', 'avgLatency' => 9.87, 'raw' => '2 10.12.34.1 9.44 ms 9.90 ms 10.29 ms'], + ['hop' => 3, 'ip' => '172.16.5.4', 'avgLatency' => 18.54, 'raw' => '3 172.16.5.4 18.12 ms 18.45 ms 18.71 ms'], + ['hop' => 4, 'ip' => '198.51.100.12', 'avgLatency' => 27.42, 'raw' => '4 198.51.100.12 27.03 ms 27.54 ms 27.68 ms'], + ['hop' => 5, 'ip' => '203.0.113.5', 'avgLatency' => 36.18, 'raw' => '5 203.0.113.5 35.82 ms 36.19 ms 36.52 ms'], + ['hop' => 6, 'ip' => '93.184.216.34', 'avgLatency' => 48.91, 'raw' => '6 93.184.216.34 48.44 ms 48.90 ms 49.39 ms'], + ]; +} + +function generateGalaxyLayout(int $count): array +{ + $positions = []; + $radiusStep = 28; + $angleOffset = pi() * (3 - sqrt(5)); // Golden angle + $heightStep = 12; + + for ($i = 0; $i < $count; $i++) { + $radius = ($i + 1) * 4 + ($i % 2 === 0 ? $radiusStep : $radiusStep * 0.6); + $angle = $i * $angleOffset; + $y = ($i - $count / 2) * $heightStep; + + $positions[] = [ + 'x' => cos($angle) * $radius, + 'y' => $y, + 'z' => sin($angle) * $radius, + ]; + } + + return $positions; +} +?> + + + + + + HyperTracer 3D + + + + + + +
+

HyperTracer 3D

+
+ + + +
+
+
+ +
+ +
+
Hop 1:
+
+
+ WebGL konnte nicht initialisiert werden. Bitte verwenden Sie einen modernen Browser oder aktivieren Sie WebGL. +
+
+
+ + + From 349f9a06cf15576f1ee2ffdcec6a87526d122256 Mon Sep 17 00:00:00 2001 From: Metacube Date: Fri, 7 Nov 2025 15:14:57 +0100 Subject: [PATCH 2/3] Add modernized webcam experience in index3 --- index3.php | 1138 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1138 insertions(+) create mode 100644 index3.php diff --git a/index3.php b/index3.php new file mode 100644 index 0000000..f3f1c13 --- /dev/null +++ b/index3.php @@ -0,0 +1,1138 @@ + [ + 'title' => 'Aurora Weather Livecam', + 'welcome' => 'Willkommen bei der Aurora Live Webcam', + 'subline' => 'Livebilder, Zeitraffer, Archiv & Community – alles in einem sonnigen Dashboard.', + 'live' => 'Live', + 'timelapse' => 'Zeitraffer', + 'archive' => 'Archiv', + 'gallery' => 'Galerie', + 'community' => 'Community', + 'contact' => 'Kontakt', + 'guestbook' => 'Gästebuch', + 'send' => 'Senden', + 'name' => 'Name', + 'email' => 'E-Mail', + 'message' => 'Nachricht', + 'screenshot' => 'Screenshot', + 'clip' => 'Clip aufnehmen', + 'download' => 'Neueste Aufnahme laden', + 'calendar_title' => 'Visueller Wetterkalender', + 'language' => 'Sprache', + 'rating' => 'Bewertung', + 'comment' => 'Kommentar', + 'add_entry' => 'Eintrag hinzufügen', + 'view_all' => 'Alle anzeigen', + 'privacy' => 'Privatsphäre', + 'share' => 'Teilen', + 'pip' => 'Bild-in-Bild', + 'stats' => 'Stream-Status', + ], + 'en' => [ + 'title' => 'Aurora Weather Livecam', + 'welcome' => 'Welcome to the Aurora Live Webcam', + 'subline' => 'Live footage, timelapse, archive & community – all in one sunny dashboard.', + 'live' => 'Live', + 'timelapse' => 'Timelapse', + 'archive' => 'Archive', + 'gallery' => 'Gallery', + 'community' => 'Community', + 'contact' => 'Contact', + 'guestbook' => 'Guestbook', + 'send' => 'Send', + 'name' => 'Name', + 'email' => 'Email', + 'message' => 'Message', + 'screenshot' => 'Screenshot', + 'clip' => 'Record clip', + 'download' => 'Download latest capture', + 'calendar_title' => 'Visual Weather Calendar', + 'language' => 'Language', + 'rating' => 'Rating', + 'comment' => 'Comment', + 'add_entry' => 'Add entry', + 'view_all' => 'View all', + 'privacy' => 'Privacy', + 'share' => 'Share', + 'pip' => 'Picture-in-Picture', + 'stats' => 'Stream status', + ], + 'fr' => [ + 'title' => 'Aurora Weather Livecam', + 'welcome' => 'Bienvenue sur la webcam Aurora', + 'subline' => 'Images en direct, time-lapse, archive et communauté – réunis dans un tableau de bord ensoleillé.', + 'live' => 'Direct', + 'timelapse' => 'Accéléré', + 'archive' => 'Archive', + 'gallery' => 'Galerie', + 'community' => 'Communauté', + 'contact' => 'Contact', + 'guestbook' => 'Livre d’or', + 'send' => 'Envoyer', + 'name' => 'Nom', + 'email' => 'E-mail', + 'message' => 'Message', + 'screenshot' => 'Capture', + 'clip' => 'Enregistrer un clip', + 'download' => 'Télécharger la dernière capture', + 'calendar_title' => 'Calendrier météo visuel', + 'language' => 'Langue', + 'rating' => 'Évaluation', + 'comment' => 'Commentaire', + 'add_entry' => 'Ajouter une entrée', + 'view_all' => 'Tout voir', + 'privacy' => 'Confidentialité', + 'share' => 'Partager', + 'pip' => 'Picture-in-Picture', + 'stats' => 'Statut du flux', + ], + 'it' => [ + 'title' => 'Aurora Weather Livecam', + 'welcome' => 'Benvenuti alla webcam Aurora', + 'subline' => 'Live, time-lapse, archivio e community – tutto in un dashboard soleggiato.', + 'live' => 'Live', + 'timelapse' => 'Time-lapse', + 'archive' => 'Archivio', + 'gallery' => 'Galleria', + 'community' => 'Community', + 'contact' => 'Contatto', + 'guestbook' => 'Libro degli ospiti', + 'send' => 'Invia', + 'name' => 'Nome', + 'email' => 'E-mail', + 'message' => 'Messaggio', + 'screenshot' => 'Screenshot', + 'clip' => 'Registra clip', + 'download' => 'Scarica l’ultima registrazione', + 'calendar_title' => 'Calendario Meteo Visivo', + 'language' => 'Lingua', + 'rating' => 'Valutazione', + 'comment' => 'Commento', + 'add_entry' => 'Aggiungi', + 'view_all' => 'Vedi tutto', + 'privacy' => 'Privacy', + 'share' => 'Condividi', + 'pip' => 'Picture-in-Picture', + 'stats' => 'Stato del flusso', + ], + ]; + + public function getCurrentLocale(): string + { + if (isset($_POST['language'])) { + $_SESSION['lang'] = $_POST['language']; + } + return $_SESSION['lang'] ?? 'de'; + } + + public function get(string $key, ?string $locale = null): string + { + $locale = $locale ?? $this->getCurrentLocale(); + $locale = array_key_exists($locale, $this->translations) ? $locale : 'de'; + return $this->translations[$locale][$key] ?? $this->translations['de'][$key] ?? $key; + } + + public function getAllTranslations(): array + { + return $this->translations; + } +} + +class WebcamManager +{ + private string $videoSrc; + + public function __construct(string $videoSrc = STREAM_SOURCE) + { + $this->videoSrc = $videoSrc; + } + + public function getVideoSrc(): string + { + return $this->videoSrc; + } + + public function getImageFiles(): array + { + $files = glob(IMAGE_DIR . '/screenshot_*.jpg'); + rsort($files); + return $files; + } + + public function getLatestVideo(): ?string + { + $videos = glob(VIDEO_DIR . '/*.mp4'); + if (!$videos) { + return null; + } + usort($videos, static fn(string $a, string $b) => filemtime($b) <=> filemtime($a)); + return $videos[0]; + } + + public function captureSnapshot(): array + { + $outputFile = 'snapshot_' . date('YmdHis') . '.jpg'; + $targetPath = UPLOAD_DIR . '/' . $outputFile; + $command = sprintf( + "ffmpeg -y -i %s -i %s -filter_complex 'overlay=main_w-overlay_w-10:10' -frames:v 1 -q:v 2 %s", + escapeshellarg($this->videoSrc), + escapeshellarg(LOGO_PATH), + escapeshellarg($targetPath) + ); + exec($command, $output, $returnVar); + if ($returnVar !== 0 || !file_exists($targetPath)) { + return ['success' => false, 'message' => 'Snapshot konnte nicht erstellt werden.']; + } + return ['success' => true, 'file' => basename($targetPath)]; + } + + public function captureClip(int $duration = 10): array + { + $outputFile = 'sequence_' . date('YmdHis') . '.mp4'; + $targetPath = UPLOAD_DIR . '/' . $outputFile; + $command = sprintf( + "ffmpeg -y -i %s -i %s -filter_complex 'overlay=10:10' -t %d -c:v libx264 -preset fast -crf 23 %s", + escapeshellarg($this->videoSrc), + escapeshellarg(LOGO_PATH), + $duration, + escapeshellarg($targetPath) + ); + exec($command, $output, $returnVar); + if ($returnVar !== 0 || !file_exists($targetPath)) { + return ['success' => false, 'message' => 'Clip konnte nicht erstellt werden.']; + } + return ['success' => true, 'file' => basename($targetPath)]; + } + + public function getStreamStats(): array + { + return [ + 'bitrate' => rand(4200, 6200), + 'latency' => rand(3, 9), + 'updated' => date('H:i:s'), + ]; + } + + public function getGallery(): array + { + $images = []; + foreach (glob(GALLERY_DIR . '/*.{jpg,jpeg,png,gif}', GLOB_BRACE) as $file) { + $images[] = [ + 'src' => str_replace(__DIR__ . '/', '', $file), + 'date' => date('Y-m-d H:i', filemtime($file)) + ]; + } + usort($images, static fn(array $a, array $b) => strcmp($b['date'], $a['date'])); + return array_slice($images, 0, 20); + } +} + +class VisualCalendarManager +{ + private array $monthNames = [ + 1 => ['de' => 'Januar', 'en' => 'January', 'it' => 'Gennaio', 'fr' => 'Janvier'], + 2 => ['de' => 'Februar', 'en' => 'February', 'it' => 'Febbraio', 'fr' => 'Février'], + 3 => ['de' => 'März', 'en' => 'March', 'it' => 'Marzo', 'fr' => 'Mars'], + 4 => ['de' => 'April', 'en' => 'April', 'it' => 'Aprile', 'fr' => 'Avril'], + 5 => ['de' => 'Mai', 'en' => 'May', 'it' => 'Maggio', 'fr' => 'Mai'], + 6 => ['de' => 'Juni', 'en' => 'June', 'it' => 'Giugno', 'fr' => 'Juin'], + 7 => ['de' => 'Juli', 'en' => 'July', 'it' => 'Luglio', 'fr' => 'Juillet'], + 8 => ['de' => 'August', 'en' => 'August', 'it' => 'Agosto', 'fr' => 'Août'], + 9 => ['de' => 'September', 'en' => 'September', 'it' => 'Settembre', 'fr' => 'Septembre'], + 10 => ['de' => 'Oktober', 'en' => 'October', 'it' => 'Ottobre', 'fr' => 'Octobre'], + 11 => ['de' => 'November', 'en' => 'November', 'it' => 'Novembre', 'fr' => 'Novembre'], + 12 => ['de' => 'Dezember', 'en' => 'December', 'it' => 'Dicembre', 'fr' => 'Décembre'], + ]; + + public function getMonthData(int $year, int $month): array + { + $firstDay = new DateTimeImmutable(sprintf('%04d-%02d-01', $year, $month)); + $daysInMonth = (int) $firstDay->format('t'); + $days = []; + for ($day = 1; $day <= $daysInMonth; $day++) { + $date = sprintf('%04d%02d%02d', $year, $month, $day); + $pattern = VIDEO_DIR . "/daily_video_{$date}_*.mp4"; + $matches = glob($pattern) ?: []; + $days[] = [ + 'day' => $day, + 'hasVideos' => !empty($matches), + 'count' => count($matches) + ]; + } + return [ + 'year' => $year, + 'month' => $month, + 'monthName' => $this->monthNames[$month] ?? $this->monthNames[date('n')], + 'days' => $days, + ]; + } + + public function getVideosForDate(int $year, int $month, int $day): array + { + $date = sprintf('%04d%02d%02d', $year, $month, $day); + $videos = []; + foreach (glob(VIDEO_DIR . "/daily_video_{$date}_*.mp4") as $file) { + $videos[] = [ + 'file' => basename($file), + 'size' => filesize($file), + 'time' => date('H:i', filemtime($file)), + ]; + } + return $videos; + } +} + +class GuestbookManager +{ + private array $entries = []; + + public function __construct() + { + $content = json_decode((string) file_get_contents(GUESTBOOK_FILE), true); + $this->entries = is_array($content) ? $content : []; + } + + public function addEntry(string $name, string $message, int $rating = 5): array + { + $entry = [ + 'name' => htmlspecialchars($name, ENT_QUOTES, 'UTF-8'), + 'message' => htmlspecialchars($message, ENT_QUOTES, 'UTF-8'), + 'rating' => max(1, min(5, $rating)), + 'created' => date('Y-m-d H:i:s') + ]; + $this->entries[] = $entry; + file_put_contents(GUESTBOOK_FILE, json_encode($this->entries, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); + return $entry; + } + + public function getEntries(int $limit = 10): array + { + return array_slice(array_reverse($this->entries), 0, $limit); + } +} + +class ContactManager +{ + private string $adminEmail = 'metacube@gmail.com'; + private string $gmailUser = 'metacube@gmail.com'; + private string $gmailAppPassword = 'qggk hsxz fdkq jgxa'; + + public function handle(string $name, string $email, string $message): array + { + if ($name === '' || $email === '' || $message === '') { + return ['success' => false, 'message' => 'Bitte alle Felder ausfüllen.']; + } + if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + return ['success' => false, 'message' => 'Bitte eine gültige E-Mail-Adresse verwenden.']; + } + if (mb_strlen($message) < 10) { + return ['success' => false, 'message' => 'Die Nachricht ist zu kurz.']; + } + + $payload = [ + 'name' => htmlspecialchars(trim($name), ENT_QUOTES, 'UTF-8'), + 'email' => filter_var(trim($email), FILTER_SANITIZE_EMAIL), + 'message' => htmlspecialchars(trim($message), ENT_QUOTES, 'UTF-8'), + 'date' => date('Y-m-d H:i:s'), + 'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown', + 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown', + ]; + + $existing = json_decode((string) file_get_contents(FEEDBACK_FILE), true); + $existing = is_array($existing) ? $existing : []; + $existing[] = $payload; + file_put_contents(FEEDBACK_FILE, json_encode($existing, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); + + $mail = new PHPMailer(true); + try { + $mail->isSMTP(); + $mail->Host = 'smtp.gmail.com'; + $mail->SMTPAuth = true; + $mail->Username = $this->gmailUser; + $mail->Password = $this->gmailAppPassword; + $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; + $mail->Port = 587; + $mail->setFrom($this->gmailUser, 'Aurora Livecam'); + $mail->addAddress($this->adminEmail); + $mail->addReplyTo($payload['email'], $payload['name']); + $mail->isHTML(true); + $mail->CharSet = 'UTF-8'; + $mail->Subject = 'Neue Kontaktanfrage von ' . $payload['name']; + $mail->Body = '

Aurora Kontakt

' . + '

Name: ' . $payload['name'] . '

' . + '

E-Mail: ' . $payload['email'] . '

' . + '

Nachricht:
' . nl2br($payload['message']) . '

' . + '
Gesendet am ' . $payload['date'] . ' | IP ' . $payload['ip'] . ''; + $mail->send(); + } catch (Exception $e) { + error_log('Mail error: ' . $mail->ErrorInfo); + return ['success' => false, 'message' => 'Nachricht gespeichert, E-Mail konnte nicht gesendet werden.']; + } + + return ['success' => true, 'message' => 'Vielen Dank! Wir melden uns zeitnah.']; + } +} + +class AdminManager +{ + public function isAdmin(): bool + { + return isset($_SESSION['admin']) && $_SESSION['admin'] === true; + } + + public function login(string $username, string $password): bool + { + if ($username === 'admin' && $password === 'sonne4000$$$$Q') { + $_SESSION['admin'] = true; + return true; + } + return false; + } +} + +$languageManager = new LanguageManager(); +$locale = $languageManager->getCurrentLocale(); +$webcamManager = new WebcamManager(); +$calendarManager = new VisualCalendarManager(); +$guestbookManager = new GuestbookManager(); +$contactManager = new ContactManager(); +$adminManager = new AdminManager(); + +if (isset($_GET['download_video']) && $_GET['download_video'] === 'latest') { + $latest = $webcamManager->getLatestVideo(); + if ($latest && file_exists($latest)) { + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header('Content-Disposition: attachment; filename="' . basename($latest) . '"'); + header('Content-Length: ' . filesize($latest)); + readfile($latest); + exit; + } + echo 'Kein Video gefunden.'; + exit; +} + +if (isset($_GET['api'])) { + $action = $_GET['api']; + switch ($action) { + case 'images': + respond_json(['images' => array_map(static fn(string $p) => str_replace(__DIR__ . '/', '', $p), $webcamManager->getImageFiles())]); + case 'gallery': + respond_json(['gallery' => $webcamManager->getGallery()]); + case 'calendar': + $year = isset($_GET['year']) ? (int) $_GET['year'] : (int) date('Y'); + $month = isset($_GET['month']) ? (int) $_GET['month'] : (int) date('n'); + respond_json(['calendar' => $calendarManager->getMonthData($year, $month)]); + case 'calendar_videos': + $year = (int) ($_GET['year'] ?? date('Y')); + $month = (int) ($_GET['month'] ?? date('n')); + $day = (int) ($_GET['day'] ?? date('j')); + respond_json(['videos' => $calendarManager->getVideosForDate($year, $month, $day)]); + case 'guestbook': + respond_json(['entries' => $guestbookManager->getEntries(50)]); + case 'stream_stats': + respond_json(['stats' => $webcamManager->getStreamStats()]); + default: + respond_json(['message' => 'Unbekannte API-Anfrage.'], 404); + } +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? ''; + switch ($action) { + case 'capture_snapshot': + respond_json($webcamManager->captureSnapshot()); + case 'capture_clip': + $duration = isset($_POST['duration']) ? max(5, min(120, (int) $_POST['duration'])) : 10; + respond_json($webcamManager->captureClip($duration)); + case 'guestbook_add': + $name = $_POST['name'] ?? ''; + $message = $_POST['message'] ?? ''; + $rating = isset($_POST['rating']) ? (int) $_POST['rating'] : 5; + if ($name === '' || $message === '') { + respond_json(['success' => false, 'message' => 'Name und Nachricht sind erforderlich.'], 422); + } + respond_json(['success' => true, 'entry' => $guestbookManager->addEntry($name, $message, $rating)]); + case 'contact_send': + $name = $_POST['name'] ?? ''; + $email = $_POST['email'] ?? ''; + $message = $_POST['message'] ?? ''; + respond_json($contactManager->handle($name, $email, $message)); + case 'admin_login': + $username = $_POST['username'] ?? ''; + $password = $_POST['password'] ?? ''; + respond_json(['success' => $adminManager->login($username, $password)]); + case 'set_language': + $_SESSION['lang'] = $_POST['language'] ?? 'de'; + respond_json(['success' => true, 'language' => $_SESSION['lang']]); + default: + respond_json(['message' => 'Unbekannte Aktion.'], 400); + } +} + +$translations = $languageManager->getAllTranslations(); +?> + + + + + + <?= htmlspecialchars($languageManager->get('title', $locale)) ?> + + + + + + +
+

get('welcome', $locale)) ?>

+

get('subline', $locale)) ?>

+
+ +
+
+
+ +
+ + + + ⬇️ get('download', $locale)) ?> + + + +
+
+
+
+

+ get('stats', $locale)) ?> + -- +

+

+ -- +

+ +
+
+

+ get('language', $locale)) ?> +

+
+ $values): ?> + + +
+
+
+

get('gallery', $locale)) ?>

+
+
+
+
+ +
+

get('calendar_title', $locale)) ?>

+
+
+
+ +
+

get('guestbook', $locale)) ?>

+
+ + + + + +
+
+
+ +
+

get('contact', $locale)) ?>

+
+ + + + + +
+
+
+
+ +
+ © Aurora Weather Livecam · get('privacy', $locale)) ?> · tiny view +
+ + + + From fbe95175885ff58e7af2bcb2c9065b1d4c54abe1 Mon Sep 17 00:00:00 2001 From: Metacube Date: Sun, 9 Nov 2025 12:29:52 +0100 Subject: [PATCH 3/3] Add canvas-based traceroute visualization --- route3.php | 673 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 route3.php diff --git a/route3.php b/route3.php new file mode 100644 index 0000000..8640f33 --- /dev/null +++ b/route3.php @@ -0,0 +1,673 @@ +&1'; + $rawOutput = shell_exec($command); + if ($rawOutput === null) { + $error = 'Traceroute konnte nicht ausgeführt werden. Ist das Kommando verfügbar?'; + } else { + $traceData = parseTraceroute($rawOutput); + if (empty($traceData)) { + $error = 'Keine Hops gefunden. Prüfen Sie den Hostnamen oder versuchen Sie es später erneut.'; + } + } + } +} + +if (empty($traceData)) { + $traceData = getSampleTrace(); + if ($error === '') { + $error = 'Es werden Beispieldaten angezeigt. Starten Sie eine Abfrage, um echte Traceroute-Daten zu sehen.'; + } +} + +$positions = generateHelixLayout(count($traceData)); +$traceWithPositions = array_map(function ($hop, $index) use ($positions) { + return $hop + [ + 'position' => $positions[$index] ?? ['x' => 0, 'y' => 0, 'z' => 0], + ]; +}, $traceData, array_keys($traceData)); + +function parseTraceroute(string $raw): array +{ + $lines = preg_split('/\r?\n/', trim($raw)); + if (!$lines) { + return []; + } + + $hops = []; + foreach ($lines as $line) { + if (!preg_match('/^\s*(\d+)\s+(.+)/', $line, $parts)) { + continue; + } + + $hopNumber = (int) $parts[1]; + $rest = $parts[2]; + + preg_match('/([0-9a-fA-F:\.\-]+|\*)/', $rest, $ipMatch); + $ip = $ipMatch[1] ?? '*'; + $ip = $ip === '*' ? 'Zeitüberschreitung' : $ip; + + preg_match_all('/(\d+\.\d+)\s+ms/', $rest, $latencyMatches); + $latencies = array_map('floatval', $latencyMatches[1] ?? []); + $avgLatency = !empty($latencies) ? round(array_sum($latencies) / count($latencies), 2) : null; + + $hops[] = [ + 'hop' => $hopNumber, + 'ip' => $ip, + 'avgLatency' => $avgLatency, + 'raw' => trim($line), + ]; + } + + return $hops; +} + +function getSampleTrace(): array +{ + return [ + ['hop' => 1, 'ip' => '192.168.0.1', 'avgLatency' => 1.23, 'raw' => '1 192.168.0.1 1.12 ms 1.20 ms 1.38 ms'], + ['hop' => 2, 'ip' => '10.12.34.1', 'avgLatency' => 9.87, 'raw' => '2 10.12.34.1 9.44 ms 9.90 ms 10.29 ms'], + ['hop' => 3, 'ip' => '172.16.5.4', 'avgLatency' => 18.54, 'raw' => '3 172.16.5.4 18.12 ms 18.45 ms 18.71 ms'], + ['hop' => 4, 'ip' => '198.51.100.12', 'avgLatency' => 27.42, 'raw' => '4 198.51.100.12 27.03 ms 27.54 ms 27.68 ms'], + ['hop' => 5, 'ip' => '203.0.113.5', 'avgLatency' => 36.18, 'raw' => '5 203.0.113.5 35.82 ms 36.19 ms 36.52 ms'], + ['hop' => 6, 'ip' => '93.184.216.34', 'avgLatency' => 48.91, 'raw' => '6 93.184.216.34 48.44 ms 48.90 ms 49.39 ms'], + ]; +} + +function generateHelixLayout(int $count): array +{ + $positions = []; + $radius = 160; + $heightStep = 70; + $angleStep = pi() / 3; + + for ($i = 0; $i < $count; $i++) { + $angle = $i * $angleStep; + $y = ($i - ($count - 1) / 2) * $heightStep; + $positions[] = [ + 'x' => cos($angle) * $radius, + 'y' => $y, + 'z' => sin($angle) * $radius, + ]; + } + + return $positions; +} +?> + + + + + + HyperTracer Canvas Edition + + + + + + +
+

HyperTracer — Canvas Flight Deck

+
+ + +
+
+ + + +
+ + +
+ + + +