Initial commit

This commit is contained in:
Evert Prants 2019-03-01 21:44:08 +02:00
commit c22caed446
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
9 changed files with 833 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules/

363
package-lock.json generated Normal file
View File

@ -0,0 +1,363 @@
{
"name": "quickstatus",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
"integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "~1.6.3",
"iconv-lite": "0.4.23",
"on-finished": "~2.3.0",
"qs": "6.5.2",
"raw-body": "2.3.3",
"type-is": "~1.6.16"
}
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.16.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
"integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
"requires": {
"accepts": "~1.3.5",
"array-flatten": "1.1.1",
"body-parser": "1.18.3",
"content-disposition": "0.5.2",
"content-type": "~1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.1.1",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.4",
"qs": "6.5.2",
"range-parser": "~1.2.0",
"safe-buffer": "5.1.2",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"mime-db": {
"version": "1.38.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
"integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg=="
},
"mime-types": {
"version": "2.1.22",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
"integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
"requires": {
"mime-db": "~1.38.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"node-os-utils": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/node-os-utils/-/node-os-utils-1.0.7.tgz",
"integrity": "sha1-Q6+sBpSwTRRoIN8EkEWPzdBxBAM="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
"integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.8.0"
}
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"range-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"raw-body": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
"integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
"requires": {
"bytes": "3.0.0",
"http-errors": "1.6.3",
"iconv-lite": "0.4.23",
"unpipe": "1.0.0"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
}
},
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.2",
"send": "0.16.2"
}
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.18"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "quickstatus",
"version": "1.0.0",
"description": "Get system status in JSON format",
"main": "server.js",
"scripts": {
"start": "node server.js",
"output": "node system.js"
},
"private": true,
"license": "CC0",
"dependencies": {
"express": "^4.16.4",
"node-os-utils": "^1.0.7"
}
}

20
public/dark-theme.css Normal file
View File

@ -0,0 +1,20 @@
body {
background-color: #00060e;
}
.infobox {
color: #ddd;
}
.infobox .header {
background-color: #393939;
border: 1px solid #454545;
}
.infobox .content {
background-color: #242424;
border: 1px solid #454545;
}
.bar-inner {
background-color: #181818;
}
.bar-progress {
background-color: #00626e;
}

87
public/index.css Normal file
View File

@ -0,0 +1,87 @@
body {
margin: 0;
padding: 0;
font-family: sans-serif, sans;
background-color: #f0f0f0;
}
#app {
margin: 10px 120px;
}
h1,h2,h3,h4 {
margin: 0;
}
.infobox {
margin: 5px;
}
.infobox .header {
background-color: #f3f3f3;
padding: 15px;
/*border-radius: 10px 10px 0 0;*/
border: 1px solid #ddd;
border-bottom: 0;
}
.infobox .content {
padding: 10px;
background-color: #ececec;
border: 1px solid #ddd;
}
.infobox-row {
display: flex;
flex-direction: row;
}
.infobox-row .infobox {
flex-grow: 1;
}
.row {
margin-bottom: 5px;
}
.row label {
min-width: 50%;
display: inline-block;
}
.bar-inner {
height: 40px;
background-color: #ddd;
border-radius: 5px;
}
.bar-progress {
height: 40px;
background-color: #00c9e1;
border-radius: 5px;
}
.bar-stats {
display: flex;
flex-direction: row;
margin-top: 5px;
}
.bar-stats span {
flex-grow: 1;
color: #9c9c9c;
}
.bar-stats .stat-now {
text-align: center;
}
.bar-stats .stat-high {
text-align: right;
}
.ip-addr {
display: block;
}
.sec-info {
margin-top: 20px;
margin-bottom: 10px;
}
.disks .disk {
margin-bottom: 20px;
}
@media only screen and (max-width: 800px) {
#app {
margin: 15px;
}
.infobox-row {
display: block;
}
.infobox {
width: 100%;
}
}

16
public/index.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="index.css">
<link rel="stylesheet" type="text/css" href="dark-theme.css">
<title>Please Wait..</title>
</head>
<body>
<div id="app">
<h1>Gathering the latest information, please wait..</h1>
</div>
<script src="index.js"></script>
</body>
</html>

203
public/index.js Normal file
View File

@ -0,0 +1,203 @@
(function () {
let app = document.getElementById('app')
function httpGet (url) {
let request = new XMLHttpRequest()
return new Promise(function (resolve, reject) {
request.onreadystatechange = function () {
if (this.readyState === 4) {
if (this.status === 200) {
resolve(this.responseText)
} else if (this.response == null && this.status === 0) {
reject(new Error('The status server appears to be offile.'))
}
}
}
request.open("GET", url, true)
request.send(null)
})
}
function bytesToSize (bytes) {
let sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
if (bytes == 0) return '0 Byte'
let i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)))
return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]
}
// Add a zero in front of single-digit numbers
function zf (v) {
if (v > 9) {
return '' + v
} else {
return '0' + v
}
}
// Convert seconds into years days hours minutes seconds(.milliseconds)
function readableTime (timems, ignoreMs) {
var time = timems | 0
var ms = ignoreMs ? '' : '.' + zf((timems * 100) % 100 | 0)
if (time < 60) return zf(time) + ms + 's'
else if (time < 3600) return zf(time / 60 | 0) + 'm ' + zf(time % 60) + ms + 's'
else if (time < 86400) return zf(time / 3600 | 0) + 'h ' + zf((time % 3600) / 60 | 0) + 'm ' + zf((time % 3600) % 60) + ms + 's'
else if (time < 31536000) return (time / 86400 | 0) + 'd ' + zf((time % 86400) / 3600 | 0) + 'h ' + zf((time % 3600) / 60 | 0) + 'm ' + zf((time % 3600) % 60) + 's'
else return (time / 31536000 | 0) + 'y ' + zf((time % 31536000) / 86400 | 0) + 'd ' + zf((time % 86400) / 3600 | 0) + 'h ' + zf((time % 3600) / 60 | 0) + 'm ' + zf((time % 3600) % 60) + 's'
}
function e (element, content, cl) {
let el = document.createElement(element)
if (cl) el.className = cl
el.innerHTML = content
return el.outerHTML
}
function box (type, content) {
return e('div', content, type)
}
function bar (metric, value, percentage, low, high) {
return box('bar-outer',
e('span', metric, 'bar-name') +
box('bar-inner',
'<div class="bar-progress" style="width: ' + percentage + '%;"></div>'
) +
box('bar-stats',
e('span', low, 'stat-low') +
e('span', value, 'stat-now') +
e('span', high, 'stat-high')
)
)
}
function renderPage (data) {
let title = 'Welcome to ' + data.hostname + ' on ' + data.kernel
document.title = title
app.innerHTML = ''
app.innerHTML += box('infobox-row',
box('general infobox',
box('header',
e('h2', 'General')
) +
box('content',
box('row',
e('label', 'Hostname') +
e('span', data.hostname)
) + box('row',
e('label', 'Uptime') +
e('span', readableTime(data.uptime))
) + box('row',
e('label', 'Kernel version') +
e('span', data.kernel)
) + box('row',
e('label', 'Processes') +
e('span', data.cpu.processes)
)
)
) +
// CPU usage
box('cpu infobox',
box('header',
e('h2', 'CPU')
) +
box('content',
box('row',
e('label', 'Num. cores') +
e('span', data.cpu.count)
) +
box('row',
e('label', 'Usage') +
e('span', Math.floor(data.cpu.usage) + ' %')
) + box('row',
e('label', 'Load averages') +
e('span', data.cpu.loadAverage[0].toFixed(2) + ', ' + data.cpu.loadAverage[1].toFixed(2) + ', ' + data.cpu.loadAverage[2].toFixed(2))
)
)
))
// Memory usage
let memuse = (data.memory.total - data.memory.free)
app.innerHTML += box('memory infobox',
box('header',
e('h2', 'Memory')
) +
box('content',
bar('Memory usage', bytesToSize(memuse), memuse * 100 / data.memory.total, '0 Bytes', bytesToSize(data.memory.total))
)
)
// Network interfaces
let interfaces = ''
for (let int in data.network) {
if (int === 'lo') continue
let iface = data.network[int]
let ips = []
for (let ip in iface.addresses) {
ips.push(e('span', iface.addresses[ip], 'ip-addr'))
}
interfaces += box('interface',
e('h3', int) +
ips.join('') +
e('h4', 'Bandwidth', 'sec-info') +
box('row',
e('label', 'Download') +
e('span', bytesToSize(iface.download))
) + box('row',
e('label', 'Upload') +
e('span', bytesToSize(iface.upload))
)
)
}
// Disks
let disks = ''
for (let mount in data.disk) {
let disk = data.disk[mount]
disks += box('disk',
e('h3', 'Mounted on ' + mount) +
box('row',
e('label', 'Free space') +
e('span', disk.freeGb + ' GB')
) +
bar('Disk usage', disk.usedGb + ' GB', disk.usedPercentage, '0 GB', disk.totalGb + ' GB')
)
}
app.innerHTML += box('infobox-row',
box('network infobox',
box('header',
e('h2', 'Network Interfaces')
) +
box('content',
box('interfaces', interfaces)
)
)+
box('disk infobox',
box('header',
e('h2', 'Disk Drives')
) +
box('content',
box('disks', disks)
)
)
)
}
function update () {
httpGet('/status').then(function (data) {
try {
renderPage(JSON.parse(data))
} catch (e) {
console.error(e)
}
}, function (e) {
console.error(e)
})
}
update()
})()

41
server.js Normal file
View File

@ -0,0 +1,41 @@
const express = require('express')
const cprog = require('child_process')
const path = require('path')
const app = express()
const port = process.env.PORT || 8432
/* Information segment */
let info = {}
let infoTimer
async function requestInfo () {
return new Promise((resolve, reject) => {
let proc = cprog.exec('node ' + path.join(__dirname, 'system.js'), (err, stdout) => {
if (err) return reject(err)
resolve(JSON.parse(stdout))
})
})
}
function update () {
requestInfo().then((newInfo) => {
info = newInfo
}).catch(e => console.error(e))
}
infoTimer = setInterval(update, 8000)
update()
/* Web server segment */
app.use('/status', function (req, res) {
res.jsonp(info)
})
app.use('/', express.static(path.join(__dirname, 'public')))
app.listen(port, function (argument) {
console.log('Listening on', port)
})

86
system.js Normal file
View File

@ -0,0 +1,86 @@
const fs = require('fs')
const util = require('util')
const os = require('os')
const osu = require('node-os-utils')
let disks = ['/']
// Get disks from environment variable
let denv = process.env.SHOW_DISKS
if (denv) {
try {
disks = JSON.parse(denv)
} catch (e) {
console.warn(e)
}
}
async function diskInfo () {
let result = {}
for (let i in disks) {
let info = await osu.drive.info(disks[i])
result[disks[i]] = info
}
return result
}
async function networkInfo () {
let interfaces = {}
let all = os.networkInterfaces()
for (let iface in all) {
let ips = all[iface]
interfaces[iface] = {
addresses: [],
upload: null,
download: null,
}
for (let i in ips) {
interfaces[iface].addresses.push(ips[i].address)
}
}
// Interface usage statistics
let data = await osu.netstat.stats()
for (let p in data) {
let ifa = data[p]
let dt = interfaces[ifa.interface]
if (!dt) continue
dt.upload = ifa.outputBytes
dt.download = ifa.inputBytes
}
return interfaces
}
async function cpuInfo () {
return {
usage: await osu.cpu.usage(),
loadAverage: os.loadavg(),
count: os.cpus().length,
processes: await osu.proc.totalProcesses()
}
}
async function memInfo () {
let free = os.freemem()
let total = os.totalmem()
return {
free: free,
total: total,
usage: (total - free) / total * 100
}
}
async function all () {
return {
hostname: os.hostname(),
kernel: os.release(),
uptime: os.uptime(),
cpu: await cpuInfo(),
memory: await memInfo(),
network: await networkInfo(),
disk: await diskInfo()
}
}
all().then((a) => console.log(JSON.stringify(a)))