From f9951daf226bb98d2c061d39c3641000943441b5 Mon Sep 17 00:00:00 2001 From: Satakun Utama Date: Thu, 15 Oct 2026 17:17:16 +0700 Subject: [PATCH] Initial commit --- block_devices.php | 16 +++ device.php | 123 ++++++++++++++++++ index.php | 29 +++++ resources/smartctl-proxy.sh | 8 ++ resources/sudoers | 1 + sakura.css | 243 ++++++++++++++++++++++++++++++++++++ zpool.php | 12 ++ 7 files changed, 432 insertions(+) create mode 100644 block_devices.php create mode 100644 device.php create mode 100644 index.php create mode 100755 resources/smartctl-proxy.sh create mode 100644 resources/sudoers create mode 100644 sakura.css create mode 100644 zpool.php diff --git a/block_devices.php b/block_devices.php new file mode 100644 index 0000000..e695bad --- /dev/null +++ b/block_devices.php @@ -0,0 +1,16 @@ +blockdevices; +} + +?> diff --git a/device.php b/device.php new file mode 100644 index 0000000..586fa9c --- /dev/null +++ b/device.php @@ -0,0 +1,123 @@ + ["pipe", "w"]], $pipes); + + if (!is_resource($proc)) { + return; + } + + $data = stream_get_contents($pipes[1]); + fclose($pipes[1]); + $retval = proc_close($proc); + + if ($retval == 0) { + return json_decode($data); + } + return $data . "\nProcess exited with $retval"; +} + +const NVME_DATA_UNIT = 1000 * 512; +const GIGABYTE = 10 ** 9; +const TERABYTE = 10 ** 12; + +function data_unit($data) +{ + $num = $data * NVME_DATA_UNIT; + if ($num < TERABYTE) { + return strval(round($num / GIGABYTE, 2)) . " GB"; + } + return strval(round($num / TERABYTE, 2)) . " TB"; +} + +$data = get_data(); +?> + + + + + + + + Block Device Status + + +

+ "; + echo $data; + echo ""; + } else { + ?> + + + + + + + + + + + + + + + + + + + + + +
PropertyValue
smartctl smartctl->version, + ) ?>
Model Namemodel_name ?>
Temperaturetemperature->current ?> Celcius
+ +

SMART data

+ + + + + + + + + + device->type == "nvme") { + $health = $data->nvme_smart_health_information_log; ?> + + + + + + + + + + + +
PropertyValue
Data Units Readdata_units_read) . + " (" . + data_unit($health->data_units_read) . + ")" ?>
Data Units Writtendata_units_written) . + " (" . + data_unit($health->data_units_written) . + ")" ?>
+ +
+ Original Output +
smartctl->output) ?>
+
+ + + diff --git a/index.php b/index.php new file mode 100644 index 0000000..45113ff --- /dev/null +++ b/index.php @@ -0,0 +1,29 @@ + + + + + + + Server Status + + +

+ + + diff --git a/resources/smartctl-proxy.sh b/resources/smartctl-proxy.sh new file mode 100755 index 0000000..647e560 --- /dev/null +++ b/resources/smartctl-proxy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -euo pipefail + +dev="${1:-}" + +exec /usr/bin/smartctl --json=co -a "/dev/$dev" + diff --git a/resources/sudoers b/resources/sudoers new file mode 100644 index 0000000..ed61143 --- /dev/null +++ b/resources/sudoers @@ -0,0 +1 @@ +linesofcodes ALL=(root) NOPASSWD: /usr/local/sbin/smartctl-proxy.sh sd[a-z], /usr/local/sbin/smartctl-proxy.sh nvme[0-9]n[0-9] diff --git a/sakura.css b/sakura.css new file mode 100644 index 0000000..7d1886a --- /dev/null +++ b/sakura.css @@ -0,0 +1,243 @@ +/* Sakura.css v1.5.1 + * ================ + * Minimal css theme. + * Project: https://github.com/oxalorg/sakura/ + */ +/* Body */ +html { + font-size: 62.5%; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; +} + +body { + font-size: 1.8rem; + line-height: 1.618; + max-width: 38em; + margin: auto; + color: #4a4a4a; + background-color: #f9f9f9; + padding: 13px; +} + +@media (max-width: 684px) { + body { + font-size: 1.53rem; + } +} +@media (max-width: 382px) { + body { + font-size: 1.35rem; + } +} +h1, h2, h3, h4, h5, h6 { + line-height: 1.1; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-weight: 700; + margin-top: 3rem; + margin-bottom: 1.5rem; + overflow-wrap: break-word; + word-wrap: break-word; + -ms-word-break: break-all; + word-break: break-word; +} + +h1 { + font-size: 2.35em; +} + +h2 { + font-size: 2em; +} + +h3 { + font-size: 1.75em; +} + +h4 { + font-size: 1.5em; +} + +h5 { + font-size: 1.25em; +} + +h6 { + font-size: 1em; +} + +p { + margin-top: 0px; + margin-bottom: 2.5rem; +} + +small, sub, sup { + font-size: 75%; +} + +hr { + border-color: #1d7484; +} + +a { + text-decoration: none; + color: #1d7484; +} +a:visited { + color: #144f5a; +} +a:hover { + color: #982c61; + border-bottom: 2px solid #4a4a4a; +} + +ul { + padding-left: 1.4em; + margin-top: 0px; + margin-bottom: 2.5rem; +} + +li { + margin-bottom: 0.4em; +} + +blockquote { + margin-left: 0px; + margin-right: 0px; + padding-left: 1em; + padding-top: 0.8em; + padding-bottom: 0.8em; + padding-right: 0.8em; + border-left: 5px solid #1d7484; + margin-bottom: 2.5rem; + background-color: #f1f1f1; +} + +blockquote p { + margin-bottom: 0; +} + +img, video { + height: auto; + max-width: 100%; + margin-top: 0px; + margin-bottom: 2.5rem; +} + +/* Pre and Code */ +pre { + background-color: #f1f1f1; + display: block; + padding: 1em; + overflow-x: auto; + margin-top: 0px; + margin-bottom: 2.5rem; + font-size: 0.9em; +} + +code, kbd, samp { + font-size: 0.9em; + padding: 0 0.5em; + background-color: #f1f1f1; + white-space: pre-wrap; +} + +pre > code { + padding: 0; + background-color: transparent; + white-space: pre; + font-size: 1em; +} + +/* Tables */ +table { + text-align: justify; + width: 100%; + border-collapse: collapse; + margin-bottom: 2rem; +} + +td, th { + padding: 0.5em; + border-bottom: 1px solid #f1f1f1; +} + +/* Buttons, forms and input */ +input, textarea { + border: 1px solid #4a4a4a; +} +input:focus, textarea:focus { + border: 1px solid #1d7484; +} + +textarea { + width: 100%; +} + +.button, button, +input[type=submit], +input[type=reset], +input[type=button], +input[type=file]::file-selector-button { + display: inline-block; + padding: 5px 10px; + text-align: center; + text-decoration: none; + white-space: nowrap; + background-color: #1d7484; + color: #f9f9f9; + border-radius: 1px; + border: 1px solid #1d7484; + cursor: pointer; + box-sizing: border-box; +} +.button:hover, button:hover, +input[type=submit]:hover, +input[type=reset]:hover, +input[type=button]:hover, +input[type=file]::file-selector-button:hover { + background-color: #982c61; + color: #f9f9f9; + outline: 0; +} + +.button[disabled], button[disabled], +input[type=submit][disabled], +input[type=reset][disabled], +input[type=button][disabled], +input[type=file][disabled] { + cursor: default; + opacity: 0.5; +} +.button:focus-visible, button:focus-visible, +input[type=submit]:focus-visible, +input[type=reset]:focus-visible, +input[type=button]:focus-visible, +input[type=file]:focus-visible { + outline-style: solid; + outline-width: 2px; +} + +textarea, select, input { + color: #4a4a4a; + padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ + margin-bottom: 10px; + background-color: #f1f1f1; + border: 1px solid #f1f1f1; + border-radius: 4px; + box-shadow: none; + box-sizing: border-box; +} +textarea:focus, select:focus, input:focus { + border: 1px solid #1d7484; + outline: 0; +} + +input[type=checkbox]:focus { + outline: 1px dotted #1d7484; +} + +label, legend, fieldset { + display: block; + margin-bottom: 0.5rem; + font-weight: 600; +} diff --git a/zpool.php b/zpool.php new file mode 100644 index 0000000..51301d7 --- /dev/null +++ b/zpool.php @@ -0,0 +1,12 @@ + + + + + + + ZFS Pool + + +
+ +