├── .gitignore ├── LICENSE.md ├── README.md ├── app.js ├── monitor.js ├── monitors ├── cpu.js ├── memory.js ├── network.js ├── processes.js ├── temps.js └── uptime.js ├── package-lock.json ├── package.json └── public ├── css ├── darkmode.css ├── light.css └── style.css ├── index.html └── js ├── lib ├── Chart.min.js └── moment.min.js ├── script.js ├── theme.js └── widgets ├── basechartwidget.js ├── basepiewidget.js ├── basewidget.js ├── cpuchart.js ├── cpuusage.js ├── freemem.js ├── memory.js ├── network.js ├── processcpu.js ├── processmemory.js ├── temps.js └── uptime.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | up.sh -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2015 Kevin Bedi 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mintr 2 | 3 | ![Screenshot](http://i.imgur.com/7gYEhQ3.png) 4 | 5 | Mintr is a simple unix monitoring tool. It intentially avoids authentication, and thus does not monitor or display any private information, nor does it allow any actions to be taken from the web interface. 6 | 7 | It, quite simply, shows a few graphs that should help indicate the current status of your server, as well as the status over the past hour. 8 | 9 | Currently it shows 10 | 11 | * Memory usage 12 | * CPU Usage 13 | * Network activity 14 | * Process memory usage & CPU usage 15 | * Temperature Sensors 16 | 17 | # Installation & Usage 18 | 19 | ``` 20 | npm install -g mintr 21 | mintr 22 | ``` 23 | 24 | If you do not have nodejs/npm, you'll need to install that first. See https://nodejs.org/ for instructions on this. 25 | 26 | `mintr` takes an optional parameter which is the port it runs on, so you can run `mintr 1337` if you want to run it on port `1337`. The default port is `3000`. 27 | You can run `mintr` in a screen/tmux, add it to your system's startup, or anything else you please. 28 | Then go to `http://your-server.com:3000`, or replace `3000` with the correct port. 29 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var express = require('express'); 4 | var app = express(); 5 | 6 | var server = require('http').Server(app); 7 | var io = require('socket.io')(server); 8 | 9 | var monitor = require('./monitor'); 10 | monitor.on('data', function(data) { 11 | io.emit('data', data); 12 | }); 13 | 14 | app.use(express.static(__dirname + '/public')); 15 | 16 | var port = parseInt(process.argv[2]) || 3000; 17 | server.listen(port); 18 | console.log("Mintr is listening on port " + port); 19 | 20 | var sockets = {}; 21 | 22 | io.on('connection', function (socket) { 23 | sockets[socket.id] = socket; 24 | 25 | var history = monitor.getHistory(); 26 | socket.emit('history', history); 27 | 28 | socket.on('disconnect', function() { 29 | delete sockets[socket.id]; 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /monitor.js: -------------------------------------------------------------------------------- 1 | // Dependencies 2 | var os = require('os'); 3 | var async = require('async'); 4 | 5 | // Constants 6 | var EMPTY_CALLBACK = function (callback) { 7 | callback(); 8 | }; 9 | 10 | // Monitors 11 | var monitors = { 12 | memory: require('./monitors/memory'), 13 | uptime: require('./monitors/uptime'), 14 | network: require('./monitors/network'), 15 | processes: require('./monitors/processes'), 16 | cpu: require('./monitors/cpu'), 17 | temps: require('./monitors/temps'), 18 | }; 19 | 20 | // XXX: Hacky way to prevent us from having to call the callback 21 | // with (null, result) from within the monitors 22 | for (var key in monitors) { 23 | var monitor = monitors[key]; 24 | 25 | (function (monitor) { 26 | monitor._asyncMonitor = function (history, callback) { 27 | monitor.monitor(history, function (result) { 28 | callback(null, result); 29 | }); 30 | }; 31 | })(monitor); 32 | } 33 | 34 | var monitorArray = []; 35 | for (var key in monitors) { 36 | var monitor = monitors[key]; 37 | 38 | monitorArray.push({ 39 | monitor: monitor, 40 | key: key, 41 | }); 42 | } 43 | 44 | monitors = monitorArray; 45 | 46 | // Event stuff 47 | var _eventHandlers = {}; 48 | var _trigger = function (eventName, data) { 49 | if (!_eventHandlers[eventName]) { 50 | return; 51 | } 52 | 53 | _eventHandlers[eventName].forEach(function (callback) { 54 | callback && callback(data); 55 | }); 56 | }; 57 | 58 | // Main 59 | var Monitor = function () { 60 | this._history = { 61 | memory: [], 62 | network: [], 63 | processes: [], 64 | temps: [], 65 | }; 66 | 67 | monitors.forEach(function (monitor) { 68 | if (monitor.monitor.history) { 69 | this._history[monitor.key] = []; 70 | } 71 | }, this); 72 | 73 | this._numData = 0; 74 | 75 | this._monitor(); 76 | }; 77 | 78 | Monitor.prototype.on = function (eventName, callback) { 79 | _eventHandlers[eventName] = _eventHandlers[eventName] || []; 80 | _eventHandlers[eventName].push(callback); 81 | }; 82 | 83 | Monitor.prototype._getData = function (callback) { 84 | var numData = this._numData++; 85 | 86 | var data = {}; 87 | 88 | var monitorAsyncFuncs = monitors.map(function (monitor) { 89 | if (numData % monitor.monitor.frequency === 0) { 90 | return monitor.monitor._asyncMonitor.bind( 91 | null, 92 | this._history[monitor.key] 93 | ); 94 | } else { 95 | return EMPTY_CALLBACK; 96 | } 97 | }, this); 98 | 99 | async.parallel(monitorAsyncFuncs, function (error, results) { 100 | results.forEach(function (result, index) { 101 | if (result === undefined || result === null) { 102 | return; 103 | } 104 | 105 | var monitor = monitors[index]; 106 | var key = monitors[index].key; 107 | monitor = monitor.monitor; 108 | 109 | data[key] = result; 110 | data[key].timestamp = Date.now(); 111 | 112 | if (monitor.history) { 113 | this._history[key].push(result); 114 | } else { 115 | this._history[key] = result; 116 | } 117 | 118 | var maxNumHistoryElements = (monitor.maxHistory / monitor.frequency); 119 | if (this._history[key].length > maxNumHistoryElements) { 120 | this._history[key].splice(0, 1); 121 | } 122 | }, this); 123 | callback(data); 124 | }.bind(this)); 125 | }; 126 | 127 | Monitor.prototype.getHistory = function () { 128 | return this._history; 129 | }; 130 | 131 | Monitor.prototype._monitor = function () { 132 | this._getData(function (data) { 133 | _trigger('data', data); 134 | }); 135 | 136 | setTimeout(this._monitor.bind(this), 1000); 137 | }; 138 | 139 | module.exports = new Monitor(); 140 | -------------------------------------------------------------------------------- /monitors/cpu.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var os = require('os'); 3 | 4 | var CPU = {}; 5 | 6 | CPU.monitor = function(history, callback) { 7 | exec('ps aux', function(error, result) { 8 | var cpu = 0; 9 | 10 | var lines = result.split('\n'); 11 | lines.splice(0, 1); 12 | 13 | for (var x = 0; x < lines.length; x++) { 14 | if (!lines[x].trim()) { 15 | continue; 16 | } 17 | cpu += parseFloat(lines[x].split(/\s+/)[2]); 18 | } 19 | 20 | // Also record a normalized CPU usage per thread 21 | var normalized = cpu / os.cpus().length; 22 | 23 | callback({ 24 | cpu: cpu, 25 | normalized: normalized, 26 | timestamp: Date.now(), 27 | }); 28 | }); 29 | }; 30 | 31 | CPU.history = true; 32 | CPU.maxHistory = 3600; 33 | CPU.frequency = 20; 34 | 35 | module.exports = CPU; 36 | -------------------------------------------------------------------------------- /monitors/memory.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | 3 | var Memory = {}; 4 | 5 | Memory.monitor = function(history, callback) { 6 | callback({ 7 | total: os.totalmem(), 8 | free: os.freemem(), 9 | }); 10 | }; 11 | 12 | Memory.history = true; 13 | Memory.maxHistory = 3600; 14 | Memory.frequency = 20; 15 | 16 | module.exports = Memory; 17 | -------------------------------------------------------------------------------- /monitors/network.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | var async = require('async'); 3 | var os = require('os'); 4 | 5 | var Memory = {}; 6 | 7 | Memory.monitor = function(history, callback) { 8 | exec('ip -s link', function(error, result) { 9 | if (error) { 10 | console.log( 11 | 'Mintr uses `ip` to get network information, which your ' + 12 | 'OS does not support.' 13 | ); 14 | console.log(error); 15 | callback({}); 16 | return; 17 | } 18 | 19 | var data = { 20 | in: 0, 21 | out: 0, 22 | }; 23 | 24 | var rx = result.match(/\d+:\s+\w+:\s+[\S\s]*?RX:\s+[\S\s]*?(\d+)/g); 25 | var tx = result.match(/\d+:\s+\w+:\s+[\S\s]*?TX:\s+[\S\s]*?(\d+)/g); 26 | 27 | for (var x = 0; x < rx.length; x++) { 28 | var localAmt = rx[x]; 29 | if (localAmt.indexOf('lo:') !== -1) { 30 | continue; 31 | } 32 | 33 | data.in += parseInt(localAmt.substring(localAmt.lastIndexOf('\n') + 1)); 34 | } 35 | 36 | for (var x = 0; x < rx.length; x++) { 37 | var localAmt = tx[x]; 38 | if (localAmt.indexOf('lo:') !== -1) { 39 | continue; 40 | } 41 | 42 | data.out += parseInt(localAmt.substring(localAmt.lastIndexOf('\n') + 1)); 43 | } 44 | 45 | data.timestamp = Date.now(); 46 | 47 | // Now calculate speeds 48 | if (history.length > 0) { 49 | var amountBack = 1; 50 | var index = history.length - amountBack; 51 | var lastTimestamp = history[index].timestamp; 52 | var lastOut = history[index].out; 53 | var lastIn = history[index].in; 54 | 55 | var elapsedTime = data.timestamp - lastTimestamp; 56 | var deltaOut = data.out - lastOut; 57 | var deltaIn = data.in - lastIn; 58 | 59 | data.outSpeed = deltaOut / elapsedTime * 1000; 60 | data.inSpeed = deltaIn / elapsedTime * 1000; 61 | } else { 62 | data.outSpeed = 0; 63 | data.inSpeed = 0; 64 | } 65 | 66 | callback(data); 67 | }); 68 | }; 69 | 70 | Memory.history = true; 71 | Memory.maxHistory = 3600; 72 | Memory.frequency = 20; 73 | 74 | module.exports = Memory; 75 | -------------------------------------------------------------------------------- /monitors/processes.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').exec; 2 | 3 | var MAX_NAME_LENGTH = 40; 4 | 5 | var Processes = {}; 6 | 7 | Processes.monitor = function(history, callback) { 8 | exec('ps aux', function(error, result) { 9 | var processes = []; 10 | 11 | var lines = result.split('\n'); 12 | lines.splice(0, 1); 13 | 14 | for (var x = 0; x < lines.length; x++) { 15 | var line = lines[x]; 16 | if (!line.trim()) { 17 | continue; 18 | } 19 | var process = {}; 20 | var words = line.split(/\s+/); 21 | process.user = words[0]; 22 | process.pid = parseInt(words[1]); 23 | process.cpu = parseFloat(words[2]); 24 | process.mem = parseFloat(words[3]); 25 | var name = words[10]; 26 | for (var y = 11; y < words.length; y++) { 27 | name += ' ' + words[y]; 28 | } 29 | process.name = name && name.substring(0, MAX_NAME_LENGTH); 30 | 31 | processes.push(process); 32 | } 33 | 34 | var topCPU = processes.sort(function(a, b) { 35 | return b.cpu - a.cpu; 36 | }).slice(0, 5); 37 | var topMem = processes.sort(function(a, b) { 38 | return b.mem - a.mem; 39 | }).slice(0, 5); 40 | 41 | var topProcesses = []; 42 | var topPIDs = {}; 43 | for (var x = 0; x < topCPU.length; x++) { 44 | topPIDs[topCPU[x].pid] = true; 45 | topProcesses.push(topCPU[x]); 46 | if (!topPIDs[topMem[x].pid]) { 47 | topPIDs[topMem[x].pid] = true; 48 | topProcesses.push(topMem[x]); 49 | } 50 | } 51 | 52 | callback(topProcesses); 53 | }); 54 | }; 55 | 56 | Processes.history = false; 57 | Processes.frequency = 20; 58 | 59 | module.exports = Processes; 60 | -------------------------------------------------------------------------------- /monitors/temps.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | var Temps = {}; 4 | 5 | Temps.monitor = function (history, callback) { 6 | if (!fs.existsSync('/sys/class/thermal')) { 7 | callback({}); 8 | return; 9 | } 10 | 11 | const tempSensorDirs = fs.readdirSync('/sys/class/thermal') 12 | .filter(x => x.startsWith('thermal')); 13 | 14 | const temps = tempSensorDirs.map(dir => { 15 | const dirType = fs.readFileSync('/sys/class/thermal/' + dir + '/type', { encoding: 'utf8' }); 16 | const dirTemp = fs.readFileSync('/sys/class/thermal/' + dir + '/temp', { encoding: 'utf8' }); 17 | 18 | return { type: dirType.trim(), temp: parseInt(dirTemp.trim()) }; 19 | }); 20 | 21 | callback(temps); 22 | }; 23 | 24 | Temps.history = false; 25 | Temps.frequency = 20; 26 | 27 | module.exports = Temps; 28 | -------------------------------------------------------------------------------- /monitors/uptime.js: -------------------------------------------------------------------------------- 1 | var os = require('os'); 2 | var exec = require('child_process').exec; 3 | 4 | var Uptime = {}; 5 | 6 | Uptime.monitor = function(history, callback) { 7 | exec('ps -eo lstart,args | grep /sbin/init', function(error, result) { 8 | var processTextIndex = result.indexOf('/sbin/init'); 9 | var dateText = result.substring(0, processTextIndex); 10 | var uptime = Date.now() - new Date(dateText).getTime(); 11 | 12 | callback(uptime); 13 | }); 14 | }; 15 | 16 | Uptime.history = false; 17 | Uptime.frequency = 1; 18 | 19 | module.exports = Uptime; 20 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mintr", 3 | "version": "0.0.12", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "mintr", 9 | "version": "0.0.12", 10 | "license": "MIT", 11 | "dependencies": { 12 | "async": "^0.9.0", 13 | "express": "^4.19.2", 14 | "socket.io": "^4.6.2" 15 | }, 16 | "bin": { 17 | "mintr": "app.js" 18 | } 19 | }, 20 | "node_modules/@socket.io/component-emitter": { 21 | "version": "3.1.0", 22 | "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", 23 | "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" 24 | }, 25 | "node_modules/@types/cookie": { 26 | "version": "0.4.1", 27 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", 28 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" 29 | }, 30 | "node_modules/@types/cors": { 31 | "version": "2.8.17", 32 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", 33 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 34 | "dependencies": { 35 | "@types/node": "*" 36 | } 37 | }, 38 | "node_modules/@types/node": { 39 | "version": "20.14.11", 40 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", 41 | "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", 42 | "dependencies": { 43 | "undici-types": "~5.26.4" 44 | } 45 | }, 46 | "node_modules/accepts": { 47 | "version": "1.3.8", 48 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 49 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 50 | "dependencies": { 51 | "mime-types": "~2.1.34", 52 | "negotiator": "0.6.3" 53 | }, 54 | "engines": { 55 | "node": ">= 0.6" 56 | } 57 | }, 58 | "node_modules/array-flatten": { 59 | "version": "1.1.1", 60 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 61 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 62 | }, 63 | "node_modules/async": { 64 | "version": "0.9.2", 65 | "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", 66 | "integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==" 67 | }, 68 | "node_modules/base64id": { 69 | "version": "2.0.0", 70 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 71 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", 72 | "engines": { 73 | "node": "^4.5.0 || >= 5.9" 74 | } 75 | }, 76 | "node_modules/body-parser": { 77 | "version": "1.20.2", 78 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 79 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 80 | "dependencies": { 81 | "bytes": "3.1.2", 82 | "content-type": "~1.0.5", 83 | "debug": "2.6.9", 84 | "depd": "2.0.0", 85 | "destroy": "1.2.0", 86 | "http-errors": "2.0.0", 87 | "iconv-lite": "0.4.24", 88 | "on-finished": "2.4.1", 89 | "qs": "6.11.0", 90 | "raw-body": "2.5.2", 91 | "type-is": "~1.6.18", 92 | "unpipe": "1.0.0" 93 | }, 94 | "engines": { 95 | "node": ">= 0.8", 96 | "npm": "1.2.8000 || >= 1.4.16" 97 | } 98 | }, 99 | "node_modules/bytes": { 100 | "version": "3.1.2", 101 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 102 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 103 | "engines": { 104 | "node": ">= 0.8" 105 | } 106 | }, 107 | "node_modules/call-bind": { 108 | "version": "1.0.7", 109 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 110 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 111 | "dependencies": { 112 | "es-define-property": "^1.0.0", 113 | "es-errors": "^1.3.0", 114 | "function-bind": "^1.1.2", 115 | "get-intrinsic": "^1.2.4", 116 | "set-function-length": "^1.2.1" 117 | }, 118 | "engines": { 119 | "node": ">= 0.4" 120 | }, 121 | "funding": { 122 | "url": "https://github.com/sponsors/ljharb" 123 | } 124 | }, 125 | "node_modules/content-disposition": { 126 | "version": "0.5.4", 127 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 128 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 129 | "dependencies": { 130 | "safe-buffer": "5.2.1" 131 | }, 132 | "engines": { 133 | "node": ">= 0.6" 134 | } 135 | }, 136 | "node_modules/content-type": { 137 | "version": "1.0.5", 138 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 139 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 140 | "engines": { 141 | "node": ">= 0.6" 142 | } 143 | }, 144 | "node_modules/cookie": { 145 | "version": "0.6.0", 146 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", 147 | "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", 148 | "engines": { 149 | "node": ">= 0.6" 150 | } 151 | }, 152 | "node_modules/cookie-signature": { 153 | "version": "1.0.6", 154 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 155 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 156 | }, 157 | "node_modules/cors": { 158 | "version": "2.8.5", 159 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 160 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 161 | "dependencies": { 162 | "object-assign": "^4", 163 | "vary": "^1" 164 | }, 165 | "engines": { 166 | "node": ">= 0.10" 167 | } 168 | }, 169 | "node_modules/debug": { 170 | "version": "2.6.9", 171 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 172 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 173 | "dependencies": { 174 | "ms": "2.0.0" 175 | } 176 | }, 177 | "node_modules/define-data-property": { 178 | "version": "1.1.4", 179 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 180 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 181 | "dependencies": { 182 | "es-define-property": "^1.0.0", 183 | "es-errors": "^1.3.0", 184 | "gopd": "^1.0.1" 185 | }, 186 | "engines": { 187 | "node": ">= 0.4" 188 | }, 189 | "funding": { 190 | "url": "https://github.com/sponsors/ljharb" 191 | } 192 | }, 193 | "node_modules/depd": { 194 | "version": "2.0.0", 195 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 196 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 197 | "engines": { 198 | "node": ">= 0.8" 199 | } 200 | }, 201 | "node_modules/destroy": { 202 | "version": "1.2.0", 203 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 204 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 205 | "engines": { 206 | "node": ">= 0.8", 207 | "npm": "1.2.8000 || >= 1.4.16" 208 | } 209 | }, 210 | "node_modules/ee-first": { 211 | "version": "1.1.1", 212 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 213 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 214 | }, 215 | "node_modules/encodeurl": { 216 | "version": "1.0.2", 217 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 218 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 219 | "engines": { 220 | "node": ">= 0.8" 221 | } 222 | }, 223 | "node_modules/engine.io": { 224 | "version": "6.5.5", 225 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", 226 | "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", 227 | "dependencies": { 228 | "@types/cookie": "^0.4.1", 229 | "@types/cors": "^2.8.12", 230 | "@types/node": ">=10.0.0", 231 | "accepts": "~1.3.4", 232 | "base64id": "2.0.0", 233 | "cookie": "~0.4.1", 234 | "cors": "~2.8.5", 235 | "debug": "~4.3.1", 236 | "engine.io-parser": "~5.2.1", 237 | "ws": "~8.17.1" 238 | }, 239 | "engines": { 240 | "node": ">=10.2.0" 241 | } 242 | }, 243 | "node_modules/engine.io-parser": { 244 | "version": "5.2.3", 245 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", 246 | "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", 247 | "engines": { 248 | "node": ">=10.0.0" 249 | } 250 | }, 251 | "node_modules/engine.io/node_modules/cookie": { 252 | "version": "0.4.2", 253 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 254 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 255 | "engines": { 256 | "node": ">= 0.6" 257 | } 258 | }, 259 | "node_modules/engine.io/node_modules/debug": { 260 | "version": "4.3.5", 261 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 262 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 263 | "dependencies": { 264 | "ms": "2.1.2" 265 | }, 266 | "engines": { 267 | "node": ">=6.0" 268 | }, 269 | "peerDependenciesMeta": { 270 | "supports-color": { 271 | "optional": true 272 | } 273 | } 274 | }, 275 | "node_modules/engine.io/node_modules/ms": { 276 | "version": "2.1.2", 277 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 278 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 279 | }, 280 | "node_modules/es-define-property": { 281 | "version": "1.0.0", 282 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 283 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 284 | "dependencies": { 285 | "get-intrinsic": "^1.2.4" 286 | }, 287 | "engines": { 288 | "node": ">= 0.4" 289 | } 290 | }, 291 | "node_modules/es-errors": { 292 | "version": "1.3.0", 293 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 294 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 295 | "engines": { 296 | "node": ">= 0.4" 297 | } 298 | }, 299 | "node_modules/escape-html": { 300 | "version": "1.0.3", 301 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 302 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 303 | }, 304 | "node_modules/etag": { 305 | "version": "1.8.1", 306 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 307 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 308 | "engines": { 309 | "node": ">= 0.6" 310 | } 311 | }, 312 | "node_modules/express": { 313 | "version": "4.19.2", 314 | "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", 315 | "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", 316 | "dependencies": { 317 | "accepts": "~1.3.8", 318 | "array-flatten": "1.1.1", 319 | "body-parser": "1.20.2", 320 | "content-disposition": "0.5.4", 321 | "content-type": "~1.0.4", 322 | "cookie": "0.6.0", 323 | "cookie-signature": "1.0.6", 324 | "debug": "2.6.9", 325 | "depd": "2.0.0", 326 | "encodeurl": "~1.0.2", 327 | "escape-html": "~1.0.3", 328 | "etag": "~1.8.1", 329 | "finalhandler": "1.2.0", 330 | "fresh": "0.5.2", 331 | "http-errors": "2.0.0", 332 | "merge-descriptors": "1.0.1", 333 | "methods": "~1.1.2", 334 | "on-finished": "2.4.1", 335 | "parseurl": "~1.3.3", 336 | "path-to-regexp": "0.1.7", 337 | "proxy-addr": "~2.0.7", 338 | "qs": "6.11.0", 339 | "range-parser": "~1.2.1", 340 | "safe-buffer": "5.2.1", 341 | "send": "0.18.0", 342 | "serve-static": "1.15.0", 343 | "setprototypeof": "1.2.0", 344 | "statuses": "2.0.1", 345 | "type-is": "~1.6.18", 346 | "utils-merge": "1.0.1", 347 | "vary": "~1.1.2" 348 | }, 349 | "engines": { 350 | "node": ">= 0.10.0" 351 | } 352 | }, 353 | "node_modules/finalhandler": { 354 | "version": "1.2.0", 355 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 356 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 357 | "dependencies": { 358 | "debug": "2.6.9", 359 | "encodeurl": "~1.0.2", 360 | "escape-html": "~1.0.3", 361 | "on-finished": "2.4.1", 362 | "parseurl": "~1.3.3", 363 | "statuses": "2.0.1", 364 | "unpipe": "~1.0.0" 365 | }, 366 | "engines": { 367 | "node": ">= 0.8" 368 | } 369 | }, 370 | "node_modules/forwarded": { 371 | "version": "0.2.0", 372 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 373 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 374 | "engines": { 375 | "node": ">= 0.6" 376 | } 377 | }, 378 | "node_modules/fresh": { 379 | "version": "0.5.2", 380 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 381 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 382 | "engines": { 383 | "node": ">= 0.6" 384 | } 385 | }, 386 | "node_modules/function-bind": { 387 | "version": "1.1.2", 388 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 389 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 390 | "funding": { 391 | "url": "https://github.com/sponsors/ljharb" 392 | } 393 | }, 394 | "node_modules/get-intrinsic": { 395 | "version": "1.2.4", 396 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 397 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 398 | "dependencies": { 399 | "es-errors": "^1.3.0", 400 | "function-bind": "^1.1.2", 401 | "has-proto": "^1.0.1", 402 | "has-symbols": "^1.0.3", 403 | "hasown": "^2.0.0" 404 | }, 405 | "engines": { 406 | "node": ">= 0.4" 407 | }, 408 | "funding": { 409 | "url": "https://github.com/sponsors/ljharb" 410 | } 411 | }, 412 | "node_modules/gopd": { 413 | "version": "1.0.1", 414 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 415 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 416 | "dependencies": { 417 | "get-intrinsic": "^1.1.3" 418 | }, 419 | "funding": { 420 | "url": "https://github.com/sponsors/ljharb" 421 | } 422 | }, 423 | "node_modules/has-property-descriptors": { 424 | "version": "1.0.2", 425 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 426 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 427 | "dependencies": { 428 | "es-define-property": "^1.0.0" 429 | }, 430 | "funding": { 431 | "url": "https://github.com/sponsors/ljharb" 432 | } 433 | }, 434 | "node_modules/has-proto": { 435 | "version": "1.0.3", 436 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 437 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 438 | "engines": { 439 | "node": ">= 0.4" 440 | }, 441 | "funding": { 442 | "url": "https://github.com/sponsors/ljharb" 443 | } 444 | }, 445 | "node_modules/has-symbols": { 446 | "version": "1.0.3", 447 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 448 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 449 | "engines": { 450 | "node": ">= 0.4" 451 | }, 452 | "funding": { 453 | "url": "https://github.com/sponsors/ljharb" 454 | } 455 | }, 456 | "node_modules/hasown": { 457 | "version": "2.0.2", 458 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 459 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 460 | "dependencies": { 461 | "function-bind": "^1.1.2" 462 | }, 463 | "engines": { 464 | "node": ">= 0.4" 465 | } 466 | }, 467 | "node_modules/http-errors": { 468 | "version": "2.0.0", 469 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 470 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 471 | "dependencies": { 472 | "depd": "2.0.0", 473 | "inherits": "2.0.4", 474 | "setprototypeof": "1.2.0", 475 | "statuses": "2.0.1", 476 | "toidentifier": "1.0.1" 477 | }, 478 | "engines": { 479 | "node": ">= 0.8" 480 | } 481 | }, 482 | "node_modules/iconv-lite": { 483 | "version": "0.4.24", 484 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 485 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 486 | "dependencies": { 487 | "safer-buffer": ">= 2.1.2 < 3" 488 | }, 489 | "engines": { 490 | "node": ">=0.10.0" 491 | } 492 | }, 493 | "node_modules/inherits": { 494 | "version": "2.0.4", 495 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 496 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 497 | }, 498 | "node_modules/ipaddr.js": { 499 | "version": "1.9.1", 500 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 501 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 502 | "engines": { 503 | "node": ">= 0.10" 504 | } 505 | }, 506 | "node_modules/media-typer": { 507 | "version": "0.3.0", 508 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 509 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 510 | "engines": { 511 | "node": ">= 0.6" 512 | } 513 | }, 514 | "node_modules/merge-descriptors": { 515 | "version": "1.0.1", 516 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 517 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 518 | }, 519 | "node_modules/methods": { 520 | "version": "1.1.2", 521 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 522 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 523 | "engines": { 524 | "node": ">= 0.6" 525 | } 526 | }, 527 | "node_modules/mime": { 528 | "version": "1.6.0", 529 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 530 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 531 | "bin": { 532 | "mime": "cli.js" 533 | }, 534 | "engines": { 535 | "node": ">=4" 536 | } 537 | }, 538 | "node_modules/mime-db": { 539 | "version": "1.52.0", 540 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 541 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 542 | "engines": { 543 | "node": ">= 0.6" 544 | } 545 | }, 546 | "node_modules/mime-types": { 547 | "version": "2.1.35", 548 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 549 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 550 | "dependencies": { 551 | "mime-db": "1.52.0" 552 | }, 553 | "engines": { 554 | "node": ">= 0.6" 555 | } 556 | }, 557 | "node_modules/ms": { 558 | "version": "2.0.0", 559 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 560 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 561 | }, 562 | "node_modules/negotiator": { 563 | "version": "0.6.3", 564 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 565 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 566 | "engines": { 567 | "node": ">= 0.6" 568 | } 569 | }, 570 | "node_modules/object-assign": { 571 | "version": "4.1.1", 572 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 573 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 574 | "engines": { 575 | "node": ">=0.10.0" 576 | } 577 | }, 578 | "node_modules/object-inspect": { 579 | "version": "1.13.2", 580 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", 581 | "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", 582 | "engines": { 583 | "node": ">= 0.4" 584 | }, 585 | "funding": { 586 | "url": "https://github.com/sponsors/ljharb" 587 | } 588 | }, 589 | "node_modules/on-finished": { 590 | "version": "2.4.1", 591 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 592 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 593 | "dependencies": { 594 | "ee-first": "1.1.1" 595 | }, 596 | "engines": { 597 | "node": ">= 0.8" 598 | } 599 | }, 600 | "node_modules/parseurl": { 601 | "version": "1.3.3", 602 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 603 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 604 | "engines": { 605 | "node": ">= 0.8" 606 | } 607 | }, 608 | "node_modules/path-to-regexp": { 609 | "version": "0.1.7", 610 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 611 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 612 | }, 613 | "node_modules/proxy-addr": { 614 | "version": "2.0.7", 615 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 616 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 617 | "dependencies": { 618 | "forwarded": "0.2.0", 619 | "ipaddr.js": "1.9.1" 620 | }, 621 | "engines": { 622 | "node": ">= 0.10" 623 | } 624 | }, 625 | "node_modules/qs": { 626 | "version": "6.11.0", 627 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 628 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 629 | "dependencies": { 630 | "side-channel": "^1.0.4" 631 | }, 632 | "engines": { 633 | "node": ">=0.6" 634 | }, 635 | "funding": { 636 | "url": "https://github.com/sponsors/ljharb" 637 | } 638 | }, 639 | "node_modules/range-parser": { 640 | "version": "1.2.1", 641 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 642 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 643 | "engines": { 644 | "node": ">= 0.6" 645 | } 646 | }, 647 | "node_modules/raw-body": { 648 | "version": "2.5.2", 649 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 650 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 651 | "dependencies": { 652 | "bytes": "3.1.2", 653 | "http-errors": "2.0.0", 654 | "iconv-lite": "0.4.24", 655 | "unpipe": "1.0.0" 656 | }, 657 | "engines": { 658 | "node": ">= 0.8" 659 | } 660 | }, 661 | "node_modules/safe-buffer": { 662 | "version": "5.2.1", 663 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 664 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 665 | "funding": [ 666 | { 667 | "type": "github", 668 | "url": "https://github.com/sponsors/feross" 669 | }, 670 | { 671 | "type": "patreon", 672 | "url": "https://www.patreon.com/feross" 673 | }, 674 | { 675 | "type": "consulting", 676 | "url": "https://feross.org/support" 677 | } 678 | ] 679 | }, 680 | "node_modules/safer-buffer": { 681 | "version": "2.1.2", 682 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 683 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 684 | }, 685 | "node_modules/send": { 686 | "version": "0.18.0", 687 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 688 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 689 | "dependencies": { 690 | "debug": "2.6.9", 691 | "depd": "2.0.0", 692 | "destroy": "1.2.0", 693 | "encodeurl": "~1.0.2", 694 | "escape-html": "~1.0.3", 695 | "etag": "~1.8.1", 696 | "fresh": "0.5.2", 697 | "http-errors": "2.0.0", 698 | "mime": "1.6.0", 699 | "ms": "2.1.3", 700 | "on-finished": "2.4.1", 701 | "range-parser": "~1.2.1", 702 | "statuses": "2.0.1" 703 | }, 704 | "engines": { 705 | "node": ">= 0.8.0" 706 | } 707 | }, 708 | "node_modules/send/node_modules/ms": { 709 | "version": "2.1.3", 710 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 711 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 712 | }, 713 | "node_modules/serve-static": { 714 | "version": "1.15.0", 715 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 716 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 717 | "dependencies": { 718 | "encodeurl": "~1.0.2", 719 | "escape-html": "~1.0.3", 720 | "parseurl": "~1.3.3", 721 | "send": "0.18.0" 722 | }, 723 | "engines": { 724 | "node": ">= 0.8.0" 725 | } 726 | }, 727 | "node_modules/set-function-length": { 728 | "version": "1.2.2", 729 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 730 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 731 | "dependencies": { 732 | "define-data-property": "^1.1.4", 733 | "es-errors": "^1.3.0", 734 | "function-bind": "^1.1.2", 735 | "get-intrinsic": "^1.2.4", 736 | "gopd": "^1.0.1", 737 | "has-property-descriptors": "^1.0.2" 738 | }, 739 | "engines": { 740 | "node": ">= 0.4" 741 | } 742 | }, 743 | "node_modules/setprototypeof": { 744 | "version": "1.2.0", 745 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 746 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 747 | }, 748 | "node_modules/side-channel": { 749 | "version": "1.0.6", 750 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 751 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 752 | "dependencies": { 753 | "call-bind": "^1.0.7", 754 | "es-errors": "^1.3.0", 755 | "get-intrinsic": "^1.2.4", 756 | "object-inspect": "^1.13.1" 757 | }, 758 | "engines": { 759 | "node": ">= 0.4" 760 | }, 761 | "funding": { 762 | "url": "https://github.com/sponsors/ljharb" 763 | } 764 | }, 765 | "node_modules/socket.io": { 766 | "version": "4.7.5", 767 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", 768 | "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", 769 | "dependencies": { 770 | "accepts": "~1.3.4", 771 | "base64id": "~2.0.0", 772 | "cors": "~2.8.5", 773 | "debug": "~4.3.2", 774 | "engine.io": "~6.5.2", 775 | "socket.io-adapter": "~2.5.2", 776 | "socket.io-parser": "~4.2.4" 777 | }, 778 | "engines": { 779 | "node": ">=10.2.0" 780 | } 781 | }, 782 | "node_modules/socket.io-adapter": { 783 | "version": "2.5.5", 784 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", 785 | "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", 786 | "dependencies": { 787 | "debug": "~4.3.4", 788 | "ws": "~8.17.1" 789 | } 790 | }, 791 | "node_modules/socket.io-adapter/node_modules/debug": { 792 | "version": "4.3.5", 793 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 794 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 795 | "dependencies": { 796 | "ms": "2.1.2" 797 | }, 798 | "engines": { 799 | "node": ">=6.0" 800 | }, 801 | "peerDependenciesMeta": { 802 | "supports-color": { 803 | "optional": true 804 | } 805 | } 806 | }, 807 | "node_modules/socket.io-adapter/node_modules/ms": { 808 | "version": "2.1.2", 809 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 810 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 811 | }, 812 | "node_modules/socket.io-parser": { 813 | "version": "4.2.4", 814 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", 815 | "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", 816 | "dependencies": { 817 | "@socket.io/component-emitter": "~3.1.0", 818 | "debug": "~4.3.1" 819 | }, 820 | "engines": { 821 | "node": ">=10.0.0" 822 | } 823 | }, 824 | "node_modules/socket.io-parser/node_modules/debug": { 825 | "version": "4.3.4", 826 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 827 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 828 | "dependencies": { 829 | "ms": "2.1.2" 830 | }, 831 | "engines": { 832 | "node": ">=6.0" 833 | }, 834 | "peerDependenciesMeta": { 835 | "supports-color": { 836 | "optional": true 837 | } 838 | } 839 | }, 840 | "node_modules/socket.io-parser/node_modules/ms": { 841 | "version": "2.1.2", 842 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 843 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 844 | }, 845 | "node_modules/socket.io/node_modules/debug": { 846 | "version": "4.3.4", 847 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 848 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 849 | "dependencies": { 850 | "ms": "2.1.2" 851 | }, 852 | "engines": { 853 | "node": ">=6.0" 854 | }, 855 | "peerDependenciesMeta": { 856 | "supports-color": { 857 | "optional": true 858 | } 859 | } 860 | }, 861 | "node_modules/socket.io/node_modules/ms": { 862 | "version": "2.1.2", 863 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 864 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 865 | }, 866 | "node_modules/statuses": { 867 | "version": "2.0.1", 868 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 869 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 870 | "engines": { 871 | "node": ">= 0.8" 872 | } 873 | }, 874 | "node_modules/toidentifier": { 875 | "version": "1.0.1", 876 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 877 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 878 | "engines": { 879 | "node": ">=0.6" 880 | } 881 | }, 882 | "node_modules/type-is": { 883 | "version": "1.6.18", 884 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 885 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 886 | "dependencies": { 887 | "media-typer": "0.3.0", 888 | "mime-types": "~2.1.24" 889 | }, 890 | "engines": { 891 | "node": ">= 0.6" 892 | } 893 | }, 894 | "node_modules/undici-types": { 895 | "version": "5.26.5", 896 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 897 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 898 | }, 899 | "node_modules/unpipe": { 900 | "version": "1.0.0", 901 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 902 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 903 | "engines": { 904 | "node": ">= 0.8" 905 | } 906 | }, 907 | "node_modules/utils-merge": { 908 | "version": "1.0.1", 909 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 910 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 911 | "engines": { 912 | "node": ">= 0.4.0" 913 | } 914 | }, 915 | "node_modules/vary": { 916 | "version": "1.1.2", 917 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 918 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 919 | "engines": { 920 | "node": ">= 0.8" 921 | } 922 | }, 923 | "node_modules/ws": { 924 | "version": "8.17.1", 925 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", 926 | "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", 927 | "engines": { 928 | "node": ">=10.0.0" 929 | }, 930 | "peerDependencies": { 931 | "bufferutil": "^4.0.1", 932 | "utf-8-validate": ">=5.0.2" 933 | }, 934 | "peerDependenciesMeta": { 935 | "bufferutil": { 936 | "optional": true 937 | }, 938 | "utf-8-validate": { 939 | "optional": true 940 | } 941 | } 942 | } 943 | } 944 | } 945 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mintr", 3 | "author": "Kevin Bedi", 4 | "version": "0.0.12", 5 | "description": "A simple unix monitor", 6 | "main": "app.js", 7 | "bin": "./app.js", 8 | "license": "MIT", 9 | "dependencies": { 10 | "async": "^0.9.0", 11 | "express": "^4.19.2", 12 | "socket.io": "^4.6.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /public/css/darkmode.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #060606; 3 | } 4 | 5 | .theme-button::after { 6 | content: "🌙"; 7 | } 8 | 9 | .header { 10 | background: #000; 11 | box-shadow: 0 0 5px #444; 12 | } 13 | 14 | .widget { 15 | background: #000; 16 | border: 1px solid #0f0f0f; 17 | box-shadow: 0 0 5px #111; 18 | } 19 | 20 | .widget-title { 21 | background: #111; 22 | color: #aaa; 23 | border-bottom: 1px solid #444; 24 | } 25 | 26 | .tooltip { 27 | background: #000; 28 | color: #fff; 29 | box-shadow: 0 2px 6px -4px #111; 30 | } -------------------------------------------------------------------------------- /public/css/light.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #fafafa; 3 | } 4 | 5 | .theme-button::after { 6 | content: "🌞"; 7 | } 8 | 9 | .header { 10 | background: white; 11 | box-shadow: 0 0 5px #888; 12 | } 13 | 14 | .widget { 15 | background: #fff; 16 | border: 1px solid #f0f0f0; 17 | box-shadow: 0 0 5px #ccc; 18 | } 19 | 20 | .widget-title { 21 | background: #f5f5f5; 22 | color: #aaa; 23 | border-bottom: 1px solid #eee; 24 | } 25 | 26 | .tooltip { 27 | background: #f9f9f9; 28 | color: #000; 29 | box-shadow: 0 2px 6px -4px #444; 30 | } -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --transition: all ease-in-out 200ms; 3 | } 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | } 10 | 11 | body { 12 | font-family: "Open Sans", sans-serif; 13 | font-weight: 300; 14 | transition: var(--transition); 15 | } 16 | 17 | .theme-button { 18 | cursor: pointer; 19 | } 20 | 21 | .header { 22 | color: #2196f3; 23 | padding: 20px; 24 | font-size: 22px; 25 | font-weight: bold; 26 | position: fixed; 27 | width: 100%; 28 | top: 0; 29 | left: 0; 30 | z-index: 1; 31 | transition: var(--transition); 32 | } 33 | 34 | .container { 35 | width: 960px; 36 | margin: 0 auto; 37 | padding: 80px 0 0 10px; 38 | } 39 | 40 | .widget { 41 | margin: 0 10px 10px 0; 42 | text-align: center; 43 | display: inline-block; 44 | width: 100%; 45 | vertical-align: top; 46 | float: left; 47 | border-radius: 8px; 48 | overflow: hidden; 49 | transition: var(--transition); 50 | } 51 | 52 | .widget-content { 53 | padding: 20px; 54 | color: #2196f3; 55 | font-weight: 500; 56 | position: relative; 57 | } 58 | 59 | .widget-content .pie { 60 | text-align: left; 61 | color: #888; 62 | } 63 | 64 | .widget-content .legend { 65 | display: inline-block; 66 | margin-left: 40px; 67 | vertical-align: top; 68 | max-width: 292px; 69 | white-space: nowrap; 70 | overflow: hidden; 71 | } 72 | 73 | .widget-content .legend ul { 74 | list-style-type: none; 75 | line-height: 56px; 76 | cursor: default; 77 | } 78 | 79 | .widget-content .legend ul li span { 80 | width: 40px; 81 | height: 5px; 82 | display: inline-block; 83 | margin-right: 15px; 84 | vertical-align: middle; 85 | } 86 | 87 | .widget-content .legend ul li { 88 | position: relative; 89 | } 90 | 91 | .widget.one { 92 | width: 306px; 93 | } 94 | 95 | .widget.two { 96 | width: 624px; 97 | } 98 | 99 | .widget.three { 100 | width: 940px; 101 | } 102 | 103 | .widget-title { 104 | font-weight: 500; 105 | font-size: 18px; 106 | text-transform: uppercase; 107 | text-align: left; 108 | padding: 14px 20px; 109 | transition: var(--transition); 110 | } 111 | 112 | .title-container { 113 | width: 960px; 114 | margin: 0 auto; 115 | padding: 0 10px; 116 | display: flex; 117 | justify-content: space-between; 118 | } 119 | 120 | .tooltip { 121 | position: absolute; 122 | top: 190px; 123 | left: 610px; 124 | padding: 5px 10px; 125 | border-radius: 2px; 126 | white-space: nowrap; 127 | pointer-events: none; 128 | display: none; 129 | line-height: normal; 130 | max-width: 800px; 131 | white-space: pre-wrap; 132 | word-wrap: break-word; 133 | transition: var(--transition); 134 | } 135 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mintr 5 | 6 | 11 | 12 | 13 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
47 |
48 |
49 | Mintr 50 |
51 |
52 |
53 |
54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /public/js/lib/moment.min.js: -------------------------------------------------------------------------------- 1 | //! moment.js 2 | //! version : 2.9.0 3 | //! authors : Tim Wood, Iskren Chernev, Moment.js contributors 4 | //! license : MIT 5 | //! momentjs.com 6 | (function(a){function b(a,b,c){switch(arguments.length){case 2:return null!=a?a:b;case 3:return null!=a?a:null!=b?b:c;default:throw new Error("Implement me")}}function c(a,b){return Bb.call(a,b)}function d(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function e(a){vb.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+a)}function f(a,b){var c=!0;return o(function(){return c&&(e(a),c=!1),b.apply(this,arguments)},b)}function g(a,b){sc[a]||(e(b),sc[a]=!0)}function h(a,b){return function(c){return r(a.call(this,c),b)}}function i(a,b){return function(c){return this.localeData().ordinal(a.call(this,c),b)}}function j(a,b){var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()),f=a.clone().add(e,"months");return 0>b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)}function k(a,b,c){var d;return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}function l(){}function m(a,b){b!==!1&&H(a),p(this,a),this._d=new Date(+a._d),uc===!1&&(uc=!0,vb.updateOffset(this),uc=!1)}function n(a){var b=A(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;this._milliseconds=+k+1e3*j+6e4*i+36e5*h,this._days=+g+7*f,this._months=+e+3*d+12*c,this._data={},this._locale=vb.localeData(),this._bubble()}function o(a,b){for(var d in b)c(b,d)&&(a[d]=b[d]);return c(b,"toString")&&(a.toString=b.toString),c(b,"valueOf")&&(a.valueOf=b.valueOf),a}function p(a,b){var c,d,e;if("undefined"!=typeof b._isAMomentObject&&(a._isAMomentObject=b._isAMomentObject),"undefined"!=typeof b._i&&(a._i=b._i),"undefined"!=typeof b._f&&(a._f=b._f),"undefined"!=typeof b._l&&(a._l=b._l),"undefined"!=typeof b._strict&&(a._strict=b._strict),"undefined"!=typeof b._tzm&&(a._tzm=b._tzm),"undefined"!=typeof b._isUTC&&(a._isUTC=b._isUTC),"undefined"!=typeof b._offset&&(a._offset=b._offset),"undefined"!=typeof b._pf&&(a._pf=b._pf),"undefined"!=typeof b._locale&&(a._locale=b._locale),Kb.length>0)for(c in Kb)d=Kb[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function q(a){return 0>a?Math.ceil(a):Math.floor(a)}function r(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&C(a[d])!==C(b[d]))&&g++;return g+f}function z(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=lc[a]||mc[b]||b}return a}function A(a){var b,d,e={};for(d in a)c(a,d)&&(b=z(d),b&&(e[b]=a[d]));return e}function B(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}vb[b]=function(e,f){var g,h,i=vb._locale[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=vb().utc().set(d,a);return i.call(vb._locale,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function C(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function D(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function E(a,b,c){return jb(vb([a,11,31+b-c]),b,c).week}function F(a){return G(a)?366:365}function G(a){return a%4===0&&a%100!==0||a%400===0}function H(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[Db]<0||a._a[Db]>11?Db:a._a[Eb]<1||a._a[Eb]>D(a._a[Cb],a._a[Db])?Eb:a._a[Fb]<0||a._a[Fb]>24||24===a._a[Fb]&&(0!==a._a[Gb]||0!==a._a[Hb]||0!==a._a[Ib])?Fb:a._a[Gb]<0||a._a[Gb]>59?Gb:a._a[Hb]<0||a._a[Hb]>59?Hb:a._a[Ib]<0||a._a[Ib]>999?Ib:-1,a._pf._overflowDayOfYear&&(Cb>b||b>Eb)&&(b=Eb),a._pf.overflow=b)}function I(b){return null==b._isValid&&(b._isValid=!isNaN(b._d.getTime())&&b._pf.overflow<0&&!b._pf.empty&&!b._pf.invalidMonth&&!b._pf.nullInput&&!b._pf.invalidFormat&&!b._pf.userInvalidated,b._strict&&(b._isValid=b._isValid&&0===b._pf.charsLeftOver&&0===b._pf.unusedTokens.length&&b._pf.bigHour===a)),b._isValid}function J(a){return a?a.toLowerCase().replace("_","-"):a}function K(a){for(var b,c,d,e,f=0;f0;){if(d=L(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&y(e,c,!0)>=b-1)break;b--}f++}return null}function L(a){var b=null;if(!Jb[a]&&Lb)try{b=vb.locale(),require("./locale/"+a),vb.locale(b)}catch(c){}return Jb[a]}function M(a,b){var c,d;return b._isUTC?(c=b.clone(),d=(vb.isMoment(a)||x(a)?+a:+vb(a))-+c,c._d.setTime(+c._d+d),vb.updateOffset(c,!1),c):vb(a).local()}function N(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function O(a){var b,c,d=a.match(Pb);for(b=0,c=d.length;c>b;b++)d[b]=rc[d[b]]?rc[d[b]]:N(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function P(a,b){return a.isValid()?(b=Q(b,a.localeData()),nc[b]||(nc[b]=O(b)),nc[b](a)):a.localeData().invalidDate()}function Q(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Qb.lastIndex=0;d>=0&&Qb.test(a);)a=a.replace(Qb,c),Qb.lastIndex=0,d-=1;return a}function R(a,b){var c,d=b._strict;switch(a){case"Q":return _b;case"DDDD":return bc;case"YYYY":case"GGGG":case"gggg":return d?cc:Tb;case"Y":case"G":case"g":return ec;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?dc:Ub;case"S":if(d)return _b;case"SS":if(d)return ac;case"SSS":if(d)return bc;case"DDD":return Sb;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Wb;case"a":case"A":return b._locale._meridiemParse;case"x":return Zb;case"X":return $b;case"Z":case"ZZ":return Xb;case"T":return Yb;case"SSSS":return Vb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?ac:Rb;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Rb;case"Do":return d?b._locale._ordinalParse:b._locale._ordinalParseLenient;default:return c=new RegExp($(Z(a.replace("\\","")),"i"))}}function S(a){a=a||"";var b=a.match(Xb)||[],c=b[b.length-1]||[],d=(c+"").match(jc)||["-",0,0],e=+(60*d[1])+C(d[2]);return"+"===d[0]?e:-e}function T(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[Db]=3*(C(b)-1));break;case"M":case"MM":null!=b&&(e[Db]=C(b)-1);break;case"MMM":case"MMMM":d=c._locale.monthsParse(b,a,c._strict),null!=d?e[Db]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[Eb]=C(b));break;case"Do":null!=b&&(e[Eb]=C(parseInt(b.match(/\d{1,2}/)[0],10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=C(b));break;case"YY":e[Cb]=vb.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[Cb]=C(b);break;case"a":case"A":c._meridiem=b;break;case"h":case"hh":c._pf.bigHour=!0;case"H":case"HH":e[Fb]=C(b);break;case"m":case"mm":e[Gb]=C(b);break;case"s":case"ss":e[Hb]=C(b);break;case"S":case"SS":case"SSS":case"SSSS":e[Ib]=C(1e3*("0."+b));break;case"x":c._d=new Date(C(b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=S(b);break;case"dd":case"ddd":case"dddd":d=c._locale.weekdaysParse(b),null!=d?(c._w=c._w||{},c._w.d=d):c._pf.invalidWeekday=b;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":a=a.substr(0,1);case"gggg":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=C(b));break;case"gg":case"GG":c._w=c._w||{},c._w[a]=vb.parseTwoDigitYear(b)}}function U(a){var c,d,e,f,g,h,i;c=a._w,null!=c.GG||null!=c.W||null!=c.E?(g=1,h=4,d=b(c.GG,a._a[Cb],jb(vb(),1,4).year),e=b(c.W,1),f=b(c.E,1)):(g=a._locale._week.dow,h=a._locale._week.doy,d=b(c.gg,a._a[Cb],jb(vb(),g,h).year),e=b(c.w,1),null!=c.d?(f=c.d,g>f&&++e):f=null!=c.e?c.e+g:g),i=kb(d,e,f,h,g),a._a[Cb]=i.year,a._dayOfYear=i.dayOfYear}function V(a){var c,d,e,f,g=[];if(!a._d){for(e=X(a),a._w&&null==a._a[Eb]&&null==a._a[Db]&&U(a),a._dayOfYear&&(f=b(a._a[Cb],e[Cb]),a._dayOfYear>F(f)&&(a._pf._overflowDayOfYear=!0),d=fb(f,0,a._dayOfYear),a._a[Db]=d.getUTCMonth(),a._a[Eb]=d.getUTCDate()),c=0;3>c&&null==a._a[c];++c)a._a[c]=g[c]=e[c];for(;7>c;c++)a._a[c]=g[c]=null==a._a[c]?2===c?1:0:a._a[c];24===a._a[Fb]&&0===a._a[Gb]&&0===a._a[Hb]&&0===a._a[Ib]&&(a._nextDay=!0,a._a[Fb]=0),a._d=(a._useUTC?fb:eb).apply(null,g),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[Fb]=24)}}function W(a){var b;a._d||(b=A(a._i),a._a=[b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],V(a))}function X(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function Y(b){if(b._f===vb.ISO_8601)return void ab(b);b._a=[],b._pf.empty=!0;var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=Q(b._f,b._locale).match(Pb)||[],c=0;c0&&b._pf.unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length),rc[f]?(d?b._pf.empty=!1:b._pf.unusedTokens.push(f),T(f,d,b)):b._strict&&!d&&b._pf.unusedTokens.push(f);b._pf.charsLeftOver=i-j,h.length>0&&b._pf.unusedInput.push(h),b._pf.bigHour===!0&&b._a[Fb]<=12&&(b._pf.bigHour=a),b._a[Fb]=k(b._locale,b._a[Fb],b._meridiem),V(b),H(b)}function Z(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function $(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function _(a){var b,c,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,c=b));o(a,c||b)}function ab(a){var b,c,d=a._i,e=fc.exec(d);if(e){for(a._pf.iso=!0,b=0,c=hc.length;c>b;b++)if(hc[b][1].exec(d)){a._f=hc[b][0]+(e[6]||" ");break}for(b=0,c=ic.length;c>b;b++)if(ic[b][1].exec(d)){a._f+=ic[b][0];break}d.match(Xb)&&(a._f+="Z"),Y(a)}else a._isValid=!1}function bb(a){ab(a),a._isValid===!1&&(delete a._isValid,vb.createFromInputFallback(a))}function cb(a,b){var c,d=[];for(c=0;ca&&h.setFullYear(a),h}function fb(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function gb(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function hb(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function ib(a,b,c){var d=vb.duration(a).abs(),e=Ab(d.as("s")),f=Ab(d.as("m")),g=Ab(d.as("h")),h=Ab(d.as("d")),i=Ab(d.as("M")),j=Ab(d.as("y")),k=e0,k[4]=c,hb.apply({},k)}function jb(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=vb(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function kb(a,b,c,d,e){var f,g,h=fb(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:F(a-1)+g}}function lb(b){var c,d=b._i,e=b._f;return b._locale=b._locale||vb.localeData(b._l),null===d||e===a&&""===d?vb.invalid({nullInput:!0}):("string"==typeof d&&(b._i=d=b._locale.preparse(d)),vb.isMoment(d)?new m(d,!0):(e?w(e)?_(b):Y(b):db(b),c=new m(b),c._nextDay&&(c.add(1,"d"),c._nextDay=a),c))}function mb(a,b){var c,d;if(1===b.length&&w(b[0])&&(b=b[0]),!b.length)return vb();for(c=b[0],d=1;d=0?"+":"-";return b+r(Math.abs(a),6)},gg:function(){return r(this.weekYear()%100,2)},gggg:function(){return r(this.weekYear(),4)},ggggg:function(){return r(this.weekYear(),5)},GG:function(){return r(this.isoWeekYear()%100,2)},GGGG:function(){return r(this.isoWeekYear(),4)},GGGGG:function(){return r(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return C(this.milliseconds()/100)},SS:function(){return r(C(this.milliseconds()/10),2)},SSS:function(){return r(this.milliseconds(),3)},SSSS:function(){return r(this.milliseconds(),3)},Z:function(){var a=this.utcOffset(),b="+";return 0>a&&(a=-a,b="-"),b+r(C(a/60),2)+":"+r(C(a)%60,2)},ZZ:function(){var a=this.utcOffset(),b="+";return 0>a&&(a=-a,b="-"),b+r(C(a/60),2)+r(C(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},x:function(){return this.valueOf()},X:function(){return this.unix()},Q:function(){return this.quarter()}},sc={},tc=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"],uc=!1;pc.length;)xb=pc.pop(),rc[xb+"o"]=i(rc[xb],xb);for(;qc.length;)xb=qc.pop(),rc[xb+xb]=h(rc[xb],2);rc.DDDD=h(rc.DDD,3),o(l.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b;this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a,b,c){var d,e,f;for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;12>d;d++){if(e=vb.utc([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=vb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b,c){var d=this._calendar[a];return"function"==typeof d?d.apply(b,[c]):d},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",_ordinalParse:/\d{1,2}/,preparse:function(a){return a},postformat:function(a){return a},week:function(a){return jb(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},firstDayOfWeek:function(){return this._week.dow},firstDayOfYear:function(){return this._week.doy},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),vb=function(b,c,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=b,g._f=c,g._l=e,g._strict=f,g._isUTC=!1,g._pf=d(),lb(g)},vb.suppressDeprecationWarnings=!1,vb.createFromInputFallback=f("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}),vb.min=function(){var a=[].slice.call(arguments,0);return mb("isBefore",a)},vb.max=function(){var a=[].slice.call(arguments,0);return mb("isAfter",a)},vb.utc=function(b,c,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=b,g._f=c,g._strict=f,g._pf=d(),lb(g).utc()},vb.unix=function(a){return vb(1e3*a)},vb.duration=function(a,b){var d,e,f,g,h=a,i=null;return vb.isDuration(a)?h={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(h={},b?h[b]=a:h.milliseconds=a):(i=Nb.exec(a))?(d="-"===i[1]?-1:1,h={y:0,d:C(i[Eb])*d,h:C(i[Fb])*d,m:C(i[Gb])*d,s:C(i[Hb])*d,ms:C(i[Ib])*d}):(i=Ob.exec(a))?(d="-"===i[1]?-1:1,f=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*d},h={y:f(i[2]),M:f(i[3]),d:f(i[4]),h:f(i[5]),m:f(i[6]),s:f(i[7]),w:f(i[8])}):null==h?h={}:"object"==typeof h&&("from"in h||"to"in h)&&(g=t(vb(h.from),vb(h.to)),h={},h.ms=g.milliseconds,h.M=g.months),e=new n(h),vb.isDuration(a)&&c(a,"_locale")&&(e._locale=a._locale),e},vb.version=yb,vb.defaultFormat=gc,vb.ISO_8601=function(){},vb.momentProperties=Kb,vb.updateOffset=function(){},vb.relativeTimeThreshold=function(b,c){return oc[b]===a?!1:c===a?oc[b]:(oc[b]=c,!0)},vb.lang=f("moment.lang is deprecated. Use moment.locale instead.",function(a,b){return vb.locale(a,b)}),vb.locale=function(a,b){var c;return a&&(c="undefined"!=typeof b?vb.defineLocale(a,b):vb.localeData(a),c&&(vb.duration._locale=vb._locale=c)),vb._locale._abbr},vb.defineLocale=function(a,b){return null!==b?(b.abbr=a,Jb[a]||(Jb[a]=new l),Jb[a].set(b),vb.locale(a),Jb[a]):(delete Jb[a],null)},vb.langData=f("moment.langData is deprecated. Use moment.localeData instead.",function(a){return vb.localeData(a)}),vb.localeData=function(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return vb._locale;if(!w(a)){if(b=L(a))return b;a=[a]}return K(a)},vb.isMoment=function(a){return a instanceof m||null!=a&&c(a,"_isAMomentObject")},vb.isDuration=function(a){return a instanceof n};for(xb=tc.length-1;xb>=0;--xb)B(tc[xb]);vb.normalizeUnits=function(a){return z(a)},vb.invalid=function(a){var b=vb.utc(0/0);return null!=a?o(b._pf,a):b._pf.userInvalidated=!0,b},vb.parseZone=function(){return vb.apply(null,arguments).parseZone()},vb.parseTwoDigitYear=function(a){return C(a)+(C(a)>68?1900:2e3)},vb.isDate=x,o(vb.fn=m.prototype,{clone:function(){return vb(this)},valueOf:function(){return+this._d-6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=vb(this).utc();return 00:!1},parsingFlags:function(){return o({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(a){return this.utcOffset(0,a)},local:function(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(this._dateUtcOffset(),"m")),this},format:function(a){var b=P(this,a||vb.defaultFormat);return this.localeData().postformat(b)},add:u(1,"add"),subtract:u(-1,"subtract"),diff:function(a,b,c){var d,e,f=M(a,this),g=6e4*(f.utcOffset()-this.utcOffset());return b=z(b),"year"===b||"month"===b||"quarter"===b?(e=j(this,f),"quarter"===b?e/=3:"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:q(e)},from:function(a,b){return vb.duration({to:this,from:a}).locale(this.locale()).humanize(!b)},fromNow:function(a){return this.from(vb(),a)},calendar:function(a){var b=a||vb(),c=M(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.localeData().calendar(e,this,vb(b)))},isLeapYear:function(){return G(this.year())},isDST:function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},day:function(a){var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=gb(a,this.localeData()),this.add(a-b,"d")):b},month:qb("Month",!0),startOf:function(a){switch(a=z(a)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===a?this.weekday(0):"isoWeek"===a&&this.isoWeekday(1),"quarter"===a&&this.month(3*Math.floor(this.month()/3)),this},endOf:function(b){return b=z(b),b===a||"millisecond"===b?this:this.startOf(b).add(1,"isoWeek"===b?"week":b).subtract(1,"ms")},isAfter:function(a,b){var c;return b=z("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=vb.isMoment(a)?a:vb(a),+this>+a):(c=vb.isMoment(a)?+a:+vb(a),c<+this.clone().startOf(b))},isBefore:function(a,b){var c;return b=z("undefined"!=typeof b?b:"millisecond"),"millisecond"===b?(a=vb.isMoment(a)?a:vb(a),+a>+this):(c=vb.isMoment(a)?+a:+vb(a),+this.clone().endOf(b)a?this:a}),max:f("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(a){return a=vb.apply(null,arguments),a>this?this:a}),zone:f("moment().zone is deprecated, use moment().utcOffset instead. https://github.com/moment/moment/issues/1779",function(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}),utcOffset:function(a,b){var c,d=this._offset||0;return null!=a?("string"==typeof a&&(a=S(a)),Math.abs(a)<16&&(a=60*a),!this._isUTC&&b&&(c=this._dateUtcOffset()),this._offset=a,this._isUTC=!0,null!=c&&this.add(c,"m"),d!==a&&(!b||this._changeInProgress?v(this,vb.duration(a-d,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,vb.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?d:this._dateUtcOffset()},isLocal:function(){return!this._isUTC},isUtcOffset:function(){return this._isUTC},isUtc:function(){return this._isUTC&&0===this._offset},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(S(this._i)),this},hasAlignedHourOffset:function(a){return a=a?vb(a).utcOffset():0,(this.utcOffset()-a)%60===0},daysInMonth:function(){return D(this.year(),this.month())},dayOfYear:function(a){var b=Ab((vb(this).startOf("day")-vb(this).startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=jb(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==a?b:this.add(a-b,"y")},isoWeekYear:function(a){var b=jb(this,1,4).year;return null==a?b:this.add(a-b,"y")},week:function(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")},isoWeek:function(a){var b=jb(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")},weekday:function(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return E(this.year(),1,4)},weeksInYear:function(){var a=this.localeData()._week;return E(this.year(),a.dow,a.doy)},get:function(a){return a=z(a),this[a]()},set:function(a,b){var c;if("object"==typeof a)for(c in a)this.set(c,a[c]);else a=z(a),"function"==typeof this[a]&&this[a](b);return this},locale:function(b){var c;return b===a?this._locale._abbr:(c=vb.localeData(b),null!=c&&(this._locale=c),this)},lang:f("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(b){return b===a?this.localeData():this.locale(b)}),localeData:function(){return this._locale},_dateUtcOffset:function(){return 15*-Math.round(this._d.getTimezoneOffset()/15)}}),vb.fn.millisecond=vb.fn.milliseconds=qb("Milliseconds",!1),vb.fn.second=vb.fn.seconds=qb("Seconds",!1),vb.fn.minute=vb.fn.minutes=qb("Minutes",!1),vb.fn.hour=vb.fn.hours=qb("Hours",!0),vb.fn.date=qb("Date",!0),vb.fn.dates=f("dates accessor is deprecated. Use date instead.",qb("Date",!0)),vb.fn.year=qb("FullYear",!0),vb.fn.years=f("years accessor is deprecated. Use year instead.",qb("FullYear",!0)),vb.fn.days=vb.fn.day,vb.fn.months=vb.fn.month,vb.fn.weeks=vb.fn.week,vb.fn.isoWeeks=vb.fn.isoWeek,vb.fn.quarters=vb.fn.quarter,vb.fn.toJSON=vb.fn.toISOString,vb.fn.isUTC=vb.fn.isUtc,o(vb.duration.fn=n.prototype,{_bubble:function(){var a,b,c,d=this._milliseconds,e=this._days,f=this._months,g=this._data,h=0;g.milliseconds=d%1e3,a=q(d/1e3),g.seconds=a%60,b=q(a/60),g.minutes=b%60,c=q(b/60),g.hours=c%24,e+=q(c/24),h=q(rb(e)),e-=q(sb(h)),f+=q(e/30),e%=30,h+=q(f/12),f%=12,g.days=e,g.months=f,g.years=h},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return q(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*C(this._months/12) 7 | },humanize:function(a){var b=ib(this,!a,this.localeData());return a&&(b=this.localeData().pastFuture(+this,b)),this.localeData().postformat(b)},add:function(a,b){var c=vb.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=vb.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=z(a),this[a.toLowerCase()+"s"]()},as:function(a){var b,c;if(a=z(a),"month"===a||"year"===a)return b=this._days+this._milliseconds/864e5,c=this._months+12*rb(b),"month"===a?c:c/12;switch(b=this._days+Math.round(sb(this._months/12)),a){case"week":return b/7+this._milliseconds/6048e5;case"day":return b+this._milliseconds/864e5;case"hour":return 24*b+this._milliseconds/36e5;case"minute":return 24*b*60+this._milliseconds/6e4;case"second":return 24*b*60*60+this._milliseconds/1e3;case"millisecond":return Math.floor(24*b*60*60*1e3)+this._milliseconds;default:throw new Error("Unknown unit "+a)}},lang:vb.fn.lang,locale:vb.fn.locale,toIsoString:f("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"},localeData:function(){return this._locale},toJSON:function(){return this.toISOString()}}),vb.duration.fn.toString=vb.duration.fn.toISOString;for(xb in kc)c(kc,xb)&&tb(xb.toLowerCase());vb.duration.fn.asMilliseconds=function(){return this.as("ms")},vb.duration.fn.asSeconds=function(){return this.as("s")},vb.duration.fn.asMinutes=function(){return this.as("m")},vb.duration.fn.asHours=function(){return this.as("h")},vb.duration.fn.asDays=function(){return this.as("d")},vb.duration.fn.asWeeks=function(){return this.as("weeks")},vb.duration.fn.asMonths=function(){return this.as("M")},vb.duration.fn.asYears=function(){return this.as("y")},vb.locale("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===C(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),Lb?module.exports=vb:"function"==typeof define&&define.amd?(define(function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(zb.moment=wb),vb}),ub(!0)):ub()}).call(this); -------------------------------------------------------------------------------- /public/js/script.js: -------------------------------------------------------------------------------- 1 | var socket = io(); 2 | 3 | window.moveTooltip = function (tooltip, element, middleX) { 4 | var $tooltip = $('.tooltip'); 5 | if (!tooltip) { 6 | $tooltip.hide(); 7 | return; 8 | } 9 | 10 | if (!$tooltip.length) { 11 | $tooltip = $("
").addClass("tooltip"); 12 | } 13 | 14 | if (element) { 15 | element.appendChild($tooltip[0]); 16 | } else { 17 | tooltip.chart.canvas.parentNode.appendChild($tooltip[0]); 18 | } 19 | 20 | $tooltip.html(''); 21 | 22 | var values = tooltip.labels.map(function (label, index) { 23 | return { 24 | label: label, 25 | datasetLabel: tooltip.datasetLabels && tooltip.datasetLabels[index], 26 | color: tooltip.colors[index], 27 | }; 28 | }); 29 | 30 | values.sort(function (a, b) { 31 | return parseInt(b.label) - parseInt(a.label); 32 | }); 33 | 34 | values.forEach(function (value) { 35 | var label = value.label; 36 | if (value.datasetLabel) { 37 | label = value.datasetLabel + ': ' + label; 38 | } 39 | 40 | var $label = $('
') 41 | .text(label) 42 | .css({ 43 | color: value.color, 44 | }); 45 | $tooltip.append($label); 46 | }); 47 | 48 | $tooltip.css({ 49 | top: tooltip.y - 20 * (values && values.length || 1), 50 | left: tooltip.x + 20 - 51 | (tooltip.x > (middleX || 500) ? $tooltip.width() + 20 : 0), 52 | }); 53 | 54 | $tooltip.show(); 55 | }; 56 | 57 | $(function () { 58 | Chart.defaults.global.animation = false; 59 | 60 | var container = document.getElementsByClassName('container')[0]; 61 | 62 | var createWidget = function (Widget) { 63 | var widget = document.createElement('div'); 64 | container.appendChild(widget); 65 | 66 | var widgetObj = new Widget(widget); 67 | 68 | widget.className = 'widget ' + widgetObj.getWidthClass(); 69 | 70 | var titleEle = document.createElement('div'); 71 | titleEle.className = 'widget-title'; 72 | 73 | titleEle.innerText = widgetObj.getTitle(); 74 | 75 | var contEle = document.createElement('div'); 76 | contEle.className = 'widget-content'; 77 | 78 | widget.appendChild(titleEle); 79 | widget.appendChild(contEle); 80 | 81 | contEle.appendChild(widgetObj.getElement()); 82 | 83 | return widgetObj; 84 | }; 85 | 86 | var widgetClasses = [ 87 | CPUChartWidget, 88 | FreememWidget, 89 | NetworkWidget, 90 | ProcessCPUWidget, 91 | MemoryWidget, 92 | UptimeWidget, 93 | CPUUsageWidget, 94 | ProcessMemoryWidget, 95 | TempsWidget, 96 | ]; 97 | 98 | var widgets = widgetClasses.map(function (Widget) { 99 | return createWidget(Widget); 100 | }); 101 | 102 | socket.on('history', function (history) { 103 | widgets.forEach(function (widget) { 104 | widget.initialize(history); 105 | }); 106 | }); 107 | 108 | socket.on('data', function (data) { 109 | widgets.forEach(function (widget) { 110 | widget.addData(data); 111 | }); 112 | }); 113 | 114 | // Legend tooltips 115 | $(".legend").on("mouseover", "li", function (event) { 116 | var rect = this.getClientRects()[0]; 117 | window.moveTooltip({ 118 | labels: [this.textContent], 119 | colors: [$(this).find("span").css("background-color")], 120 | x: rect.left + 30, 121 | y: rect.top + 30 + window.scrollY, 122 | }, document.body, 10000); 123 | }); 124 | 125 | $(".legend").on("mouseout", "li", function (event) { 126 | $(".tooltip").hide(); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /public/js/theme.js: -------------------------------------------------------------------------------- 1 | const themes = ["light", "darkmode"]; 2 | 3 | $(function () { 4 | let currentTheme = window.localStorage.getItem("theme") ?? themes[0]; 5 | 6 | const setTheme = (theme) => { 7 | currentTheme = theme; 8 | window.localStorage.setItem("theme", currentTheme); 9 | 10 | document.getElementById("theme").href = "./css/" + currentTheme + ".css"; 11 | }; 12 | 13 | setTheme(currentTheme); 14 | 15 | $(".theme-button").on("click", () => { 16 | const curThemeIdx = themes.findIndex((x) => x === currentTheme); 17 | const newThemeIdx = (curThemeIdx + 1) % themes.length; 18 | 19 | setTheme(themes[newThemeIdx]); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /public/js/widgets/basechartwidget.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var BaseChartWidget = function(widget) { 3 | this.canvas = document.createElement('canvas'); 4 | this.canvas.width = widget.clientWidth - 40; 5 | this.canvas.height = 280; 6 | this.ctx = this.canvas.getContext('2d'); 7 | 8 | this.numPoints = 0; 9 | }; 10 | BaseChartWidget.prototype = Object.create(BaseWidget.prototype); 11 | 12 | BaseChartWidget.prototype.getElement = function() { 13 | return this.canvas; 14 | }; 15 | 16 | BaseChartWidget.prototype.getColors = function() { 17 | return ['#000000']; 18 | }, 19 | 20 | BaseChartWidget.prototype.getFillColors = function() { 21 | return ['#AAAAAA']; 22 | }; 23 | 24 | BaseChartWidget.prototype.getLabels = function() { 25 | return ['Label']; 26 | }; 27 | 28 | /* 29 | * Override to specify a suffix on the values in the chart 30 | * such as MB, or kB/s 31 | */ 32 | BaseChartWidget.prototype.getSuffix = function() { 33 | return ""; 34 | }; 35 | 36 | /* 37 | * Should return an object like: 38 | * { 39 | * values: [[1, 2, 3], [2, 6, 1]], 40 | * labels: ['', 'test', ''], 41 | * } 42 | */ 43 | BaseChartWidget.prototype.getPointsFromHistory = function(history) { 44 | throw "Children must override"; 45 | }; 46 | 47 | /* 48 | * Should return an integer 49 | */ 50 | BaseChartWidget.prototype.getValuesFromData = function(data) { 51 | throw "Children must override"; 52 | }; 53 | 54 | BaseChartWidget.prototype.getLabelFromData = function(data) { 55 | return ""; 56 | } 57 | 58 | BaseChartWidget.prototype.initialize = function(history) { 59 | var dataPoints = this.getPointsFromHistory(history); 60 | var labels = dataPoints.labels; 61 | 62 | var datasets = dataPoints.values.map(function(dataset, index) { 63 | return { 64 | fillColor: this.getFillColors()[index], 65 | strokeColor: this.getColors()[index], 66 | pointColor: 'rgba(0,0,0,0)', 67 | pointStrokeColor: 'rgba(0,0,0,0)', 68 | data: dataset, 69 | }; 70 | }, this); 71 | 72 | var chartData = { 73 | labels: labels, 74 | datasets: datasets, 75 | }; 76 | 77 | this.chart = new Chart(this.ctx).Line(chartData, { 78 | bezierCurveTension: 0.3, 79 | animationSteps: 30, 80 | scaleLabel: "<%= value %> " + this.getSuffix(), 81 | pointDotRadius: 4, 82 | scaleShowVerticalLines: false, 83 | showTooltips: false, 84 | }); 85 | 86 | $(this.canvas).on('mousemove', function(event) { 87 | var mouseX = event.originalEvent.offsetX || event.originalEvent.layerX; 88 | var mouseY = event.originalEvent.offsetY || event.originalEvent.layerY; 89 | 90 | this.chart.datasets.forEach(function(dataset) { 91 | dataset.points.forEach(function(point) { 92 | point.fillColor = 'rgba(0,0,0,0)'; 93 | }); 94 | }); 95 | 96 | var points = this.chart.datasets[0].points; 97 | var sortedPoints = points.map(function(point, index) { 98 | return { 99 | point: point, 100 | index: index, 101 | }; 102 | }); 103 | 104 | sortedPoints.sort(function(a, b) { 105 | var aDist = Math.abs(mouseX - a.point.x); 106 | var bDist = Math.abs(mouseX - b.point.x); 107 | 108 | return aDist - bDist; 109 | }); 110 | 111 | var closestPoint = sortedPoints[0]; 112 | 113 | this.chart.datasets.forEach(function(dataset, datasetIndex) { 114 | var index = sortedPoints[0].index; 115 | dataset.points[index].fillColor = this.getColors()[datasetIndex]; 116 | }, this); 117 | 118 | this.chart.render(true); 119 | 120 | var labels = this.chart.datasets.map(function(dataset) { 121 | return dataset.points[closestPoint.index].value + 122 | ' ' + this.getSuffix(); 123 | }, this); 124 | 125 | moveTooltip({ 126 | chart: this.chart.chart, 127 | labels: labels, 128 | colors: this.getColors(), 129 | datasetLabels: this.getLabels(), 130 | x: closestPoint.point.x, 131 | y: closestPoint.point.y, 132 | }); 133 | }.bind(this)); 134 | 135 | 136 | $(this.canvas).on('mouseout', function() { 137 | // Reset all datasets 138 | this.chart.datasets.forEach(function(dataset) { 139 | dataset.points.forEach(function(point) { 140 | point.fillColor = 'rgba(0,0,0,0)'; 141 | }); 142 | }); 143 | 144 | this.chart.render(true); 145 | 146 | moveTooltip(false); 147 | }.bind(this)); 148 | }; 149 | 150 | BaseChartWidget.prototype.addData = function(data) { 151 | var values = this.getValuesFromData(data); 152 | 153 | if (!values) { 154 | return; 155 | } 156 | 157 | var index = this.numPoints++; 158 | 159 | this.chart.addData( 160 | values, 161 | index % 5 === 0 ? this.getLabelFromData(data) : '' 162 | ); 163 | }; 164 | 165 | window.BaseChartWidget = BaseChartWidget; 166 | })(); 167 | -------------------------------------------------------------------------------- /public/js/widgets/basepiewidget.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var COLORS = ['#F44336', '#3F51B5', '#4CAF50', '#00BCD4', '#FFEB3B']; 3 | 4 | var BasePieWidget = function(widget) { 5 | this.div = document.createElement('div'); 6 | this.div.className = 'pie'; 7 | 8 | this.canvas = document.createElement('canvas'); 9 | this.canvas.width = 250; 10 | this.canvas.height = 250; 11 | this.ctx = this.canvas.getContext('2d'); 12 | 13 | this.legendDiv = document.createElement('div'); 14 | this.legendDiv.className = 'legend'; 15 | 16 | this.div.appendChild(this.canvas); 17 | this.div.appendChild(this.legendDiv); 18 | 19 | this.numPoints = 0; 20 | }; 21 | BasePieWidget.prototype = Object.create(BaseWidget.prototype); 22 | 23 | BasePieWidget.prototype.getElement = function() { 24 | return this.div; 25 | }; 26 | 27 | BasePieWidget.prototype.getWidthClass = function() { 28 | return "two"; 29 | }; 30 | 31 | /* 32 | * Override to specify a suffix on the values in the chart 33 | * such as MB, or kB/s 34 | */ 35 | BasePieWidget.prototype.getSuffix = function() { 36 | return ""; 37 | }; 38 | 39 | BasePieWidget.prototype.initialize = function(history) { 40 | this.addData(history); 41 | }; 42 | 43 | BasePieWidget.prototype.getChartData = function(data) { 44 | throw "Children must override"; 45 | }; 46 | 47 | BasePieWidget.prototype.addData = function(data) { 48 | var index = this.numPoints++; 49 | 50 | var data = this.getChartData(data); 51 | 52 | if (!data) { 53 | return; 54 | } 55 | 56 | this.chart && this.chart.destroy(); 57 | 58 | this.chart = new Chart(this.ctx).Doughnut(data, { 59 | animateScale: index === 0, 60 | animateRotate : index === 0, 61 | tooltipTemplate: "<%= fillColor %>|<%= label %>: <%= value %>" 62 | + this.getSuffix(), 63 | customTooltips: function(tooltip) { 64 | if (!tooltip) { 65 | $(".tooltip").hide(); 66 | return; 67 | } 68 | 69 | var textSplit = tooltip.text.split("|"); 70 | var color = textSplit[0]; 71 | 72 | tooltip.labels = [textSplit[1]]; 73 | // tooltip.datasetLabels = ['']; 74 | tooltip.colors = [color]; 75 | 76 | window.moveTooltip(tooltip); 77 | }.bind(this), 78 | legendTemplate: 79 | '
    -legend\">' + 80 | '<% for (var i=0; i' + 81 | '
  • ' + 82 | '\">' + 83 | '' + 84 | '<%=segments[i].value%>' + this.getSuffix() + 85 | ': <%=segments[i].label%>' + 86 | '
  • ' + 87 | '<%}%>' + 88 | '
' 89 | }); 90 | 91 | this.legendDiv.innerHTML = this.chart.generateLegend(); 92 | }; 93 | 94 | 95 | window.BasePieWidget = BasePieWidget; 96 | })(); 97 | -------------------------------------------------------------------------------- /public/js/widgets/basewidget.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var BaseWidget = function(widget) { 3 | this.div = document.createElement('div'); 4 | }; 5 | 6 | BaseWidget.prototype.getTitle = function() { 7 | return "Base Widget"; 8 | }; 9 | 10 | BaseWidget.prototype.getWidthClass = function() { 11 | return "one"; 12 | }; 13 | 14 | BaseWidget.prototype.getElement = function() { 15 | return this.div; 16 | }; 17 | 18 | BaseWidget.prototype.initialize = function(history) { 19 | }; 20 | 21 | BaseWidget.prototype.addData = function(data) { 22 | }; 23 | 24 | window.BaseWidget = BaseWidget; 25 | })(); 26 | -------------------------------------------------------------------------------- /public/js/widgets/cpuchart.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var CPUChartWidget = function(widget) { 3 | BaseChartWidget.call(this, widget); 4 | }; 5 | CPUChartWidget.prototype = Object.create(BaseChartWidget.prototype); 6 | 7 | CPUChartWidget.prototype.getTitle = function() { 8 | return "CPU Usage"; 9 | }; 10 | 11 | CPUChartWidget.prototype.getWidthClass = function() { 12 | return "three"; 13 | }; 14 | 15 | CPUChartWidget.prototype.getColors = function() { 16 | return ['#4CAF50', '#1B5E20']; 17 | }, 18 | 19 | CPUChartWidget.prototype.getFillColors = function() { 20 | return ['rgba(76,175,80,0.05)', 'rgba(27,94,32, 0.05)']; 21 | }; 22 | 23 | CPUChartWidget.prototype.getLabels = function() { 24 | return ['CPU Usage', 'CPU Usage Per Thread']; 25 | }; 26 | 27 | CPUChartWidget.prototype.getSuffix = function() { 28 | return "%"; 29 | }; 30 | 31 | CPUChartWidget.prototype.getPointsFromHistory = function(history) { 32 | var dataPoints = []; 33 | var normalizedDataPoints = []; 34 | var labels = []; 35 | 36 | history.cpu.forEach(function(data, index) { 37 | this.numPoints++; 38 | 39 | dataPoints.push(data.cpu.toFixed(2)); 40 | normalizedDataPoints.push(data.normalized.toFixed(2)); 41 | labels.push( 42 | index % 5 === 0 43 | ? moment(data.timestamp).format('h:mm:ss') 44 | : '' 45 | ); 46 | }, this); 47 | 48 | return { 49 | values: [dataPoints, normalizedDataPoints], 50 | labels: labels, 51 | }; 52 | }; 53 | 54 | CPUChartWidget.prototype.getValuesFromData = function(data) { 55 | if (!data.cpu) { 56 | return; 57 | } 58 | 59 | return [data.cpu.cpu.toFixed(2), data.cpu.normalized.toFixed(2)]; 60 | }; 61 | 62 | CPUChartWidget.prototype.getLabelFromData = function(data) { 63 | return moment(data.cpu.timestamp).format('h:mm:ss'); 64 | }; 65 | 66 | window.CPUChartWidget = CPUChartWidget; 67 | })(); 68 | -------------------------------------------------------------------------------- /public/js/widgets/cpuusage.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var CPUUsageWidget = function(widget) { 3 | BaseWidget.call(this, widget); 4 | this.topDiv = document.createElement('div'); 5 | this.bottomDiv = document.createElement('div'); 6 | this.div.appendChild(this.topDiv); 7 | this.div.appendChild(this.bottomDiv); 8 | }; 9 | CPUUsageWidget.prototype = Object.create(BaseWidget.prototype); 10 | 11 | CPUUsageWidget.prototype.getTitle = function() { 12 | return "CPU Load"; 13 | }; 14 | 15 | CPUUsageWidget.prototype.getWidthClass = function() { 16 | return "one"; 17 | }; 18 | 19 | CPUUsageWidget.prototype.initialize = function(history) { 20 | this.addData(history); 21 | }; 22 | 23 | CPUUsageWidget.prototype.addData = function(data) { 24 | if (!data.cpu) { 25 | return false; 26 | } 27 | 28 | if (data.cpu.length) { 29 | var cpu = data.cpu[data.cpu.length - 1].cpu; 30 | var normalized = data.cpu[data.cpu.length - 1].normalized; 31 | } else { 32 | cpu = data.cpu.cpu; 33 | normalized = data.cpu.normalized; 34 | } 35 | 36 | this.topDiv.innerText = 37 | "CPU Load: " + cpu.toFixed(2) + "%"; 38 | this.bottomDiv.innerText = 39 | "Normalized: " + normalized.toFixed(2) + "% per thread"; 40 | }; 41 | 42 | window.CPUUsageWidget = CPUUsageWidget; 43 | })(); 44 | -------------------------------------------------------------------------------- /public/js/widgets/freemem.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var FreememWidget = function(widget) { 3 | BaseChartWidget.call(this, widget); 4 | }; 5 | FreememWidget.prototype = Object.create(BaseChartWidget.prototype); 6 | 7 | FreememWidget.prototype.getTitle = function() { 8 | return "Used Memory"; 9 | }; 10 | 11 | FreememWidget.prototype.getWidthClass = function() { 12 | return "three"; 13 | }; 14 | 15 | FreememWidget.prototype.getColors = function() { 16 | return ['#1E88E5']; 17 | }, 18 | 19 | FreememWidget.prototype.getFillColors = function() { 20 | return ['rgba(30,136,229,0.05)']; 21 | }; 22 | 23 | FreememWidget.prototype.getLabels = function() { 24 | return ['Used Memory']; 25 | }; 26 | 27 | FreememWidget.prototype.getSuffix = function() { 28 | return "MB"; 29 | }; 30 | 31 | FreememWidget.prototype.getPointsFromHistory = function(history) { 32 | var dataPoints = [] 33 | var labels = []; 34 | history.memory.forEach(function(data, index) { 35 | this.numPoints++; 36 | 37 | dataPoints.push(Math.floor((data.total - data.free) / 1000000)); 38 | labels.push( 39 | index % 5 === 0 40 | ? moment(data.timestamp).format('h:mm:ss') 41 | : '' 42 | ); 43 | }, this); 44 | 45 | return { 46 | values: [dataPoints], 47 | labels: labels, 48 | }; 49 | }; 50 | 51 | FreememWidget.prototype.getValuesFromData = function(data) { 52 | if (!data.memory) { 53 | return; 54 | } 55 | 56 | return [Math.floor((data.memory.total - data.memory.free) / 1000000)]; 57 | }; 58 | 59 | FreememWidget.prototype.getLabelFromData = function(data) { 60 | return moment(data.memory.timestamp).format('h:mm:ss'); 61 | }; 62 | 63 | window.FreememWidget = FreememWidget; 64 | })(); 65 | -------------------------------------------------------------------------------- /public/js/widgets/memory.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var MemoryWidget = function(widget) { 3 | BaseWidget.call(this, widget); 4 | }; 5 | MemoryWidget.prototype = Object.create(BaseWidget.prototype); 6 | 7 | MemoryWidget.prototype.getTitle = function() { 8 | return "Used Memory"; 9 | }; 10 | 11 | MemoryWidget.prototype.getWidthClass = function() { 12 | return "one"; 13 | }; 14 | 15 | MemoryWidget.prototype.initialize = function(history) { 16 | this.addData({ 17 | memory: history.memory[history.memory.length - 1] 18 | }); 19 | }; 20 | 21 | MemoryWidget.prototype.addData = function(data) { 22 | if (!data.memory) { 23 | return; 24 | } 25 | 26 | var usedMemMB = 27 | Math.floor((data.memory.total - data.memory.free) / 1000000); 28 | var totalMemMB = Math.floor(data.memory.total / 1000000); 29 | 30 | this.div.innerText = 31 | "Used Memory: " + usedMemMB + "MB / " + totalMemMB + "MB"; 32 | }; 33 | 34 | window.MemoryWidget = MemoryWidget; 35 | })(); 36 | -------------------------------------------------------------------------------- /public/js/widgets/network.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var NetworkWidget = function(widget) { 3 | BaseChartWidget.call(this, widget); 4 | }; 5 | NetworkWidget.prototype = Object.create(BaseChartWidget.prototype); 6 | 7 | NetworkWidget.prototype.getTitle = function() { 8 | return "Network Activity"; 9 | }; 10 | 11 | NetworkWidget.prototype.getWidthClass = function() { 12 | return "three"; 13 | }; 14 | 15 | NetworkWidget.prototype.getColors = function() { 16 | return ['#F44336', '#B71C1C']; 17 | }, 18 | 19 | NetworkWidget.prototype.getFillColors = function() { 20 | return ['rgba(244,67,54,0.05)', 'rgba(183,28,28,0.05)']; 21 | }; 22 | 23 | NetworkWidget.prototype.getLabels = function() { 24 | return ['Incoming Speed', 'Outgoing Speed']; 25 | }; 26 | 27 | NetworkWidget.prototype.getSuffix = function() { 28 | return "kB/s"; 29 | }; 30 | 31 | NetworkWidget.prototype.getPointsFromHistory = function(history) { 32 | var inDataPoints = []; 33 | var outDataPoints = []; 34 | var labels = []; 35 | 36 | history.network.forEach(function(data, index) { 37 | this.numPoints++; 38 | 39 | inDataPoints.push(Math.floor(data.inSpeed / 1000)); 40 | outDataPoints.push(Math.floor(data.outSpeed / 1000)); 41 | labels.push( 42 | index % 5 === 0 43 | ? moment(data.timestamp).format('h:mm:ss') 44 | : '' 45 | ); 46 | }, this); 47 | 48 | return { 49 | values: [inDataPoints, outDataPoints], 50 | labels: labels, 51 | }; 52 | }; 53 | 54 | NetworkWidget.prototype.getValuesFromData = function(data) { 55 | if (!data.network) { 56 | return; 57 | } 58 | 59 | return [ 60 | Math.floor(data.network.inSpeed / 1000), 61 | Math.floor(data.network.outSpeed / 1000) 62 | ]; 63 | }; 64 | 65 | NetworkWidget.prototype.getLabelFromData = function(data) { 66 | return moment(data.network.timestamp).format('h:mm:ss'); 67 | }; 68 | 69 | window.NetworkWidget = NetworkWidget; 70 | })(); 71 | -------------------------------------------------------------------------------- /public/js/widgets/processcpu.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var COLORS = ['#F44336', '#3F51B5', '#4CAF50', '#00BCD4', '#FF9800']; 3 | 4 | var ProcessCPUWidget = function(widget) { 5 | BasePieWidget.call(this, widget); 6 | }; 7 | ProcessCPUWidget.prototype = Object.create(BasePieWidget.prototype); 8 | 9 | ProcessCPUWidget.prototype.getTitle = function() { 10 | return "Process CPU Usage"; 11 | }; 12 | 13 | ProcessCPUWidget.prototype.getSuffix = function() { 14 | return "% CPU"; 15 | }; 16 | 17 | ProcessCPUWidget.prototype.getChartData = function(data) { 18 | if (!data.processes) { 19 | return false; 20 | } 21 | 22 | var processes = data.processes; 23 | 24 | processes.sort(function(a, b) { 25 | return b.cpu - a.cpu; 26 | }); 27 | 28 | processes = processes.slice(0, 5); 29 | 30 | var chartData = []; 31 | processes.forEach(function(process, index) { 32 | chartData.push({ 33 | value: process.cpu, 34 | color: COLORS[index], 35 | label: process.name, 36 | }); 37 | }); 38 | 39 | return chartData; 40 | }; 41 | 42 | window.ProcessCPUWidget = ProcessCPUWidget; 43 | })(); 44 | -------------------------------------------------------------------------------- /public/js/widgets/processmemory.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var COLORS = ['#F44336', '#3F51B5', '#4CAF50', '#00BCD4', '#FF9800']; 3 | 4 | var ProcessMemoryWidget = function(widget) { 5 | BasePieWidget.call(this, widget); 6 | }; 7 | ProcessMemoryWidget.prototype = Object.create(BasePieWidget.prototype); 8 | 9 | ProcessMemoryWidget.prototype.getTitle = function() { 10 | return "Process Memory Usage"; 11 | }; 12 | 13 | ProcessMemoryWidget.prototype.getSuffix = function() { 14 | return "% Memory"; 15 | }; 16 | 17 | ProcessMemoryWidget.prototype.getChartData = function(data) { 18 | if (!data.processes) { 19 | return false; 20 | } 21 | 22 | var processes = data.processes; 23 | 24 | processes.sort(function(a, b) { 25 | return b.mem - a.mem; 26 | }); 27 | 28 | processes = processes.slice(0, 5); 29 | 30 | var chartData = []; 31 | processes.forEach(function(process, index) { 32 | chartData.push({ 33 | value: process.mem, 34 | color: COLORS[index], 35 | label: process.name, 36 | }); 37 | }); 38 | 39 | return chartData; 40 | }; 41 | 42 | window.ProcessMemoryWidget = ProcessMemoryWidget; 43 | })(); 44 | -------------------------------------------------------------------------------- /public/js/widgets/temps.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var TempsWidget = function (widget) { 3 | BaseWidget.call(this, widget); 4 | 5 | this.divs = []; 6 | 7 | // this.topDiv = document.createElement('div'); 8 | // this.bottomDiv = document.createElement('div'); 9 | // this.div.appendChild(this.topDiv); 10 | // this.div.appendChild(this.bottomDiv); 11 | }; 12 | TempsWidget.prototype = Object.create(BaseWidget.prototype); 13 | 14 | TempsWidget.prototype.getTitle = function () { 15 | return "Temperature Sensors"; 16 | }; 17 | 18 | TempsWidget.prototype.getWidthClass = function () { 19 | return "one"; 20 | }; 21 | 22 | TempsWidget.prototype.initialize = function (history) { 23 | this.addData(history); 24 | }; 25 | 26 | TempsWidget.prototype.addData = function (data) { 27 | if (!data.temps) { 28 | return false; 29 | } 30 | 31 | // const latestData = data.temps[data.temps.length - 1]; 32 | 33 | console.log(data.temps); 34 | 35 | // TODO we might wanna support removing divs if the number of sensors goes 36 | // down somehow during runtime. 37 | if (this.divs.length < data.temps.length) { 38 | const numDivsToCreate = data.temps.length - this.divs.length; 39 | for (let x = 0; x < numDivsToCreate; x++) { 40 | const div = document.createElement('div'); 41 | this.div.appendChild(div); 42 | this.divs.push(div); 43 | } 44 | } 45 | 46 | data.temps.forEach((tempData, idx) => { 47 | const tempC = tempData.temp / 1000; 48 | this.divs[idx].innerText = `${tempData.type}: ${tempC}°C`; 49 | }); 50 | }; 51 | 52 | window.TempsWidget = TempsWidget; 53 | })(); 54 | -------------------------------------------------------------------------------- /public/js/widgets/uptime.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var UptimeWidget = function(widget) { 3 | BaseWidget.call(this, widget); 4 | }; 5 | UptimeWidget.prototype = Object.create(BaseWidget.prototype); 6 | 7 | UptimeWidget.prototype.getTitle = function() { 8 | return "Uptime Information"; 9 | }; 10 | 11 | UptimeWidget.prototype.getWidthClass = function() { 12 | return "one"; 13 | }; 14 | 15 | UptimeWidget.prototype.getElement = function() { 16 | return this.div; 17 | }; 18 | 19 | UptimeWidget.prototype.initialize = function(history) { 20 | this.addData(history); 21 | }; 22 | 23 | UptimeWidget.prototype.addData = function(data) { 24 | var uptimeMilli = data.uptime; 25 | var duration = moment.duration(uptimeMilli); 26 | 27 | var years = duration.years(); 28 | var months = duration.months(); 29 | var days = duration.days(); 30 | var hours = duration.hours(); 31 | var minutes = duration.minutes(); 32 | var seconds = duration.seconds(); 33 | 34 | var times = [ 35 | [years, 'year'], 36 | [months, 'month'], 37 | [days, 'day'], 38 | [hours, 'hour'], 39 | [minutes, 'minute'], 40 | [seconds, 'second'], 41 | ]; 42 | 43 | var display = ""; 44 | for (var x = 0; x < times.length; x++) { 45 | var time = times[x]; 46 | if (time[0]) { 47 | display += time[0] + ' ' + time[1] + 48 | (time[0] !== 1 ? 's' : '') + 49 | (x !== times.length - 1 ? ', ' : ''); 50 | } 51 | } 52 | 53 | this.div.innerText = display; 54 | }; 55 | 56 | window.UptimeWidget = UptimeWidget; 57 | })(); 58 | --------------------------------------------------------------------------------