Archived
Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
config.php
|
||||
stats.json
|
||||
@@ -0,0 +1,7 @@
|
||||
# Generic Stats
|
||||
|
||||
A generic stats page for PHP capable webservers.
|
||||
|
||||
## Setup
|
||||
|
||||
Create `config.php` based on `config.php.example`.
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
$stats_file = "stats.json";
|
||||
$recalculate_cooldown_seconds = 5;
|
||||
$show_actions = true;
|
||||
$auto_recalculate_seconds = 86400;
|
||||
$absolute_root = "/";
|
||||
|
||||
$disks = [
|
||||
"Root" => [
|
||||
"mount_point" => "/",
|
||||
"categories" => [
|
||||
"Additional Packages" => [
|
||||
"path" => "/opt",
|
||||
"color" => "cornflowerblue",
|
||||
],
|
||||
"User Pictures" => [
|
||||
"path" => "/home/myuser/pics",
|
||||
"color" => "peachpuff",
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
require "config.php";
|
||||
|
||||
if (isset($_GET["recalculate"])) {
|
||||
if (file_exists($stats_file)) {
|
||||
$time = json_decode(file_get_contents($stats_file), true)["time"] ?? 0;
|
||||
if ($time + $recalculate_cooldown_seconds < time()) {
|
||||
unlink($stats_file);
|
||||
}
|
||||
}
|
||||
|
||||
header("Location: " . $absolute_root);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
function human_readable_filesize(int $bytes, int $decimals = 1): string
|
||||
{
|
||||
$units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'];
|
||||
$factor = $bytes > 0 ? floor(log($bytes, 1024)) : 0;
|
||||
$factor = min($factor, count($units) - 1);
|
||||
|
||||
return sprintf("%.{$decimals}f %s", $bytes / (1024 ** $factor), $units[$factor]);
|
||||
}
|
||||
|
||||
if (!file_exists($stats_file)) {
|
||||
$stats = [
|
||||
"storage" => $disks,
|
||||
];
|
||||
|
||||
foreach ($stats["storage"] as $disk_name => $disk_stats) {
|
||||
$stats["storage"][$disk_name]["total_bytes"] = disk_total_space($disk_stats["mount_point"]);
|
||||
$stats["storage"][$disk_name]["used_bytes"] = (int) trim(shell_exec('df --block-size=1 --output=used ' . escapeshellarg($disk_stats["mount_point"]) . ' | tail -1'));
|
||||
$other_used = $stats["storage"][$disk_name]["used_bytes"];
|
||||
if (!isset($disk_stats["categories"])) {
|
||||
$stats["storage"][$disk_name]["categories"] = [];
|
||||
}
|
||||
|
||||
foreach ($disk_stats["categories"] as $category_name => $category_stats) {
|
||||
$stats["storage"][$disk_name]["categories"][$category_name]["bytes"] = (int) shell_exec('du -sb ' . escapeshellarg($stats["storage"][$disk_name]["categories"][$category_name]["path"]) . ' | cut -f1');
|
||||
$other_used -= $stats["storage"][$disk_name]["categories"][$category_name]["bytes"];
|
||||
}
|
||||
|
||||
$stats["storage"][$disk_name]["categories"]["Other"] = [
|
||||
"color" => "white",
|
||||
"bytes" => max(0, $other_used),
|
||||
];
|
||||
}
|
||||
|
||||
$stats["time"] = time();
|
||||
|
||||
$stats = file_put_contents($stats_file, json_encode($stats), LOCK_EX);
|
||||
}
|
||||
|
||||
$stats = json_decode(file_get_contents($stats_file), true);
|
||||
|
||||
if ($stats["time"] + $auto_recalculate_seconds < time()) {
|
||||
header("Location: ?recalculate");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
?>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Stats</title>
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="<?= $absolute_root ?>styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div>
|
||||
<h2>Storage</h2>
|
||||
<?php foreach ($stats["storage"] as $disk_name => $disk_stats): ?>
|
||||
<h3>
|
||||
<?= $disk_name ?>
|
||||
<?= human_readable_filesize($disk_stats["total_bytes"]) ?>
|
||||
<?= round(100 * $disk_stats["used_bytes"] / $disk_stats["total_bytes"]) ?>%
|
||||
</h3>
|
||||
<div class="storage-gauge">
|
||||
<?php foreach ($disk_stats["categories"] as $category_name => $category_stats): ?>
|
||||
<div style="--width: <?= round(100 * $category_stats["bytes"] / $disk_stats["total_bytes"], 2) ?>%; --color: <?= $category_stats["color"] ?? "red" ?>"></div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="storage-breakdown">
|
||||
<?php foreach ($disk_stats["categories"] as $category_name => $category_stats): ?>
|
||||
<div>
|
||||
<div class="storage-circle" style="--color: <?= $category_stats["color"] ?? "red" ?>"></div>
|
||||
<p><?= human_readable_filesize($category_stats["bytes"]) ?></p>
|
||||
<p><?= round(100 * $category_stats["bytes"] / $disk_stats["total_bytes"]) ?>%</p>
|
||||
<p><?= $category_name ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if ($show_actions): ?>
|
||||
<div>
|
||||
<h2>Actions</h2>
|
||||
<form method="get">
|
||||
<input type="hidden" name="recalculate"></input>
|
||||
<button>Recalculate</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
min-width: min(100svw, 600px);
|
||||
border: 1px solid white;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
gap: 10px;
|
||||
|
||||
& > * {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.storage-gauge {
|
||||
width: 100%;
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
border-radius: 10px;
|
||||
background-color: grey;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
align-items: stretch;
|
||||
|
||||
& > * {
|
||||
flex: 0 0 auto;
|
||||
display: inline-block;
|
||||
width: var(--width);
|
||||
background-color: var(--color);
|
||||
}
|
||||
}
|
||||
|
||||
.storage-breakdown {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-flow: column nowrap;
|
||||
|
||||
& > * {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
align-items: center;
|
||||
gap: 0.5em;
|
||||
|
||||
& > * {
|
||||
flex: 0 0 auto;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.storage-circle {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: var(--color);
|
||||
}
|
||||
Reference in New Issue
Block a user