161 lines
3.1 KiB
PHP
161 lines
3.1 KiB
PHP
<?php
|
||
$db = new PDO("sqlite:/var/www/data/ssl.db");
|
||
|
||
$domain = $_GET["domain"] ?? "";
|
||
if (!$domain) die("Domain missing");
|
||
|
||
$stmt = $db->prepare("
|
||
SELECT checked_at, expires
|
||
FROM history
|
||
WHERE domain = :d
|
||
ORDER BY checked_at ASC
|
||
");
|
||
$stmt->execute([":d" => $domain]);
|
||
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||
|
||
$labels = [];
|
||
$values = [];
|
||
|
||
foreach ($data as $row) {
|
||
$labels[] = date("Y-m-d H:i", $row["checked_at"]);
|
||
$values[] = round(($row["expires"] - $row["checked_at"]) / 86400, 1);
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>SSL History – <?= htmlspecialchars($domain) ?></title>
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||
<style>
|
||
:root {
|
||
--bg: #f4f4f4;
|
||
--text: #000;
|
||
--card: #fff;
|
||
--border: #ddd;
|
||
--header: #222;
|
||
--link: #0066cc;
|
||
}
|
||
|
||
body.dark {
|
||
--bg: #121212;
|
||
--text: #e0e0e0;
|
||
--card: #1e1e1e;
|
||
--border: #333;
|
||
--header: #000;
|
||
--link: #4ea3ff;
|
||
}
|
||
|
||
body {
|
||
font-family: Arial;
|
||
background: var(--bg);
|
||
color: var(--text);
|
||
}
|
||
|
||
table {
|
||
border-collapse: collapse;
|
||
width: 80%;
|
||
margin: auto;
|
||
background: var(--card);
|
||
}
|
||
|
||
th, td {
|
||
padding: 10px;
|
||
border: 1px solid var(--border);
|
||
text-align: center;
|
||
}
|
||
|
||
th {
|
||
background: var(--header);
|
||
color: #fff;
|
||
}
|
||
|
||
a {
|
||
color: var(--link);
|
||
}
|
||
|
||
.ok { color: #4caf50; font-weight: bold; }
|
||
.warn { color: #ff9800; font-weight: bold; }
|
||
.critical { color: #f44336; font-weight: bold; }
|
||
|
||
.toggle {
|
||
position: fixed;
|
||
top: 15px;
|
||
right: 15px;
|
||
cursor: pointer;
|
||
font-size: 20px;
|
||
}
|
||
|
||
body { font-family: Arial; background:#f4f4f4; text-align:center }
|
||
canvas { background:#fff; padding:20px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="toggle" onclick="toggleDark()">🌙</div>
|
||
|
||
<script>
|
||
function applyTheme() {
|
||
const dark = localStorage.getItem("darkmode") === "1";
|
||
document.body.classList.toggle("dark", dark);
|
||
document.querySelector(".toggle").textContent = dark ? "☀️" : "🌙";
|
||
}
|
||
|
||
function toggleDark() {
|
||
localStorage.setItem(
|
||
"darkmode",
|
||
localStorage.getItem("darkmode") === "1" ? "0" : "1"
|
||
);
|
||
applyTheme();
|
||
}
|
||
|
||
applyTheme();
|
||
</script>
|
||
|
||
<h2>Certificate History: <?= htmlspecialchars($domain) ?></h2>
|
||
|
||
<canvas id="chart" width="900" height="400"></canvas>
|
||
|
||
<script>
|
||
new Chart(document.getElementById('chart'), {
|
||
type: 'line',
|
||
data: {
|
||
labels: <?= json_encode($labels) ?>,
|
||
datasets: [{
|
||
label: 'Days until expiration',
|
||
data: <?= json_encode($values) ?>,
|
||
borderColor: getComputedStyle(document.body)
|
||
.getPropertyValue('--link'),
|
||
backgroundColor: 'transparent',
|
||
tension: 0.1
|
||
}]
|
||
},
|
||
options: {
|
||
scales: {
|
||
x: { ticks: { color: getComputedStyle(document.body).color } },
|
||
y: { ticks: { color: getComputedStyle(document.body).color } }
|
||
},
|
||
plugins: {
|
||
legend: {
|
||
labels: {
|
||
color: getComputedStyle(document.body).color
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
options: {
|
||
scales: {
|
||
y: {
|
||
title: { display: true, text: 'Days Remaining' }
|
||
},
|
||
x: {
|
||
title: { display: true, text: 'Check Time' }
|
||
}
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<p><a href="/">← Back</a></p>
|
||
|
||
</body>
|
||
</html>
|