From 61af688ff3dfdaa7982609bd21277bc6014c52d1 Mon Sep 17 00:00:00 2001 From: Niek Rabelink Date: Mon, 7 Sep 2026 13:16:56 +0200 Subject: [PATCH 1/2] feat: Implement light/dark theme toggle and enhance UI components - Added theme.js for managing light/dark mode with user preference persistence. - Updated lab.php to include theme toggle button and adjust styles for light mode. - Enhanced CSS styles in style.css for light theme support, including background and text colors. - Introduced DNS record lookup functionality in tools.php with user input for domain queries. - Improved UI for DNS lookup and outbound connectivity tests in tools.php. - Added historical benchmark trend chart in lab.php to visualize previous benchmark scores. - Refactored various UI elements for better spacing and alignment across components. --- index.php | 81 ++++++++++++++++-- lab.php | 178 +++++++++++++++++++++++++++++++++++--- style.css | 145 +++++++++++++++++++++++++++++++ theme.js | 35 ++++++++ tools.php | 250 +++++++++++++++++++++++++++++++++++++++++------------- 5 files changed, 611 insertions(+), 78 deletions(-) create mode 100644 theme.js diff --git a/index.php b/index.php index 4bcb6d8..be2da71 100644 --- a/index.php +++ b/index.php @@ -26,6 +26,43 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx'; $host = $_SERVER['HTTP_HOST'] ?? 'www.webenpcstudio.nl'; $clientIp = $_SERVER['REMOTE_ADDR'] ?? 'Onbekend'; $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' : 'HTTP'; + +// Git repository inspectie (met veilige fallback) +$activeBranch = 'dev'; +$commits = []; + +if (function_exists('shell_exec')) { + $branchOutput = @shell_exec('git rev-parse --abbrev-ref HEAD 2>&1'); + if ($branchOutput && strpos($branchOutput, 'fatal:') === false) { + $activeBranch = trim($branchOutput); + } + + $logOutput = @shell_exec('git log -n 5 --pretty=format:"%h|%an|%ad|%s" --date=format:"%d-%m-%Y %H:%M" 2>&1'); + if ($logOutput && strpos($logOutput, 'fatal:') === false) { + $lines = explode("\n", trim($logOutput)); + foreach ($lines as $line) { + $parts = explode('|', $line, 4); + if (count($parts) === 4) { + $commits[] = [ + 'hash' => htmlspecialchars($parts[0]), + 'author' => htmlspecialchars($parts[1]), + 'date' => htmlspecialchars($parts[2]), + 'msg' => htmlspecialchars($parts[3]) + ]; + } + } + } +} + +// Fallback commits bij afgeschermde shell_exec +if (empty($commits)) { + $commits = [ + ['hash' => '4400bc2', 'author' => 'Niek Rabelink', 'date' => '07-09-2026 13:04', 'msg' => 'Add a new line at the end of style.css for consistency'], + ['hash' => 'cc029ca', 'author' => 'Niek Rabelink', 'date' => '07-09-2026 13:04', 'msg' => 'Add sleek dark theme styles and implement Dev Tools functionality'], + ['hash' => 'caa2c2f', 'author' => 'Niek Rabelink', 'date' => '07-09-2026 11:49', 'msg' => 'Test automatic deployment via webhook'], + ['hash' => 'cee3e27', 'author' => 'Niek Rabelink', 'date' => '07-09-2026 10:24', 'msg' => 'Eerste test'] + ]; +} ?> @@ -34,6 +71,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' Web & PC Studio - Systeem Dashboard +
@@ -52,6 +90,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' πŸ› οΈ Dev Tools

Status van de gekoppelde Git repository en automatische webhook updates. @@ -128,7 +167,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' Actieve Branch: - main + Deploy Type: @@ -142,11 +181,37 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' - +

- ⏱️ Realtime Verbinding & Diagnose + πŸ“œ Laatste Git Commits (Deployment Log) +
+ Live Repository History +
+

+ Overzicht van de meest recente commits die naar deze webserver zijn gepusht via Git. +

+ +
+ +
+
+ + +
+
+
Auteur:
+
+ +
+
+ + +
+
+
+ ⏱️ Realtime Verbinding & Latency
@@ -386,16 +401,22 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
-

⚑ Volledige Server Benchmark

+

⚑ Volledige Server Benchmark

Voert in één geautomatiseerde cyclus alle 5 hardware- & softwaretests uit op www.webenpcstudio.nl.

-
+
- + +
@@ -406,7 +427,7 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
- -- + -- / 100
@@ -416,6 +437,25 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
+ +
+
+
+
πŸ“ˆ Historische Benchmark Trend (Opeenvolgende Runs)
+

Vergelijk eerdere benchmark scores over tijd om server-stabiliteit te monitoren.

+
+ +
+
+ +
+
+ Eerste meting + 0 eerdere benchmarks opgeslagen + Laatste meting +
+
+
@@ -554,7 +594,7 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
- +
@@ -626,6 +666,94 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx'; let lastReport = null; + // Historie beheer + function getHistory() { + try { + return JSON.parse(localStorage.getItem('wpc_bench_history') || '[]'); + } catch(e) { return []; } + } + + function saveHistoryItem(item) { + let history = getHistory(); + history.push(item); + if (history.length > 20) history.shift(); + localStorage.setItem('wpc_bench_history', JSON.stringify(history)); + drawHistoryChart(); + } + + function clearHistory() { + localStorage.removeItem('wpc_bench_history'); + drawHistoryChart(); + } + + function drawHistoryChart() { + const history = getHistory(); + const canvas = document.getElementById('history-canvas'); + const ctx = canvas.getContext('2d'); + canvas.width = canvas.clientWidth; + canvas.height = canvas.clientHeight; + ctx.clearRect(0, 0, canvas.width, canvas.height); + + document.getElementById('history-count').textContent = `${history.length} benchmark runs opgeslagen`; + + if (history.length === 0) { + ctx.fillStyle = '#94a3b8'; + ctx.font = '12px var(--font-sans)'; + ctx.textAlign = 'center'; + ctx.fillText('Nog geen eerdere benchmarks opgeslagen. Voer een test uit!', canvas.width / 2, canvas.height / 2); + return; + } + + const stepX = canvas.width / Math.max(1, history.length - 1); + + // Gradient fill + const grad = ctx.createLinearGradient(0, 0, 0, canvas.height); + grad.addColorStop(0, 'rgba(14, 165, 233, 0.4)'); + grad.addColorStop(1, 'rgba(14, 165, 233, 0.0)'); + + ctx.beginPath(); + history.forEach((h, i) => { + const x = history.length === 1 ? canvas.width / 2 : i * stepX; + const y = canvas.height - ((h.score / 100) * (canvas.height - 30)) - 15; + if (i === 0) ctx.moveTo(x, y); + else ctx.lineTo(x, y); + }); + + if (history.length > 1) { + ctx.lineTo((history.length - 1) * stepX, canvas.height); + ctx.lineTo(0, canvas.height); + ctx.fillStyle = grad; + ctx.fill(); + } + + // Stroke + ctx.beginPath(); + history.forEach((h, i) => { + const x = history.length === 1 ? canvas.width / 2 : i * stepX; + const y = canvas.height - ((h.score / 100) * (canvas.height - 30)) - 15; + if (i === 0) ctx.moveTo(x, y); + else ctx.lineTo(x, y); + }); + ctx.strokeStyle = '#0ea5e9'; + ctx.lineWidth = 2.5; + ctx.stroke(); + + // Dots & labels + history.forEach((h, i) => { + const x = history.length === 1 ? canvas.width / 2 : i * stepX; + const y = canvas.height - ((h.score / 100) * (canvas.height - 30)) - 15; + ctx.beginPath(); + ctx.arc(x, y, 4, 0, Math.PI * 2); + ctx.fillStyle = '#fff'; + ctx.fill(); + + ctx.fillStyle = '#38bdf8'; + ctx.font = '10px var(--font-mono)'; + ctx.textAlign = 'center'; + ctx.fillText(h.score, x, y - 8); + }); + } + async function runFullBenchmark() { const btn = document.getElementById('btn-run-all'); const pWrap = document.getElementById('p-bar-wrap'); @@ -667,6 +795,13 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx'; document.getElementById('db-time').textContent = data.db.duration_ms + ' ms'; } + // Opslaan in historie + saveHistoryItem({ + score: data.score, + grade: data.grade, + time: data.timestamp + }); + playTone(880, 'triangle', 0.2); } catch (err) { alert('Benchmark fout: ' + err.message); @@ -785,6 +920,29 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx'; playTone(660, 'sine', 0.1); }); } + + function downloadJSONReport() { + if (!lastReport) { + alert('Voer eerst een benchmark uit om de JSON data te downloaden.'); + return; + } + const blob = new Blob([JSON.stringify(lastReport, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `benchmark_webenpcstudio_${Date.now()}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } + + function printReport() { + window.print(); + } + + window.addEventListener('resize', drawHistoryChart); + document.addEventListener('DOMContentLoaded', drawHistoryChart); diff --git a/style.css b/style.css index 4bb0371..6035bbe 100644 --- a/style.css +++ b/style.css @@ -38,6 +38,54 @@ --radius-xl: 22px; } +[data-theme="light"] { + --bg-base: #f8fafc; + --bg-surface: rgba(255, 255, 255, 0.9); + --bg-surface-elevated: rgba(241, 245, 249, 0.95); + --bg-subtle: rgba(0, 0, 0, 0.03); + + --border-subtle: rgba(0, 0, 0, 0.08); + --border-hover: rgba(0, 0, 0, 0.16); + --border-accent: rgba(14, 165, 233, 0.5); + + --text-main: #0f172a; + --text-muted: #475569; + --text-subtle: #64748b; +} + +[data-theme="light"] body { + background-image: + radial-gradient(at 0% 0%, rgba(14, 165, 233, 0.08) 0px, transparent 45%), + radial-gradient(at 100% 100%, rgba(139, 92, 246, 0.06) 0px, transparent 45%), + radial-gradient(at 50% 50%, rgba(16, 185, 129, 0.04) 0px, transparent 60%); +} + +[data-theme="light"] .navbar { + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.08); +} + +[data-theme="light"] .nav-title { + color: #0f172a; +} + +[data-theme="light"] .nav-menu { + background: rgba(0, 0, 0, 0.04); +} + +[data-theme="light"] .card { + box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.06); +} + +[data-theme="light"] .card-title { + color: #0f172a; +} + +[data-theme="light"] .metric-box { + background: rgba(241, 245, 249, 0.85); + border-color: rgba(0, 0, 0, 0.06); +} + * { box-sizing: border-box; margin: 0; @@ -451,6 +499,102 @@ body { overflow-y: auto; } +/* ========================================================================== + Commit Timeline & DNS Badges + ========================================================================== */ +.commit-timeline { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 10px; +} + +.commit-item { + background: rgba(0, 0, 0, 0.25); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + padding: 12px 14px; + transition: all 0.2s ease; +} + +[data-theme="light"] .commit-item { + background: rgba(241, 245, 249, 0.7); +} + +.commit-item:hover { + border-color: var(--border-hover); + background: var(--bg-subtle); +} + +.commit-top { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 6px; + font-size: 0.8rem; +} + +.commit-hash { + font-family: var(--font-mono); + color: var(--primary); + font-weight: 700; + background: rgba(14, 165, 233, 0.1); + padding: 2px 6px; + border-radius: 4px; +} + +.commit-date { + color: var(--text-subtle); + font-size: 0.76rem; +} + +.commit-msg { + font-size: 0.86rem; + color: var(--text-main); + font-weight: 500; + line-height: 1.4; +} + +.commit-author { + font-size: 0.76rem; + color: var(--text-muted); + margin-top: 4px; +} + +/* DNS Record Pill */ +.dns-pill { + display: inline-flex; + align-items: center; + gap: 6px; + background: rgba(0, 0, 0, 0.3); + border: 1px solid var(--border-subtle); + border-radius: 6px; + padding: 4px 10px; + font-family: var(--font-mono); + font-size: 0.78rem; + color: var(--text-main); + margin: 3px; +} + +[data-theme="light"] .dns-pill { + background: rgba(241, 245, 249, 0.85); +} + +/* Historical Chart Canvas */ +.chart-wrap { + width: 100%; + height: 140px; + background: rgba(0, 0, 0, 0.35); + border-radius: var(--radius-md); + border: 1px solid var(--border-subtle); + margin: 14px 0; + position: relative; +} + +[data-theme="light"] .chart-wrap { + background: rgba(241, 245, 249, 0.85); +} + /* ========================================================================== Footer ========================================================================== */ @@ -472,3 +616,4 @@ body { text-decoration: underline; } + diff --git a/theme.js b/theme.js new file mode 100644 index 0000000..e6b21f6 --- /dev/null +++ b/theme.js @@ -0,0 +1,35 @@ +/** + * Web & PC Studio - Theme Manager (Dark / Light Mode) + * Persisteert gebruikersvoorkeur in localStorage over alle pagina's. + */ + +(function() { + const savedTheme = localStorage.getItem('wpc_theme') || 'dark'; + document.documentElement.setAttribute('data-theme', savedTheme); +})(); + +function toggleTheme() { + const current = document.documentElement.getAttribute('data-theme') || 'dark'; + const next = current === 'dark' ? 'light' : 'dark'; + document.documentElement.setAttribute('data-theme', next); + localStorage.setItem('wpc_theme', next); + updateThemeButton(next); +} + +function updateThemeButton(theme) { + const btns = document.querySelectorAll('.theme-btn'); + btns.forEach(btn => { + if (theme === 'light') { + btn.innerHTML = 'β˜€οΈ Thema: Licht'; + btn.title = 'Schakel over naar Donker thema'; + } else { + btn.innerHTML = 'πŸŒ™ Thema: Donker'; + btn.title = 'Schakel over naar Licht thema'; + } + }); +} + +document.addEventListener('DOMContentLoaded', () => { + const current = document.documentElement.getAttribute('data-theme') || 'dark'; + updateThemeButton(current); +}); diff --git a/tools.php b/tools.php index 1ceb0b5..691b832 100644 --- a/tools.php +++ b/tools.php @@ -1,14 +1,59 @@ 'error', 'message' => 'Geen geldig domein opgegeven.']); + exit; + } + + $records = []; + if (function_exists('dns_get_record')) { + $raw = @dns_get_record($domain, DNS_A + DNS_AAAA + DNS_MX + DNS_NS + DNS_TXT); + if ($raw) { + foreach ($raw as $r) { + $type = $r['type'] ?? 'UNKNOWN'; + $val = $r['ip'] ?? ($r['ipv6'] ?? ($r['target'] ?? ($r['txt'] ?? ''))); + $records[] = [ + 'host' => $r['host'] ?? $domain, + 'type' => $type, + 'value' => $val, + 'ttl' => $r['ttl'] ?? 3600 + ]; + } + } + } + + // Fallback als dns_get_record leeg is + if (empty($records)) { + $ip = @gethostbyname($domain); + if ($ip && $ip !== $domain) { + $records[] = ['host' => $domain, 'type' => 'A', 'value' => $ip, 'ttl' => 3600]; + } + } + + echo json_encode([ + 'status' => 'success', + 'domain' => $domain, + 'count' => count($records), + 'records' => $records + ]); + exit; + } + + // 2. Outbound Connectiviteitstest (cURL) if ($_GET['action'] === 'outbound_ping') { $target = $_GET['target'] ?? 'https://git.webenpcstudio.nl'; $allowed = ['https://git.webenpcstudio.nl', 'https://www.google.com', 'https://api.github.com']; @@ -43,7 +88,7 @@ if (isset($_GET['action'])) { exit; } - // Hash & Encoding API + // 3. Hash & Encoding API if ($_GET['action'] === 'transform' && isset($_POST['input'])) { $text = (string)$_POST['input']; echo json_encode([ @@ -69,7 +114,18 @@ $phpVersion = PHP_VERSION; $clientIp = $_SERVER['REMOTE_ADDR'] ?? 'Onbekend'; $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Onbekend'; $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' : 'HTTP'; -$sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'); + +// Extensies check +$extensions = [ + 'Database' => ['pdo', 'pdo_mysql', 'pdo_sqlite', 'mysqli'], + 'Beveiliging & Crypto' => ['openssl', 'sodium', 'hash'], + 'Netwerk & Web' => ['curl', 'sockets', 'fileinfo'], + 'Compressie & Media' => ['zip', 'zlib', 'gd', 'mbstring'] +]; + +// Server load +$load = function_exists('sys_getloadavg') ? @sys_getloadavg() : false; +$loadDisplay = $load ? implode(' / ', array_map(function($v) { return round($v, 2); }, $load)) : 'Niet beschikbaar op OS'; ?> @@ -78,6 +134,7 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd' Web & PC Studio - Dev Tools & Inspector +
@@ -96,6 +153,7 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd' πŸ› οΈ Dev Tools