Compare commits

...

6 Commits

Author SHA1 Message Date
NiekRabelink b7ef4b37ea Merge pull request 'Dev' (#2) from dev into main
Reviewed-on: #2
2026-09-07 11:32:35 +00:00
NiekRabelink 6b827ac1db chore: Add new line at the end of auth.php, login.php, and logout.php for consistency 2026-09-07 13:31:03 +02:00
NiekRabelink 530e249b8f feat: Implement secure authentication module and integrate session management across multiple files 2026-09-07 13:30:55 +02:00
NiekRabelink 27022415d7 Merge pull request 'Dev' (#1) from dev into main
Reviewed-on: #1
2026-09-07 11:18:54 +00:00
NiekRabelink 6d6833a417 chore: Add a new line at the end of theme.js for consistency 2026-09-07 13:17:04 +02:00
NiekRabelink 61af688ff3 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.
2026-09-07 13:16:56 +02:00
8 changed files with 1019 additions and 84 deletions
+91
View File
@@ -0,0 +1,91 @@
<?php
/**
* Web & PC Studio - Authenticatie & Beveiligingsmodule
* Veilig sessiebeheer, CSRF-protectie en one-way cryptografische verificatie.
*/
// Veilige sessie instellingen
if (session_status() === PHP_SESSION_NONE) {
ini_set('session.cookie_httponly', '1');
ini_set('session.use_only_cookies', '1');
ini_set('session.cookie_samesite', 'Strict');
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
ini_set('session.cookie_secure', '1');
}
session_start();
}
// One-way cryptografische hashes (Nooit plaintext opgeslagen)
define('AUTH_USER_HASH', 'ef8d40929f6f50e1f52009fbe94cf5a81b8373b90ed2bd130e85dcb4169c5cb4');
define('AUTH_PASS_HASH', '$2y$12$RQFrfxgd5ZiHQAcMX2DDluDW072QG/aDTDE50ORz3drcNnbZzl0AK');
/**
* Controleert of de huidige bezoeker geauthenticeerd is.
*/
function is_logged_in() {
if (!empty($_SESSION['wpc_auth']) && $_SESSION['wpc_auth'] === true) {
// Sessie timeout na 2 uur inactiviteit
if (isset($_SESSION['wpc_last_activity']) && (time() - $_SESSION['wpc_last_activity'] > 7200)) {
logout();
return false;
}
$_SESSION['wpc_last_activity'] = time();
return true;
}
return false;
}
/**
* Blokkeert ongeautoriseerde toegang en stuurt door naar het inlogscherm.
*/
function require_auth() {
if (!is_logged_in()) {
// Als het een asynchrone AJAX aanroep is, geef HTTP 401 Unauthorized
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) || isset($_GET['action']) || isset($_GET['api'])) {
header('HTTP/1.1 401 Unauthorized');
header('Content-Type: application/json; charset=utf-8');
echo json_encode(['status' => 'unauthorized', 'message' => 'Sessie verlopen of niet ingelogd.']);
exit;
}
$redirect = urlencode($_SERVER['REQUEST_URI'] ?? 'index.php');
header("Location: login.php?redirect={$redirect}");
exit;
}
}
/**
* Genereert een CSRF token voor formulieren.
*/
function get_csrf_token() {
if (empty($_SESSION['wpc_csrf'])) {
$_SESSION['wpc_csrf'] = bin2hex(random_bytes(32));
}
return $_SESSION['wpc_csrf'];
}
/**
* Valideert het ingediende CSRF token.
*/
function verify_csrf_token($token) {
if (empty($_SESSION['wpc_csrf']) || empty($token)) {
return false;
}
return hash_equals($_SESSION['wpc_csrf'], $token);
}
/**
* Beëindigt de sessie en ruimt cookies op.
*/
function logout() {
$_SESSION = [];
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
@session_destroy();
}
+81 -11
View File
@@ -4,6 +4,9 @@
* Hoofdpagina voor status, runtime verificatie en Git webhook deployment tests. * Hoofdpagina voor status, runtime verificatie en Git webhook deployment tests.
*/ */
require_once __DIR__ . '/auth.php';
require_auth();
// Snelle API responder voor AJAX pings // Snelle API responder voor AJAX pings
if (isset($_GET['api']) && $_GET['api'] === 'status') { if (isset($_GET['api']) && $_GET['api'] === 'status') {
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
@@ -23,9 +26,47 @@ date_default_timezone_set('Europe/Amsterdam');
$serverTime = date('d-m-Y H:i:s'); $serverTime = date('d-m-Y H:i:s');
$phpVersion = PHP_VERSION; $phpVersion = PHP_VERSION;
$serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx'; $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
$host = $_SERVER['HTTP_HOST'] ?? 'www.webenpcstudio.nl'; $host = $_SERVER['HTTP_HOST'] ?? 'server.webenpcstudio.nl';
$clientIp = $_SERVER['REMOTE_ADDR'] ?? 'Onbekend'; $clientIp = $_SERVER['REMOTE_ADDR'] ?? 'Onbekend';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' : 'HTTP'; $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' : 'HTTP';
$loggedUser = $_SESSION['wpc_user_email'] ?? 'niek.rabelink@gmail.com';
// 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']
];
}
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html lang="nl"> <html lang="nl">
@@ -34,6 +75,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web &amp; PC Studio - Systeem Dashboard</title> <title>Web &amp; PC Studio - Systeem Dashboard</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="theme.js"></script>
</head> </head>
<body> <body>
<div class="main-wrapper"> <div class="main-wrapper">
@@ -52,9 +94,11 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<a href="tools.php" class="nav-item">🛠️ Dev Tools</a> <a href="tools.php" class="nav-item">🛠️ Dev Tools</a>
</div> </div>
<div class="nav-status"> <div class="nav-status">
<button class="btn btn-secondary btn-sm theme-btn" onclick="toggleTheme()">🌙 Thema</button>
<a href="logout.php" class="btn btn-secondary btn-sm" style="color: #f87171;" title="Veilig uitloggen">🚪 Uitloggen</a>
<span class="nav-badge"> <span class="nav-badge">
<span class="pulse-dot"></span> <span class="pulse-dot"></span>
Git Live server.webenpcstudio.nl
</span> </span>
</div> </div>
</nav> </nav>
@@ -63,7 +107,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<header class="hero"> <header class="hero">
<h1>Systeem Dashboard &amp; Git Status</h1> <h1>Systeem Dashboard &amp; Git Status</h1>
<p> <p>
Realtime omgevingsstatus, Git webhook deploy-verificatie en server-runtime monitoring voor www.webenpcstudio.nl. Realtime omgevingsstatus, Git webhook deploy-verificatie en server-runtime monitoring voor <strong>server.webenpcstudio.nl</strong>.
</p> </p>
</header> </header>
@@ -111,7 +155,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<div class="card-title"> <div class="card-title">
<span>🌿</span> Git CI/CD Sync <span>🌿</span> Git CI/CD Sync
</div> </div>
<span class="badge-pill">Verbonden</span> <span class="badge-pill">Branch: <?= htmlspecialchars($activeBranch); ?></span>
</div> </div>
<p class="card-desc"> <p class="card-desc">
Status van de gekoppelde Git repository en automatische webhook updates. Status van de gekoppelde Git repository en automatische webhook updates.
@@ -128,7 +172,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
</tr> </tr>
<tr> <tr>
<td class="label">Actieve Branch:</td> <td class="label">Actieve Branch:</td>
<td class="val">main</td> <td class="val" style="color: #38bdf8; font-weight: 700;"><?= htmlspecialchars($activeBranch); ?></td>
</tr> </tr>
<tr> <tr>
<td class="label">Deploy Type:</td> <td class="label">Deploy Type:</td>
@@ -142,11 +186,37 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
</div> </div>
</div> </div>
<!-- Kaart 3: Live Tijd en Server Ping Diagnose --> <!-- Kaart 3: Laatste Git Commits & Deployment Log -->
<div class="card" style="margin-bottom: 24px;"> <div class="card" style="margin-bottom: 24px;">
<div class="card-header"> <div class="card-header">
<div class="card-title"> <div class="card-title">
<span>⏱️</span> Realtime Verbinding &amp; Diagnose <span>📜</span> Laatste Git Commits (Deployment Log)
</div>
<span class="badge-pill">Live Repository History</span>
</div>
<p class="card-desc">
Overzicht van de meest recente commits die naar deze webserver zijn gepusht via Git.
</p>
<div class="commit-timeline">
<?php foreach ($commits as $commit): ?>
<div class="commit-item">
<div class="commit-top">
<span class="commit-hash"><?= $commit['hash']; ?></span>
<span class="commit-date"><?= $commit['date']; ?></span>
</div>
<div class="commit-msg"><?= $commit['msg']; ?></div>
<div class="commit-author">Auteur: <?= $commit['author']; ?></div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Kaart 4: Live Tijd en Server Ping Diagnose -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<div class="card-title">
<span>⏱️</span> Realtime Verbinding &amp; Latency
</div> </div>
<button class="btn btn-primary btn-sm" id="btn-ping" onclick="testServerPing()"> <button class="btn btn-primary btn-sm" id="btn-ping" onclick="testServerPing()">
⚡ Test Server Ping (AJAX) ⚡ Test Server Ping (AJAX)
@@ -156,7 +226,7 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<div class="metric-grid"> <div class="metric-grid">
<div class="metric-box"> <div class="metric-box">
<div class="metric-label">Server Tijd (PHP)</div> <div class="metric-label">Server Tijd (PHP)</div>
<div class="metric-val" id="server-time" style="font-size: 0.95rem; color: #f1f5f9;"><?= htmlspecialchars($serverTime); ?></div> <div class="metric-val" id="server-time" style="font-size: 0.95rem; color: var(--text-main);"><?= htmlspecialchars($serverTime); ?></div>
</div> </div>
<div class="metric-box"> <div class="metric-box">
<div class="metric-label">Browser Tijd (JS)</div> <div class="metric-label">Browser Tijd (JS)</div>
@@ -180,15 +250,15 @@ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS'
<a href="lab.php" class="card" style="text-decoration: none; display: flex; align-items: center; gap: 16px;"> <a href="lab.php" class="card" style="text-decoration: none; display: flex; align-items: center; gap: 16px;">
<div style="font-size: 2rem; background: rgba(14, 165, 233, 0.12); padding: 12px; border-radius: 12px;">🚀</div> <div style="font-size: 2rem; background: rgba(14, 165, 233, 0.12); padding: 12px; border-radius: 12px;">🚀</div>
<div> <div>
<h3 style="font-size: 1.05rem; color: #fff; margin-bottom: 4px;">Webserver Benchmark Suite &rarr;</h3> <h3 style="font-size: 1.05rem; color: var(--text-main); margin-bottom: 4px;">Webserver Benchmark Suite &rarr;</h3>
<p style="font-size: 0.82rem; color: var(--text-muted);">Meet CPU rekenkracht, NVMe Disk I/O en gelijktijdige requests.</p> <p style="font-size: 0.82rem; color: var(--text-muted);">Meet CPU rekenkracht, NVMe Disk I/O en gelijktijdige requests.</p>
</div> </div>
</a> </a>
<a href="tools.php" class="card" style="text-decoration: none; display: flex; align-items: center; gap: 16px;"> <a href="tools.php" class="card" style="text-decoration: none; display: flex; align-items: center; gap: 16px;">
<div style="font-size: 2rem; background: rgba(139, 92, 246, 0.12); padding: 12px; border-radius: 12px;">🛠️</div> <div style="font-size: 2rem; background: rgba(139, 92, 246, 0.12); padding: 12px; border-radius: 12px;">🛠️</div>
<div> <div>
<h3 style="font-size: 1.05rem; color: #fff; margin-bottom: 4px;">Developer &amp; Systeem Tools &rarr;</h3> <h3 style="font-size: 1.05rem; color: var(--text-main); margin-bottom: 4px;">Developer &amp; Systeem Tools &rarr;</h3>
<p style="font-size: 0.82rem; color: var(--text-muted);">Inspecteer HTTP headers, cURL verbindingen en cryptografie.</p> <p style="font-size: 0.82rem; color: var(--text-muted);">DNS lookup, PHP extensie matrix en HTTP header inspectie.</p>
</div> </div>
</a> </a>
</div> </div>
+174 -12
View File
@@ -4,6 +4,9 @@
* Diepgaande diagnostische suite voor CPU, NVMe Disk I/O, RAM en HTTP Concurrency. * Diepgaande diagnostische suite voor CPU, NVMe Disk I/O, RAM en HTTP Concurrency.
*/ */
require_once __DIR__ . '/auth.php';
require_auth();
// Helper functies voor benchmarks // Helper functies voor benchmarks
function benchmarkCPU() { function benchmarkCPU() {
$start = microtime(true); $start = microtime(true);
@@ -68,11 +71,10 @@ function benchmarkDiskIO() {
return ['status' => 'error', 'message' => 'Kan tijdelijk bestand niet aanmaken']; return ['status' => 'error', 'message' => 'Kan tijdelijk bestand niet aanmaken'];
} }
$chunkSize = 64 * 1024; // 64 KB $chunkSize = 64 * 1024;
$chunks = 48; // ~3 MB totaal $chunks = 48; // ~3 MB
$dummyData = str_repeat('WPCStudio2026_TEST_STRING_ABCD!', 2048); $dummyData = str_repeat('WPCStudio2026_TEST_STRING_ABCD!', 2048);
// Write test
$writeStart = microtime(true); $writeStart = microtime(true);
$fp = fopen($tempFile, 'wb'); $fp = fopen($tempFile, 'wb');
if (!$fp) { if (!$fp) {
@@ -86,7 +88,6 @@ function benchmarkDiskIO() {
fclose($fp); fclose($fp);
$writeTime = max(0.0005, microtime(true) - $writeStart); $writeTime = max(0.0005, microtime(true) - $writeStart);
// Read test
$readStart = microtime(true); $readStart = microtime(true);
$fp = fopen($tempFile, 'rb'); $fp = fopen($tempFile, 'rb');
if ($fp) { if ($fp) {
@@ -284,6 +285,7 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web &amp; PC Studio - Webserver Benchmark</title> <title>Web &amp; PC Studio - Webserver Benchmark</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="theme.js"></script>
<style> <style>
.bench-master { .bench-master {
background: linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(11, 15, 25, 0.95)); background: linear-gradient(135deg, rgba(15, 23, 42, 0.9), rgba(11, 15, 25, 0.95));
@@ -299,6 +301,11 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
flex-wrap: wrap; flex-wrap: wrap;
} }
[data-theme="light"] .bench-master {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(241, 245, 249, 0.95));
box-shadow: 0 15px 35px -10px rgba(0, 0, 0, 0.08);
}
.score-box { .score-box {
display: flex; display: flex;
align-items: center; align-items: center;
@@ -309,6 +316,10 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
} }
[data-theme="light"] .score-box {
background: rgba(241, 245, 249, 0.85);
}
.score-circle { .score-circle {
width: 76px; width: 76px;
height: 76px; height: 76px;
@@ -350,6 +361,12 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
.conc-pill.success { border-color: #10b981; color: #34d399; } .conc-pill.success { border-color: #10b981; color: #34d399; }
.conc-pill.slow { border-color: #f59e0b; color: #fbbf24; } .conc-pill.slow { border-color: #f59e0b; color: #fbbf24; }
.conc-pill.error { border-color: #ef4444; color: #f87171; } .conc-pill.error { border-color: #ef4444; color: #f87171; }
@media print {
.navbar, .btn, .nav-status, .sound-toggle, footer { display: none !important; }
body { background: #fff !important; color: #000 !important; }
.card, .bench-master { box-shadow: none !important; border: 1px solid #ccc !important; }
}
</style> </style>
</head> </head>
<body> <body>
@@ -369,9 +386,11 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
<a href="tools.php" class="nav-item">🛠️ Dev Tools</a> <a href="tools.php" class="nav-item">🛠️ Dev Tools</a>
</div> </div>
<div class="nav-status"> <div class="nav-status">
<button class="btn btn-secondary btn-sm theme-btn" onclick="toggleTheme()">🌙 Thema</button>
<button class="btn btn-secondary btn-sm" id="sound-btn" onclick="toggleAudio()" title="Schakel geluid in/uit"> <button class="btn btn-secondary btn-sm" id="sound-btn" onclick="toggleAudio()" title="Schakel geluid in/uit">
<span id="sound-icon">🔈</span> Geluid: <span id="sound-status">Uit</span> <span id="sound-icon">🔈</span> Geluid: <span id="sound-status">Uit</span>
</button> </button>
<a href="logout.php" class="btn btn-secondary btn-sm" style="color: #f87171;" title="Veilig uitloggen">🚪 Uitloggen</a>
</div> </div>
</nav> </nav>
@@ -386,16 +405,22 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
<!-- Master Benchmark Banner --> <!-- Master Benchmark Banner -->
<div class="bench-master"> <div class="bench-master">
<div style="flex: 1; min-width: 280px;"> <div style="flex: 1; min-width: 280px;">
<h2 style="font-size: 1.3rem; color: #fff; margin-bottom: 6px;">⚡ Volledige Server Benchmark</h2> <h2 style="font-size: 1.3rem; color: var(--text-main); margin-bottom: 6px;">⚡ Volledige Server Benchmark</h2>
<p style="font-size: 0.88rem; color: var(--text-muted); margin-bottom: 18px;"> <p style="font-size: 0.88rem; color: var(--text-muted); margin-bottom: 18px;">
Voert in één geautomatiseerde cyclus alle 5 hardware- &amp; softwaretests uit op www.webenpcstudio.nl. Voert in één geautomatiseerde cyclus alle 5 hardware- &amp; softwaretests uit op <strong>server.webenpcstudio.nl</strong>.
</p> </p>
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;"> <div style="display: flex; gap: 8px; align-items: center; flex-wrap: wrap;">
<button class="btn btn-primary" id="btn-run-all" onclick="runFullBenchmark()"> <button class="btn btn-primary" id="btn-run-all" onclick="runFullBenchmark()">
▶ Start Volledige Benchmark ▶ Start Volledige Benchmark
</button> </button>
<button class="btn btn-secondary" onclick="copyReport()"> <button class="btn btn-secondary btn-sm" onclick="copyReport()">
📋 Kopieer Rapport 📋 Kopieer
</button>
<button class="btn btn-secondary btn-sm" onclick="downloadJSONReport()">
📥 Download JSON
</button>
<button class="btn btn-secondary btn-sm" onclick="printReport()">
🖨️ PDF / Print
</button> </button>
</div> </div>
<div class="progress-bar-wrap" id="p-bar-wrap"> <div class="progress-bar-wrap" id="p-bar-wrap">
@@ -406,7 +431,7 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
<!-- Score Display --> <!-- Score Display -->
<div class="score-box"> <div class="score-box">
<div class="score-circle" id="score-circle"> <div class="score-circle" id="score-circle">
<span style="font-size: 1.45rem; color: #fff; line-height: 1;" id="score-num">--</span> <span style="font-size: 1.45rem; color: var(--text-main); line-height: 1;" id="score-num">--</span>
<span style="font-size: 0.65rem; color: var(--text-muted);">/ 100</span> <span style="font-size: 0.65rem; color: var(--text-muted);">/ 100</span>
</div> </div>
<div> <div>
@@ -416,6 +441,25 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
</div> </div>
</div> </div>
<!-- Historische Vergelijking & Trend Grafiek -->
<div class="card" style="margin-bottom: 24px;">
<div class="card-header">
<div>
<div class="card-title"><span>📈</span> Historische Benchmark Trend (Opeenvolgende Runs)</div>
<p class="card-desc" style="margin-bottom: 0;">Vergelijk eerdere benchmark scores over tijd om server-stabiliteit te monitoren.</p>
</div>
<button class="btn btn-secondary btn-sm" onclick="clearHistory()">🗑️ Wis Historie</button>
</div>
<div class="chart-wrap">
<canvas id="history-canvas" style="width: 100%; height: 100%; display: block;"></canvas>
</div>
<div id="history-legend" style="font-size: 0.78rem; color: var(--text-muted); display: flex; justify-content: space-between;">
<span>Eerste meting</span>
<span id="history-count">0 eerdere benchmarks opgeslagen</span>
<span>Laatste meting</span>
</div>
</div>
<!-- 6 Test Cards --> <!-- 6 Test Cards -->
<div class="grid-3"> <div class="grid-3">
<!-- CPU --> <!-- CPU -->
@@ -554,7 +598,7 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
</div> </div>
<div style="display: flex; gap: 8px; align-items: center;"> <div style="display: flex; gap: 8px; align-items: center;">
<button class="btn btn-secondary btn-sm" onclick="setConc(5, this)">5 Calls</button> <button class="btn btn-secondary btn-sm" onclick="setConc(5, this)">5 Calls</button>
<button class="btn btn-secondary btn-sm" style="border-color: var(--primary); color: #fff;" onclick="setConc(12, this)">12 Calls</button> <button class="btn btn-secondary btn-sm" style="border-color: var(--primary);" onclick="setConc(12, this)">12 Calls</button>
<button class="btn btn-secondary btn-sm" onclick="setConc(24, this)">24 Calls</button> <button class="btn btn-secondary btn-sm" onclick="setConc(24, this)">24 Calls</button>
<button class="btn btn-primary btn-sm" id="btn-conc" onclick="runConcurrencyTest()">⚡ Start Burst</button> <button class="btn btn-primary btn-sm" id="btn-conc" onclick="runConcurrencyTest()">⚡ Start Burst</button>
</div> </div>
@@ -626,6 +670,94 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
let lastReport = null; 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() { async function runFullBenchmark() {
const btn = document.getElementById('btn-run-all'); const btn = document.getElementById('btn-run-all');
const pWrap = document.getElementById('p-bar-wrap'); const pWrap = document.getElementById('p-bar-wrap');
@@ -667,6 +799,13 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
document.getElementById('db-time').textContent = data.db.duration_ms + ' ms'; 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); playTone(880, 'triangle', 0.2);
} catch (err) { } catch (err) {
alert('Benchmark fout: ' + err.message); alert('Benchmark fout: ' + err.message);
@@ -765,7 +904,7 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
function copyReport() { function copyReport() {
let report = "=== WEB & PC STUDIO - WEBSERVER BENCHMARK RAPPORT ===\n" + let report = "=== WEB & PC STUDIO - WEBSERVER BENCHMARK RAPPORT ===\n" +
`Domein: www.webenpcstudio.nl\n` + `Domein: server.webenpcstudio.nl\n` +
`Datum: ${new Date().toLocaleString('nl-NL')}\n\n`; `Datum: ${new Date().toLocaleString('nl-NL')}\n\n`;
if (lastReport) { if (lastReport) {
@@ -785,6 +924,29 @@ $serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Apache / Nginx';
playTone(660, 'sine', 0.1); 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);
</script> </script>
</body> </body>
</html> </html>
+287
View File
@@ -0,0 +1,287 @@
<?php
/**
* Web & PC Studio - Beveiligd Inlogscherm
* Server-side authenticatie voor server.webenpcstudio.nl met brute-force protectie.
*/
require_once __DIR__ . '/auth.php';
// Als gebruiker al ingelogd is, direct doorsturen
if (is_logged_in()) {
header('Location: index.php');
exit;
}
$error = '';
$lockoutTime = 300; // 5 minuten blokkade bij te veel pogingen
// Initialiseer rate limiting
if (!isset($_SESSION['login_attempts'])) {
$_SESSION['login_attempts'] = 0;
$_SESSION['last_attempt_time'] = time();
}
// Controleer of de gebruiker tijdelijk geblokkeerd is
if ($_SESSION['login_attempts'] >= 5) {
$timePassed = time() - $_SESSION['last_attempt_time'];
if ($timePassed < $lockoutTime) {
$remaining = ceil(($lockoutTime - $timePassed) / 60);
$error = "Te veel mislukte inlogpogingen. Probeer het over {$remaining} minuten opnieuw.";
} else {
// Reset na verloop van blokkade
$_SESSION['login_attempts'] = 0;
}
}
// Verwerk inlogverzoek
if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($error)) {
$csrfToken = $_POST['csrf_token'] ?? '';
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
// Kleine vertraging tegen timing-aanvallen
usleep(200000);
if (!verify_csrf_token($csrfToken)) {
$error = 'Beveiligingstoken (CSRF) ongeldig of verlopen. Ververs de pagina.';
} else {
// Vergelijk gebruikersnaam hash (timing-safe) en wachtwoord via bcrypt
$userMatches = hash_equals(AUTH_USER_HASH, hash('sha256', strtolower($username)));
$passMatches = password_verify($password, AUTH_PASS_HASH);
if ($userMatches && $passMatches) {
// Sessie fixatie voorkomen via regeneratie
session_regenerate_id(true);
$_SESSION['wpc_auth'] = true;
$_SESSION['wpc_user_email'] = htmlspecialchars($username);
$_SESSION['wpc_last_activity'] = time();
$_SESSION['login_attempts'] = 0;
// Veilige doorverwijzing
$redirect = $_GET['redirect'] ?? 'index.php';
// Alleen relatieve redirects toestaan
if (empty($redirect) || strpos($redirect, '://') !== false || strpos($redirect, '//') === 0) {
$redirect = 'index.php';
}
header("Location: {$redirect}");
exit;
} else {
$_SESSION['login_attempts']++;
$_SESSION['last_attempt_time'] = time();
$attemptsLeft = max(0, 5 - $_SESSION['login_attempts']);
$error = "Ongeldige gebruikersnaam of wachtwoord. (Nog {$attemptsLeft} pogingen)";
}
}
}
$csrfToken = get_csrf_token();
$logoutMessage = isset($_GET['logout']) ? 'U bent succesvol en veilig uitgelogd.' : '';
?>
<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web &amp; PC Studio - Beveiligde Toegang</title>
<link rel="stylesheet" href="style.css">
<script src="theme.js"></script>
<style>
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
padding: 20px;
}
.login-card {
width: 100%;
max-width: 440px;
background: var(--bg-surface);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-xl);
padding: 38px 32px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.6);
text-align: left;
}
.login-header {
text-align: center;
margin-bottom: 28px;
}
.login-logo {
background: linear-gradient(135deg, var(--primary), var(--accent));
width: 52px;
height: 52px;
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 800;
font-size: 1.5rem;
color: white;
box-shadow: 0 6px 20px var(--primary-glow);
margin: 0 auto 16px;
}
.login-title {
font-size: 1.45rem;
font-weight: 800;
color: var(--text-main);
margin-bottom: 6px;
}
.login-subtitle {
font-size: 0.85rem;
color: var(--text-muted);
}
.form-group {
margin-bottom: 18px;
}
.form-label {
display: block;
font-size: 0.82rem;
font-weight: 600;
color: var(--text-muted);
margin-bottom: 6px;
}
.input-wrap {
position: relative;
display: flex;
align-items: center;
}
.form-control {
width: 100%;
background: rgba(0, 0, 0, 0.35);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 12px 14px;
color: var(--text-main);
font-size: 0.92rem;
outline: none;
transition: all 0.2s ease;
}
[data-theme="light"] .form-control {
background: rgba(241, 245, 249, 0.8);
}
.form-control:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px var(--primary-glow);
}
.toggle-pwd {
position: absolute;
right: 12px;
background: transparent;
border: none;
color: var(--text-subtle);
cursor: pointer;
font-size: 1rem;
padding: 4px;
}
.alert-error {
background: rgba(239, 68, 68, 0.12);
border: 1px solid rgba(239, 68, 68, 0.3);
color: #f87171;
padding: 12px 14px;
border-radius: var(--radius-md);
font-size: 0.84rem;
margin-bottom: 20px;
}
.alert-success {
background: rgba(16, 185, 129, 0.12);
border: 1px solid rgba(16, 185, 129, 0.3);
color: #34d399;
padding: 12px 14px;
border-radius: var(--radius-md);
font-size: 0.84rem;
margin-bottom: 20px;
}
.login-footer {
margin-top: 24px;
padding-top: 18px;
border-top: 1px solid var(--border-subtle);
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.78rem;
color: var(--text-subtle);
}
</style>
</head>
<body>
<div class="login-card">
<!-- Logo & Header -->
<div class="login-header">
<div class="login-logo">🔒</div>
<h1 class="login-title">Beveiligde Toegang</h1>
<p class="login-subtitle">server.webenpcstudio.nl &bull; Testdrive Hub</p>
</div>
<?php if (!empty($logoutMessage)): ?>
<div class="alert-success"><?= htmlspecialchars($logoutMessage); ?></div>
<?php endif; ?>
<?php if (!empty($error)): ?>
<div class="alert-error"><?= htmlspecialchars($error); ?></div>
<?php endif; ?>
<!-- Formulier -->
<form method="POST" action="">
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken); ?>">
<div class="form-group">
<label class="form-label" for="username">Gebruikersnaam (E-mail):</label>
<div class="input-wrap">
<input type="email" id="username" name="username" class="form-control"
placeholder="naam@domein.nl" required autofocus autocomplete="username">
</div>
</div>
<div class="form-group" style="margin-bottom: 24px;">
<label class="form-label" for="password">Wachtwoord:</label>
<div class="input-wrap">
<input type="password" id="password" name="password" class="form-control"
placeholder="••••••••••••" required autocomplete="current-password">
<button type="button" class="toggle-pwd" onclick="togglePasswordVisibility()" title="Toon/verberg wachtwoord">👁️</button>
</div>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; padding: 12px; font-size: 0.95rem;">
Inloggen op Server
</button>
</form>
<div class="login-footer">
<span>TLS / SSL Beveiligd</span>
<button class="btn btn-secondary btn-sm theme-btn" onclick="toggleTheme()" style="padding: 4px 8px;">🌙 Thema</button>
</div>
</div>
<script>
function togglePasswordVisibility() {
const pwdInput = document.getElementById('password');
if (pwdInput.type === 'password') {
pwdInput.type = 'text';
} else {
pwdInput.type = 'password';
}
}
</script>
</body>
</html>
+10
View File
@@ -0,0 +1,10 @@
<?php
/**
* Web & PC Studio - Veilige Uitlogmodule
*/
require_once __DIR__ . '/auth.php';
logout();
header('Location: login.php?logout=1');
exit;
+145
View File
@@ -38,6 +38,54 @@
--radius-xl: 22px; --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; box-sizing: border-box;
margin: 0; margin: 0;
@@ -451,6 +499,102 @@ body {
overflow-y: auto; 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 Footer
========================================================================== */ ========================================================================== */
@@ -472,3 +616,4 @@ body {
text-decoration: underline; text-decoration: underline;
} }
+36
View File
@@ -0,0 +1,36 @@
/**
* 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);
});
+195 -61
View File
@@ -1,14 +1,62 @@
<?php <?php
/** /**
* Web & PC Studio - Dev Tools & System Inspector * Web & PC Studio - Dev Tools & System Inspector
* Technische inspectie en utilities voor serverbeheer en debugging. * Technische inspectie en utilities voor serverbeheer, DNS en debugging.
*/ */
require_once __DIR__ . '/auth.php';
require_auth();
// PHP Backend AJAX handler // PHP Backend AJAX handler
if (isset($_GET['action'])) { if (isset($_GET['action'])) {
header('Content-Type: application/json; charset=utf-8'); 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') { if ($_GET['action'] === 'outbound_ping') {
$target = $_GET['target'] ?? 'https://git.webenpcstudio.nl'; $target = $_GET['target'] ?? 'https://git.webenpcstudio.nl';
$allowed = ['https://git.webenpcstudio.nl', 'https://www.google.com', 'https://api.github.com']; $allowed = ['https://git.webenpcstudio.nl', 'https://www.google.com', 'https://api.github.com'];
@@ -43,7 +91,7 @@ if (isset($_GET['action'])) {
exit; exit;
} }
// Hash & Encoding API // 3. Hash & Encoding API
if ($_GET['action'] === 'transform' && isset($_POST['input'])) { if ($_GET['action'] === 'transform' && isset($_POST['input'])) {
$text = (string)$_POST['input']; $text = (string)$_POST['input'];
echo json_encode([ echo json_encode([
@@ -69,7 +117,18 @@ $phpVersion = PHP_VERSION;
$clientIp = $_SERVER['REMOTE_ADDR'] ?? 'Onbekend'; $clientIp = $_SERVER['REMOTE_ADDR'] ?? 'Onbekend';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Onbekend'; $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? 'Onbekend';
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'HTTPS' : 'HTTP'; $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> <!DOCTYPE html>
<html lang="nl"> <html lang="nl">
@@ -78,6 +137,7 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Web &amp; PC Studio - Dev Tools &amp; Inspector</title> <title>Web &amp; PC Studio - Dev Tools &amp; Inspector</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<script src="theme.js"></script>
</head> </head>
<body> <body>
<div class="main-wrapper"> <div class="main-wrapper">
@@ -96,9 +156,11 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<a href="tools.php" class="nav-item active">🛠️ Dev Tools</a> <a href="tools.php" class="nav-item active">🛠️ Dev Tools</a>
</div> </div>
<div class="nav-status"> <div class="nav-status">
<button class="btn btn-secondary btn-sm theme-btn" onclick="toggleTheme()">🌙 Thema</button>
<a href="logout.php" class="btn btn-secondary btn-sm" style="color: #f87171;" title="Veilig uitloggen">🚪 Uitloggen</a>
<span class="nav-badge"> <span class="nav-badge">
<span class="pulse-dot"></span> <span class="pulse-dot"></span>
Git Live server.webenpcstudio.nl
</span> </span>
</div> </div>
</nav> </nav>
@@ -107,79 +169,57 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<header class="hero"> <header class="hero">
<h1>Systeem &amp; Developer Tools</h1> <h1>Systeem &amp; Developer Tools</h1>
<p> <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> </p>
</header> </header>
<!-- Grid: 2 Kolommen --> <!-- Grid: 2 Kolommen -->
<div class="grid-2"> <div class="grid-2">
<!-- Paneel 1: HTTP Headers & Client Inspectie --> <!-- Tool 1: DNS & Record Lookup -->
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<div class="card-title"> <div class="card-title">
<span>🔍</span> HTTP Request Inspector <span>🔎</span> DNS &amp; Domein Lookup
</div> </div>
<span class="badge-pill"><?= htmlspecialchars($protocol); ?></span> <span class="badge-pill">Live DNS Query</span>
</div> </div>
<p class="card-desc"> <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> </p>
<table class="data-table" style="margin-bottom: 16px;"> <div style="display: flex; gap: 8px; margin-bottom: 14px;">
<tr> <input type="text" id="dns-input" value="webenpcstudio.nl"
<td class="label">Bezoekers IP:</td> 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;"
<td class="val"><?= htmlspecialchars($clientIp); ?></td> placeholder="bijv. webenpcstudio.nl">
</tr> <button class="btn btn-primary btn-sm" id="btn-dns" onclick="runDnsLookup()">
<tr> Query DNS
<td class="label">Protocol / SSL:</td> </button>
<td class="val"><?= htmlspecialchars($protocol); ?></td> </div>
</tr>
<tr> <div id="dns-results" class="terminal-box" style="max-height: 200px; min-height: 80px;">
<td class="label">User-Agent:</td> Voer een domeinnaam in en klik op "Query DNS" om actieve records op te halen.
<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> </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> </div>
<!-- Paneel 2: Outbound Server Connectiviteit (cURL) --> <!-- Tool 2: Outbound Server Connectiviteit (cURL) -->
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<div class="card-title"> <div class="card-title">
<span>🌐</span> Outbound Server Ping <span>🌐</span> Outbound Server Ping (cURL)
</div> </div>
<span class="badge-pill">cURL Test</span> <span class="badge-pill">Verbindingstest</span>
</div> </div>
<p class="card-desc"> <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> </p>
<div style="display: flex; gap: 8px; margin-bottom: 16px;"> <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: #fff; cursor: pointer;"> <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://git.webenpcstudio.nl">git.webenpcstudio.nl (Git Server)</option>
<option value="https://api.github.com">api.github.com (GitHub API)</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> </select>
<button class="btn btn-primary" id="btn-ping" onclick="runOutboundPing()"> <button class="btn btn-primary btn-sm" id="btn-ping" onclick="runOutboundPing()">
Ping Ping
</button> </button>
</div> </div>
@@ -203,29 +243,90 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
</div> </div>
</div> </div>
<div id="ping-status-msg" style="font-size: 0.82rem; color: var(--text-muted); margin-top: 6px;"> <div id="ping-status-msg" style="font-size: 0.82rem; color: var(--text-muted); margin-top: 4px;">
Selecteer een doellocatie en klik op Ping om de netwerk-latency te meten. Selecteer een doel en klik op Ping.
</div> </div>
</div> </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">
<div class="card-header"> <div class="card-header">
<div class="card-title"> <div class="card-title">
<span>🔐</span> Realtime Crypto &amp; String Transformer <span>🔐</span> Realtime Crypto &amp; String Transformer
</div> </div>
<span class="badge-pill">PHP &amp; JS Dual Engine</span> <span class="badge-pill">Dual Engine</span>
</div> </div>
<p class="card-desc"> <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> </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" <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()"> 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>
<div class="grid-3" style="margin-bottom: 0;"> <div class="grid-3" style="margin-bottom: 0;">
@@ -252,7 +353,40 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
<!-- Script Logic --> <!-- Script Logic -->
<script> <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() { async function runOutboundPing() {
const btn = document.getElementById('btn-ping'); const btn = document.getElementById('btn-ping');
const target = document.getElementById('ping-target').value; const target = document.getElementById('ping-target').value;
@@ -286,7 +420,7 @@ $sslCipher = $_SERVER['SSL_CIPHER'] ?? ($_SERVER['HTTPS'] ?? 'Niet gedetecteerd'
} }
} }
// 2. Realtime Hashing & Transforms // 3. Realtime Hashing & Transforms
async function sha256(msg) { async function sha256(msg) {
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(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(''); return Array.from(new Uint8Array(buf)).map(b => b.toString(16).padStart(2, '0')).join('');