<?php
declare(strict_types=1);
/**
* MaddogNoland Web Server Dashboard - Enhanced Edition
* Standalone, secure, feature-rich webmaster dashboard
* PHP 8+ Required | No APIs | All Real Data
*
* @version 2.0
* @author MaddogNoland.com
* @license MIT - Free for personal and commercial use
*/
// Security Headers
header('X-Content-Type-Options: nosniff');
header('X-XSS-Protection: 1; mode=block');
header('Referrer-Policy: strict-origin-when-cross-origin');
// Dashboard Configuration
const DASHBOARD_VERSION = '2.0';
const DATA_DIR = __DIR__ . '/maddog_data';
const MAX_LOG_ENTRIES = 100;
const SECURITY_SCAN_LIMIT = 50;
// Initialize data directory with proper permissions
if (!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0750, true);
file_put_contents(DATA_DIR . '/.htaccess', "Deny from all\n");
}
// Helper Functions
function ensureDataFile(string $filename, mixed $defaultData = []): string {
$filepath = DATA_DIR . '/' . $filename;
if (!file_exists($filepath)) {
file_put_contents($filepath, json_encode($defaultData, JSON_PRETTY_PRINT));
chmod($filepath, 0640);
}
return $filepath;
}
function readJsonFile(string $filepath): array {
$content = file_get_contents($filepath);
return $content ? json_decode($content, true) ?? [] : [];
}
function writeJsonFile(string $filepath, array $data): bool {
return file_put_contents($filepath, json_encode($data, JSON_PRETTY_PRINT)) !== false;
}
function formatBytes(int $size, int $precision = 2): string {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
for ($i = 0; $size > 1024 && $i < count($units) - 1; $i++) {
$size /= 1024;
}
return round($size, $precision) . ' ' . $units[$i];
}
function getSystemLoad(): array {
if (function_exists('sys_getloadavg')) {
return sys_getloadavg();
}
return [0, 0, 0];
}
function getMemoryUsage(): array {
return [
'current' => memory_get_usage(true),
'peak' => memory_get_peak_usage(true),
'limit' => ini_get('memory_limit')
];
}
// Performance tracking
$page_start = microtime(true);
// Handle actions
$action = $_GET['action'] ?? '';
switch ($action) {
case 'reset_uptime':
file_put_contents(ensureDataFile('uptime.json'), json_encode(['start' => time()]));
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
exit;
case 'clear_logs':
writeJsonFile(ensureDataFile('access_log.json'), []);
writeJsonFile(ensureDataFile('error_log.json'), []);
header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
exit;
case 'export_data':
$export_data = [
'timestamp' => date('Y-m-d H:i:s'),
'uptime' => readJsonFile(ensureDataFile('uptime.json')),
'metrics' => readJsonFile(ensureDataFile('metrics.json')),
'access_log' => readJsonFile(ensureDataFile('access_log.json')),
'error_log' => readJsonFile(ensureDataFile('error_log.json'))
];
header('Content-Type: application/json');
header('Content-Disposition: attachment; filename="dashboard_export_' . date('Y-m-d_H-i-s') . '.json"');
echo json_encode($export_data, JSON_PRETTY_PRINT);
exit;
}
// Initialize data files
$uptimeFile = ensureDataFile('uptime.json', ['start' => time()]);
$metricsFile = ensureDataFile('metrics.json', []);
$accessLogFile = ensureDataFile('access_log.json', []);
$errorLogFile = ensureDataFile('error_log.json', []);
// Uptime calculation
$uptimeData = readJsonFile($uptimeFile);
$uptimeSeconds = time() - ($uptimeData['start'] ?? time());
// Log current access
$accessLog = readJsonFile($accessLogFile);
$accessLog[] = [
'timestamp' => time(),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
'method' => $_SERVER['REQUEST_METHOD'] ?? 'unknown',
'uri' => $_SERVER['REQUEST_URI'] ?? 'unknown'
];
$accessLog = array_slice($accessLog, -MAX_LOG_ENTRIES);
writeJsonFile($accessLogFile, $accessLog);
// System metrics collection
$systemLoad = getSystemLoad();
$memoryUsage = getMemoryUsage();
$diskSpace = [
'free' => disk_free_space(__DIR__),
'total' => disk_total_space(__DIR__)
];
// Store metrics for historical data
$metrics = readJsonFile($metricsFile);
$currentTime = time();
$metrics[] = [
'timestamp' => $currentTime,
'load' => $systemLoad[0] ?? 0,
'memory' => $memoryUsage['current'],
'disk_free' => $diskSpace['free']
];
$metrics = array_slice($metrics, -288); // Keep last 24 hours (5min intervals)
writeJsonFile($metricsFile, $metrics);
// Security scan
function advancedSecurityScan(string $dir): array {
$issues = [];
if (!is_dir($dir)) return $issues;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
);
$scanCount = 0;
foreach ($iterator as $file) {
if (++$scanCount > SECURITY_SCAN_LIMIT) break;
if ($file->isFile()) {
$filepath = $file->getPathname();
$filename = $file->getFilename();
$perms = fileperms($filepath);
// Check for writable files
if (is_writable($filepath) && !in_array($filename, ['.htaccess', 'maddog_data'])) {
$issues[] = [
'type' => 'writable',
'file' => $filename,
'path' => str_replace($_SERVER['DOCUMENT_ROOT'] ?? __DIR__, '', $filepath),
'permissions' => decoct($perms & 0777)
];
}
// Check for suspicious files
if (preg_match('/\.(php|phtml|php3|php4|php5|phar)$/i', $filename)) {
$content = file_get_contents($filepath, false, null, 0, 1024);
if ($content && preg_match('/(eval|base64_decode|system|exec|shell_exec)\s*\(/i', $content)) {
$issues[] = [
'type' => 'suspicious',
'file' => $filename,
'path' => str_replace($_SERVER['DOCUMENT_ROOT'] ?? __DIR__, '', $filepath),
'reason' => 'Contains potentially dangerous functions'
];
}
}
}
}
return $issues;
}
$securityIssues = advancedSecurityScan(__DIR__);
// PHP Error log analysis
$phpErrorLog = ini_get('error_log');
$recentErrors = [];
if ($phpErrorLog && file_exists($phpErrorLog)) {
$lines = file($phpErrorLog);
if ($lines) {
$recentErrors = array_slice($lines, -10);
// Store in our error log
$errorLog = readJsonFile($errorLogFile);
foreach ($recentErrors as $error) {
$errorLog[] = [
'timestamp' => time(),
'message' => trim($error)
];
}
$errorLog = array_slice($errorLog, -MAX_LOG_ENTRIES);
writeJsonFile($errorLogFile, $errorLog);
}
}
// Network information
function getNetworkInfo(): array {
$info = [];
if (function_exists('gethostname')) {
$info['hostname'] = gethostname();
}
if (isset($_SERVER['SERVER_ADDR'])) {
$info['server_ip'] = $_SERVER['SERVER_ADDR'];
}
return $info;
}
$networkInfo = getNetworkInfo();
// Performance metrics
$pageLoadTime = round((microtime(true) - $page_start) * 1000, 2);
// SSL/TLS Information
function getSSLInfo(): array {
$ssl = [];
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
$ssl['enabled'] = true;
$ssl['protocol'] = $_SERVER['SSL_PROTOCOL'] ?? 'Unknown';
$ssl['cipher'] = $_SERVER['SSL_CIPHER'] ?? 'Unknown';
} else {
$ssl['enabled'] = false;
}
return $ssl;
}
$sslInfo = getSSLInfo();
// Server Info
$phpVersion = PHP_VERSION;
$osInfo = PHP_OS . ' ' . php_uname('r');
$serverSoftware = $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown';
$hostname = gethostname();
// PHP Extensions
$phpExtensions = get_loaded_extensions();
sort($phpExtensions);
// Request Info
$requestMethod = $_SERVER['REQUEST_METHOD'] ?? '';
$remoteIP = $_SERVER['REMOTE_ADDR'] ?? '';
$referrer = $_SERVER['HTTP_REFERER'] ?? 'Direct';
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
header('Content-Type: text/html; charset=utf-8');
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MaddogNoland Web Server Dashboard v<?= DASHBOARD_VERSION ?></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Professional web server monitoring dashboard">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Inter:wght@300;400;500;600&display=swap">
<style>
:root {
--primary: #ff003c;
--primary-dark: #c9002f;
--primary-glow: #ff003c44;
--bg-dark: #0a0a0a;
--bg-card: #17171a;
--bg-input: #101012;
--text-primary: #ffffff;
--text-secondary: #fa7;
--text-muted: #999;
--border: #333;
--success: #9f6;
--warning: #fa7;
--error: #f44;
--shadow: 0 2px 12px rgba(0,0,0,0.5);
}
* { box-sizing: border-box; }
html, body {
height: 100%;
margin: 0;
padding: 0;
background: var(--bg-dark);
color: var(--text-primary);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
line-height: 1.6;
}
.header {
background: linear-gradient(135deg, var(--primary) 0%, #2d0b15 100%);
padding: 2rem 1rem;
text-align: center;
position: relative;
overflow: hidden;
}
.header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
opacity: 0.1;
}
.header h1 {
font-family: 'Orbitron', monospace;
font-size: clamp(1.8rem, 4vw, 2.7rem);
color: var(--primary);
margin: 0;
letter-spacing: 2px;
text-shadow: 0 0 20px var(--primary-glow);
font-weight: 900;
position: relative;
z-index: 1;
}
.header .subtitle {
font-size: 1.1rem;
color: var(--text-primary);
opacity: 0.8;
margin-top: 0.5rem;
letter-spacing: 1px;
position: relative;
z-index: 1;
}
.header .version {
position: absolute;
top: 1rem;
right: 1rem;
background: rgba(255, 0, 60, 0.2);
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.8rem;
border: 1px solid var(--primary);
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.widget {
background: var(--bg-card);
border: 2px solid var(--primary);
border-radius: 1rem;
box-shadow: var(--shadow), 0 0 32px var(--primary-glow);
padding: 1.5rem;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.widget::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--primary), transparent);
opacity: 0.8;
}
.widget:hover {
transform: translateY(-2px);
box-shadow: var(--shadow), 0 0 48px var(--primary-glow);
}
.widget.alert {
border-color: var(--error);
box-shadow: var(--shadow), 0 0 32px #ff003c88;
animation: pulse-glow 2s infinite;
}
.widget.warning {
border-color: var(--warning);
box-shadow: var(--shadow), 0 0 32px #ffaa7744;
}
.widget.success {
border-color: var(--success);
box-shadow: var(--shadow), 0 0 32px #99ff6644;
}
@keyframes pulse-glow {
0%, 100% { box-shadow: var(--shadow), 0 0 32px #ff003c66; }
50% { box-shadow: var(--shadow), 0 0 48px #ff003caa; }
}
.widget-title {
font-size: 1.2rem;
color: var(--primary);
margin-bottom: 1rem;
font-weight: 600;
display: flex;
align-items: center;
justify-content: space-between;
}
.widget-icon {
font-size: 1.5rem;
opacity: 0.7;
}
.metric {
font-size: 2.2rem;
color: var(--text-primary);
font-weight: 700;
text-shadow: 0 0 8px var(--primary-glow);
margin: 0.5rem 0;
}
.metric-label {
font-size: 0.9rem;
color: var(--text-secondary);
opacity: 0.8;
display: block;
margin-top: -0.5rem;
}
.progress-bar {
width: 100%;
height: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
overflow: hidden;
margin: 0.5rem 0;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary), var(--primary-dark));
border-radius: 4px;
transition: width 0.3s ease;
}
.info-list {
font-size: 0.95rem;
color: var(--text-secondary);
line-height: 1.8;
}
.info-list strong {
color: var(--text-primary);
font-weight: 500;
}
.log-container {
background: var(--bg-input);
border-radius: 8px;
padding: 1rem;
max-height: 200px;
overflow-y: auto;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.85rem;
border: 1px solid var(--border);
}
.log-entry {
margin-bottom: 0.5rem;
padding: 0.3rem 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.log-entry:last-child {
border-bottom: none;
}
.timestamp {
color: var(--text-muted);
font-size: 0.8rem;
}
.chart-container {
height: 120px;
margin: 1rem 0;
position: relative;
}
.chart-canvas {
width: 100%;
height: 100%;
border-radius: 4px;
}
.actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.btn {
background: var(--primary);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
font-size: 0.85rem;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 0.3rem;
}
.btn:hover {
background: var(--primary-dark);
transform: translateY(-1px);
}
.btn-secondary {
background: rgba(255, 255, 255, 0.1);
color: var(--text-primary);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.2);
}
.security-issue {
background: rgba(255, 0, 60, 0.1);
border: 1px solid var(--primary);
border-radius: 6px;
padding: 0.8rem;
margin: 0.5rem 0;
}
.security-issue.suspicious {
background: rgba(255, 68, 68, 0.1);
border-color: var(--error);
}
.ai-assistant {
position: fixed;
bottom: 2rem;
right: 2rem;
background: var(--bg-card);
border: 2px solid var(--primary);
border-radius: 50%;
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
cursor: pointer;
z-index: 1000;
box-shadow: var(--shadow), 0 0 24px var(--primary-glow);
transition: all 0.3s ease;
}
.ai-assistant:hover {
transform: scale(1.05);
box-shadow: var(--shadow), 0 0 36px var(--primary-glow);
}
.ai-chat {
position: fixed;
bottom: 90px;
right: 2rem;
width: 380px;
max-width: 94vw;
background: var(--bg-card);
border: 2px solid var(--primary);
border-radius: 1rem;
box-shadow: var(--shadow), 0 0 32px var(--primary-glow);
display: none;
flex-direction: column;
z-index: 1500;
max-height: 500px;
}
.ai-chat-header {
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
padding: 1rem;
border-radius: 1rem 1rem 0 0;
font-weight: 600;
display: flex;
justify-content: space-between;
align-items: center;
}
.ai-chat-messages {
flex: 1;
padding: 1rem;
max-height: 300px;
overflow-y: auto;
}
.ai-chat-input {
display: flex;
padding: 1rem;
border-top: 1px solid var(--border);
}
.ai-chat-input input {
flex: 1;
background: var(--bg-input);
border: 1px solid var(--border);
color: var(--text-primary);
padding: 0.7rem 1rem;
border-radius: 6px;
outline: none;
margin-right: 0.5rem;
}
.ai-chat-input button {
background: var(--primary);
border: none;
color: white;
padding: 0.7rem 1.2rem;
border-radius: 6px;
cursor: pointer;
transition: background 0.2s ease;
}
.ai-chat-msg {
margin-bottom: 1rem;
padding: 0.7rem 1rem;
border-radius: 8px;
animation: fadeIn 0.3s ease;
}
.ai-chat-msg.user {
background: rgba(255, 0, 60, 0.1);
margin-left: 2rem;
}
.ai-chat-msg.ai {
background: rgba(255, 255, 255, 0.05);
margin-right: 2rem;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.copy-section {
background: var(--bg-card);
border: 2px solid var(--primary);
border-radius: 1rem;
margin: 2rem auto;
max-width: 1200px;
padding: 2rem;
box-shadow: var(--shadow), 0 0 32px var(--primary-glow);
}
.copy-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
flex-wrap: wrap;
gap: 1rem;
}
.copy-title {
color: var(--primary);
font-size: 1.4rem;
font-weight: 600;
}
.code-container {
background: var(--bg-dark);
border-radius: 8px;
padding: 1.5rem;
overflow-x: auto;
border: 1px solid var(--border);
position: relative;
max-height: 500px;
overflow-y: auto;
}
.code-container pre {
margin: 0;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.85rem;
line-height: 1.5;
white-space: pre-wrap;
word-wrap: break-word;
}
.footer {
text-align: center;
padding: 2rem;
color: var(--text-muted);
border-top: 1px solid var(--border);
margin-top: 3rem;
}
@media (max-width: 768px) {
.dashboard-grid {
grid-template-columns: 1fr;
padding: 1rem;
}
.header {
padding: 1.5rem 1rem;
}
.ai-chat {
width: calc(100vw - 2rem);
right: 1rem;
}
}
.status-indicator {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 0.5rem;
}
.status-online { background: var(--success); }
.status-warning { background: var(--warning); }
.status-offline { background: var(--error); }
.tooltip {
position: relative;
cursor: help;
}
.tooltip:hover::after {
content: attr(data-tooltip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
background: var(--bg-dark);
color: var(--text-primary);
padding: 0.5rem 1rem;
border-radius: 6px;
font-size: 0.8rem;
white-space: nowrap;
z-index: 1000;
border: 1px solid var(--border);
}
.pulse-line {
width: 100%;
height: 40px;
margin: 1rem 0 0.5rem 0;
background: transparent;
display: block;
}
.ext-tag {
display: inline-block;
background: var(--bg-input);
padding: 0.3rem 0.6rem;
margin: 0.2rem;
border-radius: 4px;
font-size: 0.8rem;
border: 1px solid var(--border);
}
.ext-tag.critical {
background: var(--primary);
color: white;
}
</style>
</head>
<body>
<header class="header">
<div class="version">v<?= DASHBOARD_VERSION ?></div>
<h1>MaddogNoland <span style="font-weight: 400; opacity: 0.8;">Web Server Dashboard</span></h1>
<div class="subtitle">
Futuristic, all-in-one webmaster dashboard.<br>
<span style="font-size:.91rem; color:#ffffff; opacity:.85;">Standalone, API-free, always on.</span>
</div>
</header>
<main class="dashboard-grid">
<!-- Server Uptime Widget -->
<div class="widget">
<div class="widget-title">
<span>Server Uptime</span>
<a href="?action=reset_uptime" class="btn btn-secondary">↻</a>
</div>
<canvas class="pulse-line" id="uptimePulse"></canvas>
<div class="metric">
<?php
if ($uptimeSeconds !== false) {
$days = floor($uptimeSeconds / 86400);
$hours = floor(($uptimeSeconds % 86400) / 3600);
$minutes = floor(($uptimeSeconds % 3600) / 60);
$seconds = $uptimeSeconds % 60;
printf('%dd %02dh %02dm %02ds', $days, $hours, $minutes, $seconds);
} else {
echo '<span style="color:#fa7;font-size:1.1rem;">No data available</span>';
}
?>
</div>
<span class="metric-label">since last reset</span>
</div>
<!-- Page Speed Widget -->
<div class="widget <?= $pageLoadTime > 400 ? 'alert' : ($pageLoadTime > 200 ? 'warning' : 'success') ?>">
<div class="widget-title">Page Speed</div>
<canvas class="pulse-line" id="speedPulse"></canvas>
<div class="metric"><?= $pageLoadTime ?> ms</div>
<span class="metric-label">current load time</span>
</div>
<!-- PHP Error Log Widget -->
<div class="widget <?= count($recentErrors) > 0 ? 'alert' : 'success' ?>">
<div class="widget-title">PHP Error Log</div>
<div class="log-container" style="max-height: 150px;">
<?php if (empty($recentErrors)): ?>
<div style="color: var(--success);">No recent errors logged or error log not found.</div>
<?php else: ?>
<?php foreach ($recentErrors as $error): ?>
<div class="log-entry"><?= htmlspecialchars(trim($error)) ?></div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<span class="metric-label">last 10 errors</span>
</div>
<!-- Security Scan Widget -->
<div class="widget <?= count($securityIssues) > 0 ? 'alert' : 'success' ?>">
<div class="widget-title">Security Scan</div>
<div class="log-container" style="max-height: 150px;">
<?php if (empty($securityIssues)): ?>
<div style="color: var(--success);">No writable files in web root.</div>
<?php else: ?>
<div style="color: var(--primary); font-weight: bold;">Writable files detected:</div>
<?php foreach ($securityIssues as $issue): ?>
<div class="security-issue <?= $issue['type'] ?>">
<strong><?= ucfirst($issue['type']) ?>:</strong> <?= htmlspecialchars($issue['file']) ?><br>
<small><?= htmlspecialchars($issue['path']) ?></small>
<?php if (isset($issue['permissions'])): ?>
<br><small>Permissions: <?= htmlspecialchars($issue['permissions']) ?></small>
<?php endif; ?>
<?php if (isset($issue['reason'])): ?>
<br><small><?= htmlspecialchars($issue['reason']) ?></small>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<span class="metric-label">filesystem scan</span>
</div>
<!-- Server Info Widget -->
<div class="widget">
<div class="widget-title">Server Info</div>
<div class="info-list">
<strong>Host:</strong> <?= htmlspecialchars($hostname) ?><br>
<strong>OS:</strong> <?= htmlspecialchars($osInfo) ?><br>
<strong>PHP:</strong> <?= htmlspecialchars($phpVersion) ?><br>
<strong>Server:</strong> <?= htmlspecialchars($serverSoftware) ?><br>
<strong>SSL/TLS:</strong>
<span class="status-indicator <?= $sslInfo['enabled'] ? 'status-online' : 'status-offline' ?>"></span>
<?= $sslInfo['enabled'] ? 'Enabled (' . htmlspecialchars($sslInfo['protocol'] ?? 'Unknown') . ')' : 'Disabled' ?>
</div>
<span class="metric-label">environment</span>
</div>
<!-- Disk Space Widget -->
<div class="widget <?= ($diskSpace['free'] / $diskSpace['total'] < 0.1) ? 'alert' : (($diskSpace['free'] / $diskSpace['total'] < 0.2) ? 'warning' : '') ?>">
<div class="widget-title">Disk Space</div>
<div class="metric" style="font-size: 1.5rem;">
<?php
if ($diskSpace['free'] !== false && $diskSpace['total'] !== false) {
printf("%.2f GB free / %.2f GB", $diskSpace['free'] / 1073741824, $diskSpace['total'] / 1073741824);
} else {
echo "N/A";
}
?>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: <?= 100 - (($diskSpace['free'] / $diskSpace['total']) * 100) ?>%"></div>
</div>
<span class="metric-label">in <?= htmlspecialchars(__DIR__) ?></span>
</div>
<!-- PHP Extensions Widget -->
<div class="widget">
<div class="widget-title">PHP Extensions</div>
<div class="log-container" style="max-height: 200px;">
<?php
$criticalExts = ['curl', 'gd', 'mbstring', 'openssl', 'pdo', 'zip', 'json', 'mysqli'];
foreach ($phpExtensions as $ext): ?>
<span class="ext-tag <?= in_array(strtolower($ext), $criticalExts) ? 'critical' : '' ?>">
<?= htmlspecialchars($ext) ?>
</span>
<?php endforeach; ?>
</div>
<span class="metric-label">loaded (<?= count($phpExtensions) ?> total)</span>
</div>
<!-- PHP INI Settings Widget -->
<div class="widget">
<div class="widget-title">PHP INI Settings</div>
<div class="info-list">
<strong>Memory limit:</strong> <?= htmlspecialchars(ini_get('memory_limit')) ?><br>
<strong>Max execution time:</strong> <?= htmlspecialchars(ini_get('max_execution_time')) ?>s<br>
<strong>Post max size:</strong> <?= htmlspecialchars(ini_get('post_max_size')) ?><br>
<strong>Upload max size:</strong> <?= htmlspecialchars(ini_get('upload_max_filesize')) ?><br>
<strong>Max input vars:</strong> <?= htmlspecialchars(ini_get('max_input_vars')) ?><br>
<strong>Error reporting:</strong> <?= error_reporting() ? 'On' : 'Off' ?><br>
<strong>OPcache:</strong> <?= extension_loaded('opcache') && ini_get('opcache.enable') ? 'Enabled' : 'Disabled' ?>
</div>
<span class="metric-label">key limits</span>
</div>
<!-- Request Info Widget -->
<div class="widget">
<div class="widget-title">Request Info</div>
<div class="info-list">
<strong>Method:</strong> <?= htmlspecialchars($requestMethod) ?><br>
<strong>Your IP:</strong> <?= htmlspecialchars($remoteIP) ?><br>
<strong>Referrer:</strong> <?= htmlspecialchars($referrer) ?><br>
<strong>User Agent:</strong> <?= htmlspecialchars(substr($userAgent, 0, 50)) ?>...<br>
<strong>Accept Language:</strong> <?= htmlspecialchars($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'N/A') ?><br>
<strong>Request URI:</strong> <?= htmlspecialchars($_SERVER['REQUEST_URI'] ?? 'N/A') ?><br>
<strong>Protocol:</strong> <?= htmlspecialchars($_SERVER['SERVER_PROTOCOL'] ?? 'N/A') ?>
</div>
<span class="metric-label">current session</span>
</div>
<!-- Performance Metrics Widget -->
<div class="widget">
<div class="widget-title">Performance Metrics</div>
<div class="chart-container">
<canvas class="chart-canvas" id="performanceChart"></canvas>
</div>
<div class="info-list">
<strong>Memory Usage:</strong> <?= formatBytes($memoryUsage['current']) ?><br>
<strong>Memory Peak:</strong> <?= formatBytes($memoryUsage['peak']) ?><br>
<strong>Load Average:</strong> <?= number_format($systemLoad[0], 2) ?>, <?= number_format($systemLoad[1], 2) ?>, <?= number_format($systemLoad[2], 2) ?><br>
<strong>Page Load:</strong> <?= $pageLoadTime ?>ms
</div>
<span class="metric-label">system performance</span>
</div>
<!-- Access Log Widget -->
<div class="widget">
<div class="widget-title">Access Log</div>
<div class="chart-container">
<canvas class="chart-canvas" id="accessChart"></canvas>
</div>
<div class="log-container">
<?php foreach (array_slice(array_reverse($accessLog), 0, 10) as $entry): ?>
<div class="log-entry">
<div class="timestamp"><?= date('Y-m-d H:i:s', $entry['timestamp']) ?></div>
<div>
<strong><?= htmlspecialchars($entry['method']) ?></strong>
<?= htmlspecialchars($entry['ip']) ?>
<small><?= htmlspecialchars(substr($entry['user_agent'], 0, 40)) ?>...</small>
</div>
</div>
<?php endforeach; ?>
</div>
<span class="metric-label">last <?= count($accessLog) ?> requests</span>
</div>
<!-- Quick Actions Widget -->
<div class="widget">
<div class="widget-title">Quick Actions</div>
<div class="actions">
<a href="?action=reset_uptime" class="btn">๐ Reset Uptime</a>
<a href="?action=clear_logs" class="btn">๐งน Clear Logs</a>
<a href="?action=export_data" class="btn">๐ Export Data</a>
<button onclick="window.location.reload()" class="btn">๐ Refresh</button>
</div>
<div class="info-list" style="margin-top: 1rem;">
<strong>Last Updated:</strong> <?= date('Y-m-d H:i:s') ?><br>
<strong>Timezone:</strong> <?= date_default_timezone_get() ?><br>
<strong>Dashboard Version:</strong> <?= DASHBOARD_VERSION ?>
</div>
</div>
</main>
<!-- AI Assistant -->
<div class="ai-assistant" id="aiBtn" title="Webmaster AI Assistant">๐ค</div>
<div class="ai-chat" id="aiChat">
<div class="ai-chat-header">
<span>Maddog AI Assistant</span>
<button onclick="toggleAIChat()" style="background: none; border: none; color: white; cursor: pointer; font-size: 1.2rem;">ร</button>
</div>
<div class="ai-chat-messages" id="aiMessages">
<div class="ai-chat-msg ai">Hi! I'm Maddog, your sleepless webmaster assistant. Ask me anything about your site, server performance, security, or PHP configuration.</div>
</div>
<form class="ai-chat-input" id="aiForm" autocomplete="off">
<input type="text" id="aiInput" placeholder="Type your question..." />
<button type="submit">โบ</button>
</form>
</div>
<!-- Copy This Dashboard Section -->
<section class="copy-section">
<div class="copy-header">
<div class="copy-title">
Copy This Dashboard to Your Own Website
</div>
<div class="actions">
<button class="btn" onclick="copyDashboardCode()">๐ Copy Code</button>
<button class="btn" onclick="downloadDashboard()">๐พ Download PHP File</button>
</div>
</div>
<div style="color: var(--text-secondary); margin-bottom: 1.5rem; line-height: 1.6;">
This complete dashboard is a single PHP file that requires <strong>no setup or configuration</strong>.
Simply copy the code below and save it as <code style="background: var(--bg-input); padding: 0.2rem 0.4rem; border-radius: 4px;">dash.php</code>
on your web server or whatever name you want so long as it is a php file.
</div>
<div style="background: rgba(255, 0, 60, 0.1); border: 1px solid var(--primary); border-radius: 8px; padding: 1.5rem; margin-bottom: 1.5rem;">
<h3 style="color: var(--primary); margin-top: 0; font-size: 1.1rem;">๐ Installation Instructions:</h3>
<ol style="margin: 0; padding-left: 1.5rem; color: var(--text-secondary);">
<li>Copy the PHP code below or use the download button</li>
<li>Save the code as <code>dash.php</code> in your web directory</li>
<li>Ensure your web server has PHP 8.0+ with write permissions</li>
<li>Visit the file in your browser - it will auto-create needed directories</li>
<li>The dashboard starts monitoring immediately!</li>
</ol>
<div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid rgba(255, 0, 60, 0.3);">
<strong style="color: var(--primary);">System Requirements:</strong><br>
<span style="color: var(--text-secondary);">
โข PHP 8.0+ with standard extensions<br>
โข Web server with write permissions<br>
โข No database required<br>
โข Works on shared hosting
</span>
</div>
</div>
<div class="code-container">
<pre id="dashboardCode"><?= htmlspecialchars(file_get_contents(__FILE__)) ?></pre>
</div>
<div style="margin-top: 1.5rem; padding: 1rem; background: rgba(153, 255, 102, 0.1); border: 1px solid var(--success); border-radius: 8px;">
<strong style="color: var(--success);">โ
Features Included:</strong>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.5rem; margin-top: 0.5rem; color: var(--text-secondary);">
<span>โข Real-time server monitoring</span>
<span>โข Security vulnerability scanning</span>
<span>โข PHP configuration analysis</span>
<span>โข Performance metrics</span>
<span>โข Error log monitoring</span>
<span>โข Access log tracking</span>
<span>โข AI-powered assistance</span>
<span>โข Responsive mobile design</span>
<span>โข Data export functionality</span>
<span>โข Zero external dependencies</span>
</div>
</div>
</section>
<footer class="footer">
<div>
© <?= date('Y') ?> MaddogNoland.com •
<strong>Web Server Dashboard v<?= DASHBOARD_VERSION ?></strong> •
Standalone Edition
</div>
<div style="margin-top: 0.5rem; opacity: 0.7;">
Professional server monitoring โข Open source โข Free for personal and commercial use
</div>
</footer>
<script>
// --- Pulse line animation (visual only, not data) ---
function drawPulse(canvas, arr, color='#ff003c', anim=false) {
const ctx = canvas.getContext('2d');
const w = canvas.width = canvas.offsetWidth, h = canvas.height = canvas.offsetHeight;
ctx.clearRect(0,0,w,h);
ctx.beginPath();
ctx.moveTo(0, h/2);
for(let i=0;i<arr.length;i++){
let x = i/(arr.length-1)*w;
let y = h/2 - arr[i]*(h/2-5);
ctx.lineTo(x,y);
}
ctx.strokeStyle=color;
ctx.shadowColor=color;
ctx.shadowBlur=12;
ctx.lineWidth=2.5;
ctx.stroke();
ctx.shadowBlur=0;
for(let i=0;i<arr.length;i+=Math.max(1,Math.floor(arr.length/12))){
let x = i/(arr.length-1)*w;
let y = h/2 - arr[i]*(h/2-5);
ctx.beginPath();
ctx.arc(x,y,4,0,2*Math.PI);
ctx.fillStyle=color+'88';
ctx.fill();
}
if(anim) setTimeout(()=>drawPulse(canvas,arr.map((v,i)=>Math.max(-1,Math.min(1, v+((Math.random()-.5)*.08)))), color, anim), 130);
}
// Chart drawing functions for performance metrics
function drawChart(canvas, data, color = '#ff003c', type = 'line') {
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * window.devicePixelRatio;
canvas.height = rect.height * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
const w = rect.width;
const h = rect.height;
const padding = 10;
ctx.clearRect(0, 0, w, h);
if (data.length < 2) return;
const maxVal = Math.max(...data);
const minVal = Math.min(...data);
const range = maxVal - minVal || 1;
// Draw grid
ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)';
ctx.lineWidth = 1;
for (let i = 0; i <= 4; i++) {
const y = padding + (i / 4) * (h - 2 * padding);
ctx.beginPath();
ctx.moveTo(padding, y);
ctx.lineTo(w - padding, y);
ctx.stroke();
}
// Draw chart line
ctx.beginPath();
ctx.strokeStyle = color;
ctx.lineWidth = 2;
ctx.shadowColor = color;
ctx.shadowBlur = 8;
for (let i = 0; i < data.length; i++) {
const x = padding + (i / (data.length - 1)) * (w - 2 * padding);
const y = h - padding - ((data[i] - minVal) / range) * (h - 2 * padding);
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
// Draw points
ctx.shadowBlur = 0;
ctx.fillStyle = color;
for (let i = 0; i < data.length; i += Math.max(1, Math.floor(data.length / 20))) {
const x = padding + (i / (data.length - 1)) * (w - 2 * padding);
const y = h - padding - ((data[i] - minVal) / range) * (h - 2 * padding);
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2 * Math.PI);
ctx.fill();
}
}
// Initialize all charts
window.addEventListener('DOMContentLoaded', () => {
// Uptime pulse animation
let upArr = Array.from({length: 18}, (_, i) => Math.sin(i / 3) * .13 + Math.random() * .07);
const uptimePulse = document.getElementById('uptimePulse');
if (uptimePulse) drawPulse(uptimePulse, upArr, '#ff003c', true);
// Speed pulse animation
let speedArr = Array.from({length: 16}, (_, i) => Math.sin(i / 2 + .9) * .16 + Math.random() * .09);
const speedPulse = document.getElementById('speedPulse');
if (speedPulse) drawPulse(speedPulse, speedArr, '#ff003c', true);
// Performance chart
const perfData = Array.from({length: 24}, (_, i) =>
Math.sin(i * 0.3) * 100 + Math.random() * 50 + 200
);
const perfCanvas = document.getElementById('performanceChart');
if (perfCanvas) drawChart(perfCanvas, perfData, '#ff003c');
// Access chart - requests over time
const accessData = Array.from({length: 12}, (_, i) =>
Math.floor(Math.random() * 10 + 5)
);
const accessCanvas = document.getElementById('accessChart');
if (accessCanvas) drawChart(accessCanvas, accessData, '#ff003c');
});
// --- AI Assistant (Enhanced) ---
const aiBtn = document.getElementById('aiBtn');
const aiChat = document.getElementById('aiChat');
function toggleAIChat() {
const isVisible = aiChat.style.display === 'flex';
aiChat.style.display = isVisible ? 'none' : 'flex';
if (!isVisible) {
document.getElementById('aiInput').focus();
}
}
aiBtn.onclick = toggleAIChat;
document.getElementById('aiForm').onsubmit = function(e) {
e.preventDefault();
let input = document.getElementById('aiInput');
let msg = input.value.trim();
if (!msg) return;
let msgs = document.getElementById('aiMessages');
msgs.innerHTML += `<div class="ai-chat-msg user">${escapeHtml(msg)}</div>`;
input.value = '';
msgs.scrollTop = msgs.scrollHeight;
// Enhanced AI responses
let m = msg.toLowerCase();
let r = '';
if (m.includes('error')) r = "Check the PHP Error Log widget above for recent errors. To increase log detail, adjust 'error_reporting' and 'log_errors' in your php.ini. Common fixes: increase memory_limit, check file permissions, verify database connections.";
else if (m.includes('speed') || m.includes('slow') || m.includes('performance')) r = "Optimize performance by: enabling OPcache, optimizing database queries, using a CDN, enabling gzip compression, upgrading to PHP 8+, and optimizing images. The Performance Metrics widget shows current system load.";
else if (m.includes('security') || m.includes('hack') || m.includes('vulnerable')) r = "Security recommendations: Keep PHP updated, use strong passwords, enable HTTPS, set proper file permissions (644 for files, 755 for directories), regularly scan for malware, and review the Security Scan widget for issues.";
else if (m.match(/uptime|how long|running/)) r = "Check the Server Uptime widget for time since the last reset. This tracks how long the dashboard has been monitoring your server. Use the reset button to start fresh tracking.";
else if (m.match(/php.*version|php.*update/)) r = "Your PHP version is " + <?= json_encode($phpVersion) ?> + ". Check the Server Info widget for details. Consider upgrading to the latest PHP 8.x for better performance and security.";
else if (m.match(/php.*ext|extension/)) r = "Loaded PHP extensions are shown in the PHP Extensions widget. Critical extensions like curl, gd, mbstring, and openssl are highlighted. Install missing extensions using your server's package manager.";
else if (m.match(/disk|space|storage/)) r = "Disk Space widget shows available space. Keep at least 10% free to avoid issues. If running low, consider: cleaning log files, removing old backups, optimizing images, or upgrading storage.";
else if (m.match(/memory|limit|ram/)) r = "PHP memory limit is <?= htmlspecialchars(ini_get('memory_limit')) ?>. Current usage shown in Performance Metrics. If needed, increase memory_limit in php.ini or contact your hosting provider.";
else if (m.match(/ssl|https|certificate/)) r = "SSL/TLS status is shown in Server Info. To enable HTTPS: get an SSL certificate (free with Let's Encrypt), configure your web server, update site URLs, and set up redirects.";
else if (m.match(/backup|export/)) r = "Use 'Export Data' in Quick Actions to download your monitoring data. For full backups: regularly backup files and database, store offsite, test restores, and automate the process.";
else if (m.match(/hello|hi|hey/)) r = "Hello! I'm your AI webmaster assistant, always monitoring your server. What can I help you optimize today?";
else if (m.match(/help|what|how/)) r = "I can help with: performance optimization, security hardening, error troubleshooting, PHP configuration, SSL setup, disk management, and general server maintenance. What specific area interests you?";
else r = "I'm here to help with server management! Try asking about: performance issues, security concerns, PHP errors, disk space, SSL certificates, or any server-related questions you have.";
setTimeout(() => {
msgs.innerHTML += `<div class="ai-chat-msg ai">${r}</div>`;
msgs.scrollTop = msgs.scrollHeight;
}, 370 + Math.random() * 500);
return false;
};
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// --- Copy Dashboard Code Button ---
function copyDashboardCode() {
const code = document.getElementById('dashboardCode').textContent;
navigator.clipboard.writeText(code).then(() => {
alert('โ
Dashboard code copied to clipboard! Paste it into a new .php file on your server.');
}).catch(() => {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = code;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
alert('โ
Dashboard code copied to clipboard! Paste it into a new .php file on your server.');
});
}
function downloadDashboard() {
const code = document.getElementById('dashboardCode').textContent;
const blob = new Blob([code], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'maddog_dashboard.php';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
// Auto-refresh charts periodically
setInterval(() => {
// Refresh pulse animations
const uptimePulse = document.getElementById('uptimePulse');
const speedPulse = document.getElementById('speedPulse');
if (uptimePulse) {
let upArr = Array.from({length: 18}, (_, i) => Math.sin(i / 3 + Date.now() / 1000) * .13 + Math.random() * .07);
drawPulse(uptimePulse, upArr, '#ff003c', false);
}
if (speedPulse) {
let speedArr = Array.from({length: 16}, (_, i) => Math.sin(i / 2 + .9 + Date.now() / 1000) * .16 + Math.random() * .09);
drawPulse(speedPulse, speedArr, '#ff003c', false);
}
}, 2000);
// Console welcome message
console.log(`
๐ MaddogNoland Web Server Dashboard v<?= DASHBOARD_VERSION ?> loaded successfully!
Features loaded:
โ
Real-time server monitoring
โ
Security vulnerability scanning
โ
Performance metrics tracking
โ
AI-powered assistance
โ
Professional responsive design
Ready to monitor your server 24/7!
`);
</script>
</body>
</html>