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.
This commit is contained in:
2026-09-07 13:16:56 +02:00
parent 4400bc2640
commit 61af688ff3
5 changed files with 611 additions and 78 deletions
+190 -60
View File
@@ -1,14 +1,59 @@
<?php
/**
* Web & PC Studio - Dev Tools & System Inspector
* Technische inspectie en utilities voor serverbeheer en debugging.
* Technische inspectie en utilities voor serverbeheer, DNS en debugging.
*/
// PHP Backend AJAX handler
if (isset($_GET['action'])) {
header('Content-Type: application/json; charset=utf-8');
// Outbound Connectiviteitstest
// 1. DNS Record Lookup
if ($_GET['action'] === 'dns_lookup') {
$domain = trim($_GET['domain'] ?? 'webenpcstudio.nl');
$domain = preg_replace('#^https?://#', '', $domain);
$domain = rtrim(explode('/', $domain)[0], '.');
if (empty($domain)) {
echo json_encode(['status' => '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';
?>
<!DOCTYPE html>
<html lang="nl">
@@ -78,6 +134,7 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web &amp; PC Studio - Dev Tools &amp; Inspector</title>
<link rel="stylesheet" href="style.css">
<script src="theme.js"></script>
</head>
<body>
<div class="main-wrapper">
@@ -96,6 +153,7 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<a href="tools.php" class="nav-item active">🛠️ Dev Tools</a>
</div>
<div class="nav-status">
<button class="btn btn-secondary btn-sm theme-btn" onclick="toggleTheme()">🌙 Thema</button>
<span class="nav-badge">
<span class="pulse-dot"></span>
Git Live
@@ -107,79 +165,57 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<header class="hero">
<h1>Systeem &amp; Developer Tools</h1>
<p>
Inspecteer binnenkomende HTTP headers, valideer server-connectiviteit en gebruik realtime cryptografische encoding tools.
Realtime DNS lookup utility, HTTP header inspectie, PHP extensie matrix en outbound netwerk diagnostics.
</p>
</header>
<!-- Grid: 2 Kolommen -->
<div class="grid-2">
<!-- Paneel 1: HTTP Headers & Client Inspectie -->
<!-- Tool 1: DNS & Record Lookup -->
<div class="card">
<div class="card-header">
<div class="card-title">
<span>🔍</span> HTTP Request Inspector
<span>🔎</span> DNS &amp; Domein Lookup
</div>
<span class="badge-pill"><?= htmlspecialchars($protocol); ?></span>
<span class="badge-pill">Live DNS Query</span>
</div>
<p class="card-desc">
Reële headers zoals ontvangen door de Apache/Nginx webserver voor deze verbinding.
Vraag rechtstreeks vanaf deze server de A, AAAA, MX, NS en TXT records op van een domein.
</p>
<table class="data-table" style="margin-bottom: 16px;">
<tr>
<td class="label">Bezoekers IP:</td>
<td class="val"><?= htmlspecialchars($clientIp); ?></td>
</tr>
<tr>
<td class="label">Protocol / SSL:</td>
<td class="val"><?= htmlspecialchars($protocol); ?></td>
</tr>
<tr>
<td class="label">User-Agent:</td>
<td class="val" style="max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="<?= htmlspecialchars($userAgent); ?>">
<?= htmlspecialchars($userAgent); ?>
</td>
</tr>
</table>
<div class="card-header" style="margin-bottom: 8px;">
<span style="font-size: 0.8rem; color: var(--text-muted); font-weight: 600;">ALLE REQUEST HEADERS:</span>
<button class="btn btn-secondary btn-sm" onclick="copyHeaders()">📋 Kopieer</button>
<div style="display: flex; gap: 8px; margin-bottom: 14px;">
<input type="text" id="dns-input" value="webenpcstudio.nl"
style="flex: 1; background: rgba(0,0,0,0.3); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 8px 12px; color: var(--text-main); font-size: 0.88rem; outline: none;"
placeholder="bijv. webenpcstudio.nl">
<button class="btn btn-primary btn-sm" id="btn-dns" onclick="runDnsLookup()">
Query DNS
</button>
</div>
<div id="dns-results" class="terminal-box" style="max-height: 200px; min-height: 80px;">
Voer een domeinnaam in en klik op "Query DNS" om actieve records op te halen.
</div>
<div class="terminal-box" id="headers-box"><?php
if (!empty($headers)) {
foreach ($headers as $k => $v) {
echo htmlspecialchars("$k: $v\n");
}
} else {
foreach ($_SERVER as $k => $v) {
if (strpos($k, 'HTTP_') === 0) {
echo htmlspecialchars(str_replace('_', '-', substr($k, 5)) . ": $v\n");
}
}
}
?></div>
</div>
<!-- Paneel 2: Outbound Server Connectiviteit (cURL) -->
<!-- Tool 2: Outbound Server Connectiviteit (cURL) -->
<div class="card">
<div class="card-header">
<div class="card-title">
<span>🌐</span> Outbound Server Ping
<span>🌐</span> Outbound Server Ping (cURL)
</div>
<span class="badge-pill">cURL Test</span>
<span class="badge-pill">Verbindingstest</span>
</div>
<p class="card-desc">
Test of de webserver vanuit PHP naar buiten kan verbinden (bijv. voor Git webhooks of API syncs).
Test of de webserver vanuit PHP naar buiten kan verbinden (bijv. voor Git webhooks of API communicatie).
</p>
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
<select id="ping-target" class="btn btn-secondary" style="flex: 1; outline: none; background: #0b111e; color: #fff; cursor: pointer;">
<div style="display: flex; gap: 8px; margin-bottom: 14px;">
<select id="ping-target" class="btn btn-secondary" style="flex: 1; outline: none; background: #0b111e; color: var(--text-main); cursor: pointer; font-size: 0.84rem;">
<option value="https://git.webenpcstudio.nl">git.webenpcstudio.nl (Git Server)</option>
<option value="https://api.github.com">api.github.com (GitHub API)</option>
<option value="https://www.google.com">google.com (Algemene DNS/Web)</option>
<option value="https://www.google.com">google.com (Algemeen Web)</option>
</select>
<button class="btn btn-primary" id="btn-ping" onclick="runOutboundPing()">
<button class="btn btn-primary btn-sm" id="btn-ping" onclick="runOutboundPing()">
Ping
</button>
</div>
@@ -203,29 +239,90 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
</div>
</div>
<div id="ping-status-msg" style="font-size: 0.82rem; color: var(--text-muted); margin-top: 6px;">
Selecteer een doellocatie en klik op Ping om de netwerk-latency te meten.
<div id="ping-status-msg" style="font-size: 0.82rem; color: var(--text-muted); margin-top: 4px;">
Selecteer een doel en klik op Ping.
</div>
</div>
</div>
<!-- Rij 2: Live String, Hash & Epoch Transformer -->
<!-- Tool 3: PHP Extensies & Server Capabilities Matrix -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<div class="card-title">
<span>🧩</span> PHP Extensies &amp; Server Matrix
</div>
<span class="badge-pill success">PHP v<?= htmlspecialchars($phpVersion); ?></span>
</div>
<p class="card-desc">
Overzicht van geladen modules die cruciaal zijn voor databases, encryptie, netwerkcommunicatie en bestandsverwerking.
</p>
<div class="grid-2" style="margin-bottom: 0;">
<?php foreach ($extensions as $cat => $extList): ?>
<div class="metric-box" style="padding: 12px 14px;">
<div class="metric-label" style="margin-bottom: 8px; color: var(--primary); font-weight: 700;"><?= $cat; ?></div>
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
<?php foreach ($extList as $ext):
$isLoaded = extension_loaded($ext);
?>
<span class="badge-pill <?= $isLoaded ? 'success' : 'warning'; ?>" style="font-size: 0.76rem;">
<?= $isLoaded ? '✓' : '✗'; ?> <?= $ext; ?>
</span>
<?php endforeach; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<div style="margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--border-subtle); display: flex; justify-content: space-between; font-size: 0.82rem; color: var(--text-muted);">
<span>Server Load Average (1 / 5 / 15 min): <strong><?= htmlspecialchars($loadDisplay); ?></strong></span>
<span>Actief Werkgeheugen: <strong><?= round(memory_get_usage() / 1024 / 1024, 1); ?> MB</strong></span>
</div>
</div>
<!-- Tool 4: HTTP Headers Inspector -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<div class="card-title">
<span>🔍</span> Inkomende HTTP Request Headers
</div>
<button class="btn btn-secondary btn-sm" onclick="copyHeaders()">📋 Kopieer Headers</button>
</div>
<p class="card-desc">
Headers zoals ontvangen door de webserver voor de huidige verbinding van client <?= htmlspecialchars($clientIp); ?>.
</p>
<div class="terminal-box" id="headers-box"><?php
if (!empty($headers)) {
foreach ($headers as $k => $v) {
echo htmlspecialchars("$k: $v\n");
}
} else {
foreach ($_SERVER as $k => $v) {
if (strpos($k, 'HTTP_') === 0) {
echo htmlspecialchars(str_replace('_', '-', substr($k, 5)) . ": $v\n");
}
}
}
?></div>
</div>
<!-- Tool 5: Realtime Crypto & String Transformer -->
<div class="card">
<div class="card-header">
<div class="card-title">
<span>🔐</span> Realtime Crypto &amp; String Transformer
</div>
<span class="badge-pill">PHP &amp; JS Dual Engine</span>
<span class="badge-pill">Dual Engine</span>
</div>
<p class="card-desc">
Converteer strings direct naar verschillende hashing formaten, base64 encoding en timestamp berekeningen.
Converteer strings direct naar MD5, SHA-256 en Base64 formaten.
</p>
<div style="display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap;">
<div style="display: flex; gap: 10px; margin-bottom: 14px; flex-wrap: wrap;">
<input type="text" id="trans-input" value="WebenPCStudio 2026 Git Testdrive"
style="flex: 1; min-width: 260px; background: rgba(0,0,0,0.4); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 10px 14px; color: #fff; font-size: 0.9rem; outline: none;"
style="flex: 1; min-width: 260px; background: rgba(0,0,0,0.3); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: 10px 14px; color: var(--text-main); font-size: 0.9rem; outline: none;"
placeholder="Typ invoertekst..." oninput="updateTransforms()">
<button class="btn btn-secondary" onclick="insertCurrentEpoch()">⏱️ Epoch Nu</button>
<button class="btn btn-secondary btn-sm" onclick="insertCurrentEpoch()">⏱️ Epoch Nu</button>
</div>
<div class="grid-3" style="margin-bottom: 0;">
@@ -252,7 +349,40 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<!-- Script Logic -->
<script>
// 1. Outbound Ping via PHP cURL
// 1. DNS Record Lookup
async function runDnsLookup() {
const btn = document.getElementById('btn-dns');
const domain = document.getElementById('dns-input').value.trim();
const resBox = document.getElementById('dns-results');
if (!domain) return;
btn.disabled = true;
btn.textContent = 'Querying...';
resBox.textContent = `DNS records opzoeken voor ${domain}...`;
try {
const res = await fetch(`tools.php?action=dns_lookup&domain=${encodeURIComponent(domain)}&t=${Date.now()}`);
const data = await res.json();
if (data.status === 'success' && data.records.length > 0) {
let out = `[DNS RECORDS VOOR: ${data.domain}]\n`;
out += `Totaal gevonden: ${data.count} records\n\n`;
data.records.forEach(r => {
out += `${r.type.padEnd(8)} ${r.value.padEnd(36)} (TTL: ${r.ttl}s)\n`;
});
resBox.textContent = out;
} else {
resBox.textContent = `Geen DNS records gevonden voor ${domain}.`;
}
} catch (err) {
resBox.textContent = 'DNS Lookup fout: ' + err.message;
} finally {
btn.disabled = false;
btn.textContent = 'Query DNS';
}
}
// 2. Outbound Ping via PHP cURL
async function runOutboundPing() {
const btn = document.getElementById('btn-ping');
const target = document.getElementById('ping-target').value;
@@ -286,7 +416,7 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
}
}
// 2. Realtime Hashing & Transforms
// 3. Realtime Hashing & Transforms
async function sha256(msg) {
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(msg));
return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');