v2/index.php hinzugefügt
This commit is contained in:
+145
@@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
// status.php - API & Frontend in einer Datei
|
||||||
|
|
||||||
|
$db_file = __DIR__ . '/status.sqlite';
|
||||||
|
$pdo = new PDO('sqlite:' . $db_file);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
// Tabelle erstellen, falls nicht existent
|
||||||
|
$pdo->exec("CREATE TABLE IF NOT EXISTS nodes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
server TEXT,
|
||||||
|
client TEXT,
|
||||||
|
status TEXT,
|
||||||
|
handshake TEXT,
|
||||||
|
last_seen DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE(server, client)
|
||||||
|
)");
|
||||||
|
|
||||||
|
// --- TEIL 1: API (Daten empfangen) ---
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
if ($data && isset($data['server']) && isset($data['client'])) {
|
||||||
|
$stmt = $pdo->prepare("
|
||||||
|
INSERT INTO nodes (server, client, status, handshake, last_seen)
|
||||||
|
VALUES (:server, :client, :status, :handshake, datetime('now'))
|
||||||
|
ON CONFLICT(server, client)
|
||||||
|
DO UPDATE SET status=:status, handshake=:handshake, last_seen=datetime('now')
|
||||||
|
");
|
||||||
|
$stmt->execute([
|
||||||
|
':server' => $data['server'],
|
||||||
|
':client' => $data['client'],
|
||||||
|
':status' => $data['status'],
|
||||||
|
':handshake' => $data['handshake']
|
||||||
|
]);
|
||||||
|
echo json_encode(["success" => true]);
|
||||||
|
} else {
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["error" => "Invalid payload"]);
|
||||||
|
}
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- TEIL 2: API (Daten für Frontend ausliefern) ---
|
||||||
|
if (isset($_GET['api'])) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$stmt = $pdo->query("SELECT * FROM nodes ORDER BY server, client");
|
||||||
|
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- TEIL 3: HTML FRONTEND ---
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Gateway Status Dashboard</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
background-color: #121212;
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0;
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
h1 { border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 30px; }
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: #1e1e1e;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-left: 6px solid #555;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.card:hover { transform: translateY(-3px); }
|
||||||
|
.card.online { border-left-color: #00C851; }
|
||||||
|
.card.offline { border-left-color: #ff4444; }
|
||||||
|
.card h3 { margin: 0 0 10px 0; font-size: 1.3rem; }
|
||||||
|
.card p { margin: 6px 0; font-size: 0.9rem; color: #cccccc; }
|
||||||
|
.server-badge {
|
||||||
|
display: inline-block;
|
||||||
|
background: #333;
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #aaa;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.time { color: #888; font-size: 0.8rem; margin-top: 15px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>🟢 Gateway Status Overview</h1>
|
||||||
|
<div class="grid" id="node-grid">
|
||||||
|
<p>Lade Status-Daten...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function fetchStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('?api=1');
|
||||||
|
const data = await response.json();
|
||||||
|
const grid = document.getElementById('node-grid');
|
||||||
|
grid.innerHTML = '';
|
||||||
|
|
||||||
|
data.forEach(node => {
|
||||||
|
// Zeitstempel parsen (SQLite liefert UTC)
|
||||||
|
const lastSeen = new Date(node.last_seen + " UTC");
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
// Differenz in Minuten berechnen
|
||||||
|
const diffMinutes = (now - lastSeen) / 1000 / 60;
|
||||||
|
|
||||||
|
// Node ist online, wenn status="true" UND der letzte Ping nicht älter als 5 Minuten ist
|
||||||
|
const isOnline = (node.status === 'true' && diffMinutes < 5);
|
||||||
|
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = `card ${isOnline ? 'online' : 'offline'}`;
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="server-badge">${node.server}</div>
|
||||||
|
<h3>${node.client}</h3>
|
||||||
|
<p><strong>Handshake:</strong> ${node.handshake}</p>
|
||||||
|
<p class="time">Update: ${lastSeen.toLocaleTimeString()} (vor ${Math.round(diffMinutes)} Min.)</p>
|
||||||
|
`;
|
||||||
|
grid.appendChild(card);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Fehler beim Abrufen der Daten:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchStatus();
|
||||||
|
setInterval(fetchStatus, 30000); // Alle 30 Sekunden aktualisieren
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user