mirror of
https://github.com/gmemstr/sliproad.git
synced 2024-09-20 00:21:15 +01:00
Add disk usage statistics to index & API.
Update README.md Add disk usage statistics to index & API.
This commit is contained in:
parent
a2a1e0e573
commit
88e0d6c7b1
|
@ -21,6 +21,8 @@ then navigate to `localhost:3000`
|
||||||
|
|
||||||
initially the heavy lifting was done by the server, but the need for a better frontend was clear.
|
initially the heavy lifting was done by the server, but the need for a better frontend was clear.
|
||||||
|
|
||||||
|
full documentation coming soon once actual functionality has been nailed down.
|
||||||
|
|
||||||
## credits
|
## credits
|
||||||
|
|
||||||
svg icons via https://iconsvg.xyz
|
svg icons via https://iconsvg.xyz
|
||||||
|
|
|
@ -3,14 +3,17 @@
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta content="utf-8" http-equiv="encoding">
|
<meta content="utf-8" http-equiv="encoding">
|
||||||
<title>Home | PiNas</title>
|
<title>Home | GoNAS</title>
|
||||||
<link rel="stylesheet" href="/assets/styles.css">
|
<link rel="stylesheet" href="/assets/styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<div>
|
<div>
|
||||||
<img src="/assets/svgs/pi.svg" alt="">
|
<h1 style="display: inline-block">GoNAS</h1>
|
||||||
<h1 style="display: inline-block">PiNas</h1>
|
<p>Hot Storage Usage (<span id="hotUsagePercent"></span>%)</p>
|
||||||
|
<progress max="100" id="hotUsage"></progress>
|
||||||
|
<p>Cold Storage Usage (<span id="coldUsagePercent"></span>%)</p>
|
||||||
|
<progress max="100" id="coldUsage"></progress>
|
||||||
</div>
|
</div>
|
||||||
<div class="content index">
|
<div class="content index">
|
||||||
<a href="/files/">
|
<a href="/files/">
|
||||||
|
@ -19,5 +22,31 @@
|
||||||
<img src="/assets/svgs/coldfiles.svg" alt="">Cold Files</a>
|
<img src="/assets/svgs/coldfiles.svg" alt="">Cold Files</a>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
<script>
|
||||||
|
const $ = selector => document.querySelector(selector);
|
||||||
|
|
||||||
|
const init = event => {
|
||||||
|
getUsages();
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUsages = () => {
|
||||||
|
fetch('/api/diskusage')
|
||||||
|
.then(function (response) {
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (jsonResult) {
|
||||||
|
console.log(jsonResult);
|
||||||
|
let hot = (jsonResult.HotStorage.Free) / (jsonResult.HotStorage.Total) * 100;
|
||||||
|
let cold = (jsonResult.ColdStorage.Free) / (jsonResult.ColdStorage.Total) * 100;
|
||||||
|
// Flip values to reflect % used rather than % free.
|
||||||
|
$("#hotUsage").value = 100 - hot;
|
||||||
|
$("#hotUsagePercent").innerText = Math.floor(100 - hot);
|
||||||
|
$("#coldUsage").value = 100 - cold;
|
||||||
|
$("#coldUsagePercent").innerText = Math.floor(100 - cold);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', init);
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -3,7 +3,7 @@
|
||||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta content="utf-8" http-equiv="encoding">
|
<meta content="utf-8" http-equiv="encoding">
|
||||||
<title>| PiNas</title>
|
<title>| GoNAS</title>
|
||||||
<link rel="stylesheet" href="/assets/styles.css">
|
<link rel="stylesheet" href="/assets/styles.css">
|
||||||
<script type="text/javascript" src="/assets/app.js"></script>
|
<script type="text/javascript" src="/assets/app.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
<a href="/" class="back-button">
|
<a href="/" class="back-button">
|
||||||
<img src="/assets/svgs/back.svg" alt="" data-dismiss-context="false">
|
<img src="/assets/svgs/back.svg" alt="" data-dismiss-context="false">
|
||||||
</a>
|
</a>
|
||||||
<h1 style="display: inline-block">PiNas</h1>
|
<h1 style="display: inline-block">GoNAS</h1>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div>
|
<div>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<div class="content" id="filelist">
|
<div class="content" id="filelist">
|
||||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||||
<input id="path" type="value" name="path">
|
<input id="path" type="value" name="path">
|
||||||
<input type="file" data-multiple-caption="{count} files selected" multiple name="file">
|
<input type="file" multiple name="file">
|
||||||
<input type="submit" value="Upload">
|
<input type="submit" value="Upload">
|
||||||
</form>
|
</form>
|
||||||
<p class="directory"><a href="#" id="previous-dir">..</a></p>
|
<p class="directory"><a href="#" id="previous-dir">..</a></p>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const $ = element => document.getElementById(element);
|
const $ = selector => document.querySelector(selector);
|
||||||
|
|
||||||
const init = event => {
|
const init = event => {
|
||||||
var path = window.location.pathname;
|
var path = window.location.pathname;
|
||||||
|
@ -12,8 +12,8 @@ const init = event => {
|
||||||
buildList(json);
|
buildList(json);
|
||||||
});
|
});
|
||||||
|
|
||||||
$("path-header").innerText = path;
|
$("#path-header").innerText = path;
|
||||||
|
$("#path").value = path;
|
||||||
document.querySelector("body").addEventListener('contextmenu', function (ev) {
|
document.querySelector("body").addEventListener('contextmenu', function (ev) {
|
||||||
if (ev.target.localName == "a") {
|
if (ev.target.localName == "a") {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
@ -28,13 +28,13 @@ const init = event => {
|
||||||
return false;
|
return false;
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
document.querySelector("body").addEventListener('click', function(ev) {
|
$("body").addEventListener('click', function (ev) {
|
||||||
let shouldDismiss = ev.target.dataset.dismissContext == undefined && ev.target.parentElement.classList.contains("context-actions") == false && ev.target.localName != 'a';
|
let shouldDismiss = ev.target.dataset.dismissContext == undefined && ev.target.parentElement.classList.contains("context-actions") == false && ev.target.localName != 'a';
|
||||||
|
|
||||||
if (ev.which == 1 && shouldDismiss) {
|
if (ev.which == 1 && shouldDismiss) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
var d = document.getElementById('context');
|
var d = $('#context');
|
||||||
d.classList.add("hidden");
|
d.classList.add("hidden");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -58,15 +58,14 @@ const buildList = data => {
|
||||||
if (data.Files[i].IsDirectory == true) {
|
if (data.Files[i].IsDirectory == true) {
|
||||||
fileItem.classList.add("directory");
|
fileItem.classList.add("directory");
|
||||||
fileLink.href = "/" + data.Prefix + "/" + data.Path + "/" + data.Files[i].Name;
|
fileLink.href = "/" + data.Prefix + "/" + data.Path + "/" + data.Files[i].Name;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
fileItem.classList.add("file");
|
fileItem.classList.add("file");
|
||||||
fileLink.href = "/" + data.SinglePrefix + "/" + data.Path + "/" + data.Files[i].Name;
|
fileLink.href = "/" + data.SinglePrefix + "/" + data.Path + "/" + data.Files[i].Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
fileLink.innerText = data.Files[i].Name;
|
fileLink.innerText = data.Files[i].Name;
|
||||||
fileItem.appendChild(fileLink);
|
fileItem.appendChild(fileLink);
|
||||||
document.getElementById("filelist").appendChild(fileItem);
|
$("#filelist").appendChild(fileItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,3 +97,7 @@ a.back-button {
|
||||||
border-bottom: 1px solid black;
|
border-bottom: 1px solid black;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progress[value] {
|
||||||
|
height: 20px;
|
||||||
|
}
|
|
@ -44,7 +44,7 @@ func Listing(tier string) common.Handler {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var config Config;
|
var config Config
|
||||||
err = json.Unmarshal(d, &config)
|
err = json.Unmarshal(d, &config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/gmemstr/nas/common"
|
"github.com/gmemstr/nas/common"
|
||||||
"github.com/gmemstr/nas/files"
|
"github.com/gmemstr/nas/files"
|
||||||
|
"github.com/gmemstr/nas/system"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -61,6 +62,9 @@ func Init() *mux.Router {
|
||||||
fileList(),
|
fileList(),
|
||||||
)).Methods("GET")
|
)).Methods("GET")
|
||||||
|
|
||||||
|
r.Handle("/api/diskusage", Handle(
|
||||||
|
system.DiskUsages(),
|
||||||
|
)).Methods("GET")
|
||||||
|
|
||||||
r.Handle("/api/files/", Handle(
|
r.Handle("/api/files/", Handle(
|
||||||
files.Listing("hot"),
|
files.Listing("hot"),
|
||||||
|
|
72
system/system.go
Normal file
72
system/system.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/gmemstr/nas/common"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
ColdStorage string
|
||||||
|
HotStorage string
|
||||||
|
}
|
||||||
|
|
||||||
|
type UsageStat struct {
|
||||||
|
Available int64
|
||||||
|
Free int64
|
||||||
|
Total int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type UsageStats struct {
|
||||||
|
ColdStorage UsageStat
|
||||||
|
HotStorage UsageStat
|
||||||
|
}
|
||||||
|
|
||||||
|
func DiskUsages() common.Handler {
|
||||||
|
|
||||||
|
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
|
||||||
|
var statHot syscall.Statfs_t
|
||||||
|
var statCold syscall.Statfs_t
|
||||||
|
|
||||||
|
d, err := ioutil.ReadFile("assets/config/config.json")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
err = json.Unmarshal(d, &config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to hot storage
|
||||||
|
storage := config.HotStorage
|
||||||
|
err = syscall.Statfs(storage, &statHot)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
hotStats := UsageStat{
|
||||||
|
Free: statHot.Bsize * int64(statHot.Bfree),
|
||||||
|
Total: statHot.Bsize * int64(statHot.Blocks),
|
||||||
|
}
|
||||||
|
storage = config.ColdStorage
|
||||||
|
err = syscall.Statfs(storage, &statCold)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
coldStats := UsageStat{
|
||||||
|
Free: statCold.Bsize * int64(statCold.Bfree),
|
||||||
|
Total: statCold.Bsize * int64(statCold.Blocks),
|
||||||
|
}
|
||||||
|
usages := UsageStats{
|
||||||
|
HotStorage: hotStats,
|
||||||
|
ColdStorage: coldStats,
|
||||||
|
}
|
||||||
|
// Available blocks * size per block = available space in bytes
|
||||||
|
resultJson, err := json.Marshal(usages)
|
||||||
|
w.Write(resultJson)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue