Repository: tariqbuilds/linux-dash
Branch: master
Commit: 186a802ea76e
Files: 57
Total size: 94.5 KB
Directory structure:
gitextract_txcbka2j/
├── .gitignore
├── LICENSE.md
├── README.md
├── app/
│ ├── index.html
│ └── server/
│ ├── config/
│ │ └── ping_hosts
│ ├── index.go
│ ├── index.js
│ ├── index.php
│ ├── index.py
│ └── linux_json_api.sh
├── bin/
│ └── linux-dash
├── demo.js
├── ecosystem.config.js
├── gulpfile.js
├── index.html
├── package.json
└── src/
├── css/
│ ├── README.md
│ ├── buttons.css
│ ├── main.css
│ ├── plugins-cotnainer.css
│ └── tables.css
└── js/
├── README.md
├── core/
│ ├── app.js
│ ├── features/
│ │ ├── key-value-list/
│ │ │ ├── key-value-list.directive.js
│ │ │ └── key-value-list.html
│ │ ├── line-chart/
│ │ │ ├── line-chart-plugin.directive.js
│ │ │ └── line-chart-plugin.html
│ │ ├── loader/
│ │ │ ├── loader.css
│ │ │ └── loader.directive.js
│ │ ├── multi-line-chart/
│ │ │ ├── multi-line-chart-plugin.directive.js
│ │ │ └── multi-line-chart-plugin.html
│ │ ├── navbar/
│ │ │ ├── navbar.css
│ │ │ └── navbar.directive.js
│ │ ├── plugin/
│ │ │ ├── plugin.css
│ │ │ ├── plugin.directive.js
│ │ │ └── plugin.html
│ │ ├── progress-bar/
│ │ │ ├── progress-bar-plugin.directive.js
│ │ │ └── progress-bar.css
│ │ ├── table-data/
│ │ │ ├── table-data.css
│ │ │ ├── table-data.directive.js
│ │ │ └── table-data.html
│ │ └── top-bar/
│ │ ├── topbar.css
│ │ └── topbar.directive.js
│ ├── rootscope-event-handlers/
│ │ ├── hide-plugin.run.js
│ │ └── make-plugins-draggable.run.js
│ ├── routes.js
│ └── server.service.js
└── plugins/
├── README.md
├── cpu-avg-load-chart.directive.js
├── cpu-temp.directive.js
├── cpu-utilization-chart.directive.js
├── disk-space/
│ ├── disk-space.directive.js
│ └── disk-space.html
├── download-transfer-rate-chart.directive.js
├── ram-chart.directive.js
├── simple-table-data-plugins.directive.js
└── upload-transfer-rate-chart.directive.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
*.log
*/linuxDash.min.js.map
temp
================================================
FILE: LICENSE.md
================================================
The MIT License (MIT)
Copyright (c) 2014 afaqurk
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
<h1 align="center">
<a href="https://afaqurk.github.io/linux-dash">
<img src="https://raw.githubusercontent.com/afaqurk/screenshots/master/linux-dash/v2.0-logo.png"/>
</a>
</h1>
<p align="center">
<sub>v2.0</sub><br/>
<small>A simple & low-overhead web dashboard for linux systems</small>
</p>
<p align="center">
<small>
<a href="https://afaqurk.github.io/linux-dash">Demo</a> |
<a href="https://github.com/afaqurk/linux-dash/wiki">
Docs
</a>
</small>
</p>
<p align="center">
<a href="https://gitter.im/afaqurk/linux-dash">
<img
src="https://badges.gitter.im/gitterHQ/gitter.png"
alt="linux-dash Gitter chat">
</a>
</p>
<br/>
## Features
* **Small** ----- Under 400KB on disk _(with .git removed)!_
* **Simple** ---- A minimalist, beautiful dashboard
* **Easy** ------ Drop-in installation
* **Versatile** -- Choose your stack from Node.js, Go, Python, PHP
## Installation
### Step 1
```sh
## 1. clone the repo
git clone --depth 1 https://github.com/afaqurk/linux-dash.git
## 2. go to the cloned directory
cd linux-dash/app/server
```
OR, if you prefer to download manually:
```sh
## 1. Download the .zip
curl -LOk https://github.com/afaqurk/linux-dash/archive/master.zip && unzip master.zip
## 2. navigate to downloaded & unzipped dir
cd linux-dash-master/app/server
```
### Step 2
See instructions for preferred server linux-dash server _(all included)_:
* [Node.js](#if-using-nodejs) _(recommended)_
* [Go](#if-using-go)
* [Python](#if-using-python)
* [PHP](#if-using-php)
#### If Using Node.js
```sh
## install dependencies
npm install --production
## start linux-dash (on port 80 by default; may require sudo)
## You may change this with the `LINUX_DASH_SERVER_PORT` environment variable (eg. `LINUX_DASH_SERVER_PORT=8080 node server`)
## or provide a --port flag to the command below
## Additionally, the server will listen on every network interface (`0.0.0.0`).
## You may change this with the `LINUX_DASH_SERVER_HOST` environment variable (eg. `LINUX_DASH_SERVER_HOST=127.0.0.1 node server`)
## or provide a --host flag to the command below
node index.js
```
#### If Using Go
```sh
## start the server (on port 80 by default; may require sudo)
go run index.go
```
To build a binary, run `go build && ./server -h`. See [@tehbilly](https://github.com/sergeifilippov)'s notes [here](https://github.com/afaqurk/linux-dash/pull/281) for binary usage options
#### If Using Python
```sh
# Start the server (on port 80 by default; may require sudo).
python index.py
```
#### If Using PHP
1. Make sure you have the `exec`, `shell_exec`, and `escapeshellarg` functions enabled
2. Point your web server to `app/` directory under `linux-dash`
2. Restart your web server (Apache, nginx, etc.)
- For PHP + Apache setup follow the [Digital Ocean tutorial](https://www.digitalocean.com/community/tutorials/how-to-install-linux-dash-on-ubuntu-14-04).
- For help with nginx setup, see [this gist](https://gist.github.com/sergeifilippov/8909839) by [@sergeifilippov](https://github.com/sergeifilippov).
## Support
For general help, please use the [Gitter chat room](https://gitter.im/afaqurk/linux-dash).
## Security
**It is strongly recommended** that all linux-dash installations be protected via a security measure of your choice.
Linux Dash does not provide any security or authentication features.
================================================
FILE: app/index.html
================================================
<!DOCTYPE html>
<html lang="en" ng-app="linuxDash">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Linux Dash : Simple, beautiful server monitoring web dashboard</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Monitor your Linux server through a simple web dashboard. Open source and free!">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href='//fonts.googleapis.com/css?family=Merriweather:300italic,300|Open+Sans:400,600' rel='stylesheet' type='text/css'>
<link href='linuxDash.min.css' rel='stylesheet' type='text/css'>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<nav-bar></nav-bar>
<!-- Templates Get Rendered Here -->
<div id="plugins" ng-view></div>
<!-- Javascript-->
<script src="linuxDash.min.js" type="text/javascript"></script>
</body>
</html>
================================================
FILE: app/server/config/ping_hosts
================================================
google.com
yahoo.com
twitter.com
================================================
FILE: app/server/index.go
================================================
package main
import (
"bytes"
"flag"
"fmt"
"net/http"
"os"
"os/exec"
)
var (
listenAddress = flag.String("listen", "0.0.0.0:80", "Where the server listens for connections. [interface]:port")
staticPath = flag.String("static", "../", "Location of static files.")
)
func init() {
flag.Parse()
}
func main() {
http.Handle("/", http.FileServer(http.Dir(*staticPath)))
http.HandleFunc("/server/", func(w http.ResponseWriter, r *http.Request) {
module := r.URL.Query().Get("module")
if module == "" {
http.Error(w, "No module specified, or requested module doesn't exist.", 406)
return
}
// Execute the command
cmd := exec.Command("./linux_json_api.sh", module)
var output bytes.Buffer
cmd.Stdout = &output
err := cmd.Run()
if err != nil {
fmt.Printf("Error executing '%s': %s\n\tScript output: %s\n", module, err.Error(), output.String())
http.Error(w, "Unable to execute module.", http.StatusInternalServerError)
return
}
w.Write(output.Bytes())
})
fmt.Println("Starting http server at:", *listenAddress)
err := http.ListenAndServe(*listenAddress, nil)
if err != nil {
fmt.Println("Error starting http server:", err)
os.Exit(1)
}
}
================================================
FILE: app/server/index.js
================================================
var express = require('express')
var app = require('express')()
var server = require('http').Server(app)
var path = require('path')
var spawn = require('child_process').spawn
var fs = require('fs')
var ws = require('websocket').server
var args = require('yargs').argv
var host = args.host || process.env.LINUX_DASH_SERVER_HOST || '0.0.0.0'
var port = args.port || process.env.LINUX_DASH_SERVER_PORT || 80
server.listen(port, host, function() {
console.log('Linux Dash Server Started on ' + host + ':' + port + '!');
})
app.use(express.static(path.resolve(__dirname + '/../')))
app.get('/', function (req, res) {
res.sendFile(path.resolve(__dirname + '/../index.html'))
})
app.get('/websocket', function (req, res) {
res.send({
websocket_support: true,
})
})
wsServer = new ws({
httpServer: server
})
var nixJsonAPIScript = __dirname + '/linux_json_api.sh'
function getPluginData(pluginName, callback) {
var command = spawn(nixJsonAPIScript, [ pluginName, '' ])
var output = []
command.stdout.on('data', function(chunk) {
output.push(chunk.toString())
})
command.on('close', function (code) {
callback(code, output)
})
}
wsServer.on('request', function(request) {
var wsClient = request.accept('', request.origin)
wsClient.on('message', function(wsReq) {
var moduleName = wsReq.utf8Data
var sendDataToClient = function(code, output) {
if (code === 0) {
var wsResponse = '{ "moduleName": "' + moduleName + '", "output": "'+ output.join('') +'" }'
wsClient.sendUTF(wsResponse)
}
}
getPluginData(moduleName, sendDataToClient)
})
})
app.get('/server/', function (req, res) {
var respondWithData = function(code, output) {
if (code === 0) res.send(output.toString())
else res.sendStatus(500)
}
getPluginData(req.query.module, respondWithData)
})
================================================
FILE: app/server/index.php
================================================
<?php
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Pragma: no-cache");
$shell_file = dirname(__FILE__) . '/linux_json_api.sh';
$module = escapeshellcmd($_GET['module']);
echo stripcslashes(shell_exec( $shell_file . " " . $module ));
================================================
FILE: app/server/index.py
================================================
#!/usr/bin/env python
from __future__ import print_function
import os
import sys
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer, test as _test
import subprocess
from SocketServer import ThreadingMixIn
import argparse
parser = argparse.ArgumentParser(description='Simple Threaded HTTP server to run linux-dash.')
parser.add_argument('--port', metavar='PORT', type=int, nargs='?', default=80,
help='Port to run the server on.')
modulesSubPath = '/server/linux_json_api.sh'
appRootPath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
pass
class MainHandler(BaseHTTPRequestHandler):
def do_GET(self):
try:
data = ''
contentType = 'text/html'
if self.path.startswith("/server/"):
module = self.path.split('=')[1]
output = subprocess.Popen(
appRootPath + modulesSubPath + " " + module,
shell = True,
stdout = subprocess.PIPE)
data = output.communicate()[0]
else:
if self.path == '/':
self.path = 'index.html'
f = open(appRootPath + os.sep + self.path)
data = f.read()
if self.path.startswith('/linuxDash.min.css'):
contentType = 'text/css'
f.close()
self.send_response(200)
self.send_header('Content-type', contentType)
self.end_headers()
self.wfile.write(data)
except IOError:
self.send_error(404, 'File Not Found: %s' % self.path)
if __name__ == '__main__':
args = parser.parse_args()
server = ThreadedHTTPServer(('0.0.0.0', args.port), MainHandler)
print('Starting server, use <Ctrl-C> to stop')
server.serve_forever()
================================================
FILE: app/server/linux_json_api.sh
================================================
#!/bin/bash
ECHO=$(type -P echo)
SED=$(type -P sed)
GREP=$(type -P grep)
TR=$(type -P tr)
AWK=$(type -P awk)
CAT=$(type -P cat)
HEAD=$(type -P head)
CUT=$(type -P cut)
PS=$(type -P ps)
_parseAndPrint() {
while read data; do
$ECHO -n "$data" | $SED -r 's/\\//g' | $TR -d "\n";
done;
}
arp_cache() {
local arpCommand=$(type -P arp)
result=$($arpCommand | $AWK 'BEGIN {print "["} NR>1 \
{print "{ \"addr\": \"" $1 "\", " \
"\"hw_type\": \"" $2 "\", " \
"\"hw_addr.\": \"" $3 "\", " \
"\"mask\": \"" $5 "\" }, " \
} \
END {print "]"}' \
| $SED 'N;$s/},/}/;P;D')
if [ -z "$result" ]; then
$ECHO {}
else
$ECHO $result | _parseAndPrint
fi
}
bandwidth() {
$CAT /proc/net/dev \
| $AWK 'BEGIN {print "["} NR>2 {print "{ \"interface\": \"" $1 "\"," \
" \"tx\": " $2 "," \
" \"rx\": " $10 " }," } END {print "]"}' \
| $SED 'N;$s/,\n/\n/;P;D' \
| _parseAndPrint
}
common_applications() {
result=$(whereis php node mysql mongo vim python ruby java apache2 nginx openssl vsftpd make \
| $AWK -F: '{if(length($2)==0) { installed="false"; } else { installed="true"; } \
print \
"{ \
\"binary\": \""$1"\", \
\"location\": \""$2"\", \
\"installed\": "installed" \
},"}')
$ECHO "[" ${result%?} "]" | _parseAndPrint
}
cpu_info() {
local lscpuCommand=$(type -P lscpu)
result=$($lscpuCommand \
| $AWK -F: '{print "\""$1"\": \""$2"\"," } '\
)
$ECHO "{" ${result%?} "}" | _parseAndPrint
}
cpu_intensive_processes() {
result=$($PS axo pid,user,pcpu,rss,vsz,comm --sort -pcpu,-rss,-vsz \
| $HEAD -n 15 \
| $AWK 'BEGIN{OFS=":"} NR>1 {print "{ \"pid\": " $1 \
", \"user\": \"" $2 "\"" \
", \"cpu%\": " $3 \
", \"rss\": " $4 \
", \"vsz\": " $5 \
", \"cmd\": \"" $6 "\"" "},"\
}')
$ECHO "[" ${result%?} "]" | _parseAndPrint
}
cpu_temp() {
local ID=*
[ -f /etc/os-release ] && source /etc/os-release
case "$ID" in
"raspbian")
cpu=$(</sys/class/thermal/thermal_zone0/temp)
echo "$((cpu/1000))" | _parseAndPrint
;;
*)
if type -P sensors 2>/dev/null; then
returnString=`sensors`
#amd
if [[ "${returnString/"k10"}" != "${returnString}" ]] ; then
$ECHO ${returnString##*k10} | $CUT -d ' ' -f 6 | $CUT -c 2- | $CUT -c 1-4
#intel
elif [[ "${returnString/"core"}" != "${returnString}" ]] ; then
fromcore=${returnString##*"coretemp"}
$ECHO ${fromcore##*Physical} | $CUT -d ' ' -f 3 | $CUT -c 2-5 | _parseAndPrint
fi
else
$ECHO "[]" | _parseAndPrint
fi
;;
esac
}
# by Paul Colby (http://colby.id.au), no rights reserved ;)
cpu_utilization() {
PREV_TOTAL=0
PREV_IDLE=0
iteration=0
while [[ iteration -lt 2 ]]; do
# Get the total CPU statistics, discarding the 'cpu ' prefix.
CPU=(`$SED -n 's/^cpu\s//p' /proc/stat`)
IDLE=${CPU[3]} # Just the idle CPU time.
# Calculate the total CPU time.
TOTAL=0
for VALUE in "${CPU[@]}"; do
let "TOTAL=$TOTAL+$VALUE"
done
# Calculate the CPU usage since we last checked.
let "DIFF_IDLE=$IDLE-$PREV_IDLE"
let "DIFF_TOTAL=$TOTAL-$PREV_TOTAL"
let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10"
#echo -en "\rCPU: $DIFF_USAGE% \b\b"
# Remember the total and idle CPU times for the next check.
PREV_TOTAL="$TOTAL"
PREV_IDLE="$IDLE"
# Wait before checking again.
sleep 1
iteration="$iteration+1"
done
$ECHO -en "$DIFF_USAGE"
}
cron_history() {
local cronLog='/var/log/syslog'
local numberOfLines='50'
# Month, Day, Time, Hostname, tag, user,
result=$($GREP -m $numberOfLines CRON $cronLog \
| $AWK '{ s = ""; for (i = 6; i <= NF; i++) s = s $i " "; \
print "{\"time\" : \"" $1" "$2" "$3 "\"," \
"\"user\" : \"" $6 "\"," \
"\"message\" : \"" $5" "gensub("\"", "\\\\\"", "g", s) "\"" \
"},"
}'
)
$ECHO [${result%?}] | _parseAndPrint
}
current_ram() {
local memInfoFile="/proc/meminfo"
# References:
# Calculations: http://zcentric.com/2012/05/29/mapping-procmeminfo-to-output-of-free-command/
# Fields: https://www.kernel.org/doc/Documentation/filesystems/proc.txt
memInfo=$($CAT $memInfoFile | $GREP 'MemTotal\|MemFree\|Buffers\|Cached')
$ECHO $memInfo | $AWK '{print "{ \"total\": " ($2/1024) ", \"used\": " ( ($2-($5+$8+$11))/1024 ) ", \"available\": " (($5+$8+$11)/1024) " }" }' | _parseAndPrint
}
disk_partitions() {
local dfCommand=$(type -P df)
result=$($dfCommand -Ph | $AWK 'NR>1 {print "{\"file_system\": \"" $1 "\", \"size\": \"" $2 "\", \"used\": \"" $3 "\", \"avail\": \"" $4 "\", \"used%\": \"" $5 "\", \"mounted\": \"" $6 "\"},"}')
$ECHO [ ${result%?} ] | _parseAndPrint
}
docker_processes() {
local result=""
local dockerCommand=$(type -P docker)
local containers="$($dockerCommand ps | $AWK '{if(NR>1) print $NF}')"
for i in $containers; do
result="$result $($dockerCommand top $i axo pid,user,pcpu,pmem,comm --sort -pcpu,-pmem \
| $HEAD -n 15 \
| $AWK -v cnt="$i" 'BEGIN{OFS=":"} NR>1 {print "{ \"cname\": \"" cnt \
"\", \"pid\": " $1 \
", \"user\": \"" $2 "\"" \
", \"cpu%\": " $3 \
", \"mem%\": " $4 \
", \"cmd\": \"" $5 "\"" "},"\
}')"
done
$ECHO "[" ${result%?} "]" | _parseAndPrint
}
download_transfer_rate() {
local files=(/sys/class/net/*)
local pos=$(( ${#files[*]} - 1 ))
local last=${files[$pos]}
local json_output="{"
for interface in "${files[@]}"
do
basename=$(basename "$interface")
# find the number of bytes transfered for this interface
in1=$($CAT /sys/class/net/"$basename"/statistics/rx_bytes)
# wait a second
sleep 1
# check same interface again
in2=$($CAT /sys/class/net/"$basename"/statistics/rx_bytes)
# get the difference (transfer rate)
in_bytes=$((in2 - in1))
# convert transfer rate to KB
in_kbytes=$((in_bytes / 1024))
# convert transfer rate to KB
json_output="$json_output \"$basename\": $in_kbytes"
# if it is not the last line
if [[ ! $interface == $last ]]
then
# add a comma to the line (JSON formatting)
json_output="$json_output,"
fi
done
# close the JSON object & print to screen
$ECHO "$json_output}" | _parseAndPrint
}
general_info() {
local lsbRelease=$(type -P lsb_release)
local uName=$(type -P uname)
local hostName=$(type -P hostname)
function displaytime {
local T=$1
local D=$((T/60/60/24))
local H=$((T/60/60%24))
local M=$((T/60%60))
local S=$((T%60))
[[ $D > 0 ]] && printf '%d days ' $D
[[ $H > 0 ]] && printf '%d hours ' $H
[[ $M > 0 ]] && printf '%d minutes ' $M
[[ $D > 0 || $H > 0 || $M > 0 ]] && printf 'and '
printf '%d seconds\n' $S
}
local lsbRelease=$($lsbRelease -ds | $SED -e 's/^"//' -e 's/"$//')
local uname=$($uName -r | $SED -e 's/^"//' -e 's/"$//')
local os=$($ECHO $lsbRelease $uname)
local hostname=$($hostName)
local uptime_seconds=$($CAT /proc/uptime | awk '{print $1}')
local server_time=$(date)
$ECHO "{ \"OS\": \"$os\", \"Hostname\": \"$hostname\", \"Uptime\": \" $(displaytime ${uptime_seconds%.*}) \", \"Server Time\": \"$server_time\" }" | _parseAndPrint
}
io_stats() {
result=$($CAT /proc/diskstats | $AWK \
'{ if($4==0 && $8==0 && $12==0 && $13==0) next } \
{print "{ \"device\": \"" $3 "\", \"reads\": \""$4"\", \"writes\": \"" $8 "\", \"in_prog.\": \"" $12 "\", \"time\": \"" $13 "\"},"}'
)
$ECHO [ ${result%?} ] | _parseAndPrint
}
ip_addresses() {
local ifconfigCmd=$(type -P ifconfig)
local digCmd=$(type -P dig)
externalIp=$($digCmd +short myip.opendns.com @resolver1.opendns.com)
$ECHO -n "["
for item in $($ifconfigCmd | $GREP -oP "^[a-zA-Z0-9:]*(?=:)")
do
$ECHO -n "{\"interface\" : \""$item"\", \"ip\" : \"$( $ifconfigCmd $item | $GREP "inet" | $AWK '{match($0,"inet (addr:)?([0-9.]*)",a)}END{ if (NR != 0){print a[2]; exit}{print "none"}}')\"}, "
done
$ECHO "{ \"interface\": \"external\", \"ip\": \"$externalIp\" } ]" | _parseAndPrint
}
load_avg() {
local numberOfCores=$($GREP -c 'processor' /proc/cpuinfo)
if [ $numberOfCores -eq 0 ]; then
numberOfCores=1
fi
result=$($CAT /proc/loadavg | $AWK '{print "{ \"1_min_avg\": " ($1*100)/'$numberOfCores' ", \"5_min_avg\": " ($2*100)/'$numberOfCores' ", \"15_min_avg\": " ($3*100)/'$numberOfCores' "}," }')
$ECHO ${result%?} | _parseAndPrint
}
logged_in_users() {
local whoCommand=$(type -P w)
result=$(COLUMNS=300 $whoCommand -h | $AWK '{print "{\"user\": \"" $1 "\", \"from\": \"" $3 "\", \"when\": \"" $4 "\"},"}')
$ECHO [ ${result%?} ] | _parseAndPrint
}
memcached() {
local ncCommand=$(type -P nc)
$ECHO "stats" \
| $ncCommand -w 1 127.0.0.1 11211 \
| $GREP 'bytes' \
| $AWK 'BEGIN {print "{"} {print "\"" $2 "\": " $3 } END {print "}"}' \
| $TR '\r' ',' \
| $SED 'N;$s/,\n/\n/;P;D' \
| _parseAndPrint
}
memory_info() {
$CAT /proc/meminfo \
| $AWK -F: 'BEGIN {print "{"} {print "\"" $1 "\": \"" $2 "\"," } END {print "}"}' \
| $SED 'N;$s/,\n/\n/;P;D' \
| _parseAndPrint
}
network_connections() {
local netstatCmd=$(type -P netstat)
local sortCmd=$(type -P sort)
local uniqCmd=$(type -P uniq)
$netstatCmd -ntu \
| $AWK 'NR>2 {print $5}' \
| $sortCmd \
| $uniqCmd -c \
| $AWK 'BEGIN {print "["} {print "{ \"connections\": " $1 ", \"address\": \"" $2 "\" }," } END {print "]"}' \
| $SED 'N;$s/},/}/;P;D' \
| _parseAndPrint
}
number_of_cpu_cores() {
local numberOfCPUCores=$($GREP -c 'model name' /proc/cpuinfo)
if [ -z $numberOfCPUCores ]; then
echo "cannot be found";
fi
}
# http://askubuntu.com/questions/413367/ping-multiple-ips-using-bash
ping() {
# get absolute path to config file
local SCRIPTPATH=$(dirname $(readlink -f $0))
local CONFIG_PATH=$SCRIPTPATH"/config/ping_hosts"
local pingCmd=$(type -P ping)
local numOfLinesInConfig=$($SED -n '$=' $CONFIG_PATH)
local result='['
$CAT $CONFIG_PATH \
| while read output
do
singlePing=$($pingCmd -qc 2 $output \
| $AWK -F/ 'BEGIN { endLine="}," } /^rtt/ { if ('$numOfLinesInConfig'==1){endLine="}"} print "{" "\"host\": \"'$output'\", \"ping\": " $5 " " endLine }' \
)
numOfLinesInConfig=$(($numOfLinesInConfig-1))
result=$result$singlePing
if [ $numOfLinesInConfig -eq 0 ]
then
$ECHO $result"]"
fi
done \
| $SED 's/\},]/}]/g' \
| _parseAndPrint
}
pm2_stats() {
#get data
local data="$(pm2 list)"
local tailCommand=$(type -P tail)
#only process data if variable has a length
#this should handle cases where pm2 is not installed
if [ -n "$data" ]; then
#start processing data on line 4
#don't process last 2 lines
json=$( $ECHO "$data" | $tailCommand -n +4 | $HEAD -n +2 \
| $AWK '{print "{"}\
{print "\"appName\":\"" $2 "\","} \
{print "\"id\":\"" $4 "\","} \
{print "\"mode\":\"" $6 "\","} \
{print "\"pid\":\"" $8 "\","}\
{print "\"status\":\"" $10 "\","}\
{print "\"restart\":\"" $12 "\","}\
{print "\"uptime\":\"" $14 "\","}\
{print "\"memory\":\"" $16 $17 "\","}\
{print "\"watching\":\"" $19 "\""}\
{print "},"}')
#make sure to remove last comma and print in array
$ECHO "[" ${json%?} "]" | _parseAndPrint
else
#no data found
$ECHO "[]" | _parseAndPrint
fi
}
ram_intensive_processes() {
local psCommand=$(type -P ps)
result=$($psCommand axo pid,user,pmem,rss,vsz,comm --sort -pmem,-rss,-vsz \
| $HEAD -n 15 \
| $AWK 'NR>1 {print "{ \"pid\": " $1 \
", \"user\": \"" $2 \
"\", \"mem%\": " $3 \
", \"rss\": " $4 \
", \"vsz\": " $5 \
", \"cmd\": \"" $6 \
"\"},"}')
$ECHO [ ${result%?} ] | _parseAndPrint
}
recent_account_logins() {
local lastLogCommand=$(type -p lastlog)
result=$($lastLogCommand -t 365 \
| $AWK 'NR>1 {\
print "{ \
\"user\": \"" $1 "\", \
\"ip\": \"" $3 "\","" \
\"date\": \"" $5" "$6" "$7" "$8" "$9 "\"},"
}'
)
$ECHO [ ${result%?} ] | _parseAndPrint
}
redis() {
########### Enter Your Redis Password HERE #########
local redisPassword=''
########### Enter Your Redis Password HERE #########
local redisCommand=$(type -P redis-cli);
if [ -n "$redisPassword" ]; then
redisCommand="$redisCommand -a $redisPassword"
fi
result=$($redisCommand INFO \
| $GREP 'redis_version\|connected_clients\|connected_slaves\|used_memory_human\|total_connections_received\|total_commands_processed' \
| $AWK -F: '{print "\"" $1 "\":" "\"" $2 }' \
| $TR '\r' '"' | $TR '\n' ','
)
$ECHO { ${result%?} } | _parseAndPrint
}
scheduled_crons() {
######
# Credit: http://stackoverflow.com/questions/134906/how-do-i-list-all-cron-jobs-for-all-users#answer-137173
######
local egrepCmd=$(type -P egrep)
local crontabCmd=$(type -P crontab)
# System-wide crontab file and cron job directory. Change these for your system.
CRONTAB='/etc/crontab'
CRONDIR='/etc/cron.d'
# Single tab character. Annoyingly necessary.
tab=$($ECHO -en "\t")
# Given a stream of crontab lines, exclude non-cron job lines, replace
# whitespace characters with a single space, and remove any spaces from the
# beginning of each line.
function clean_cron_lines() {
while read line ; do
$ECHO "${line}" |
$egrepCmd --invert-match '^($|\s*#|\s*[[:alnum:]_]+=)' |
$SED --regexp-extended "s/\s+/ /g" |
$SED --regexp-extended "s/^ //"
done;
}
# Given a stream of cleaned crontab lines, $echoCmd any that don't include the
# run-parts command, and for those that do, show each job file in the run-parts
# directory as if it were scheduled explicitly.
function lookup_run_parts() {
while read line ; do
match=$($ECHO "${line}" | $egrepCmd -o 'run-parts (-{1,2}\S+ )*\S+')
if [[ -z "${match}" ]] ; then
$echoCmd "${line}"
else
cron_fields=$($ECHO "${line}" | $CUT -f1-6 -d' ')
cron_job_dir=$($ECHO "${match}" | $AWK '{print $NF}')
if [[ -d "${cron_job_dir}" ]] ; then
for cron_job_file in "${cron_job_dir}"/* ; do # */ <not a comment>
[[ -f "${cron_job_file}" ]] && $ECHO "${cron_fields} ${cron_job_file}"
done
fi
fi
done;
}
# Temporary file for crontab lines.
temp=$(mktemp) || exit 1
# Add all of the jobs from the system-wide crontab file.
$CAT "${CRONTAB}" | clean_cron_lines | lookup_run_parts >"${temp}"
# Add all of the jobs from the system-wide cron directory.
$CAT "${CRONDIR}"/* | clean_cron_lines >>"${temp}" # */ <not a comment>
# Add each user's crontab (if it exists). Insert the user's name between the
# five time fields and the command.
while read user ; do
$crontabCmd -l -u "${user}" 2>/dev/null |
clean_cron_lines |
$SED --regexp-extended "s/^((\S+ +){5})(.+)$/\1${user} \3/" >>"${temp}"
done < <($CUT --fields=1 --delimiter=: /etc/passwd)
# Output the collected crontab lines.
## Changes: Parses output into JSON
$CAT "${temp}" \
| $AWK 'BEGIN {print "["} \
{print "{ \"min\": \"" $1 \
"\", \"hrs\": \"" $2 "\", " \
" \"day\": \"" $3 "\", " \
" \"month\": \"" $4 "\", " \
" \"wkday\": \"" $5 "\", " \
" \"user\": \"" $6 "\", " \
" \"CMD\": \""} \
{for(i=7;i<=NF;++i) printf("%s ", gensub("\"", "\\\\\"", "g", $i) ) } \
{print "\" " \
"}," } \
END {print "]"}' \
| $SED 'N;$s/,\n//;P;D' \
| _parseAndPrint
rm --force "${temp}"
}
swap() {
local wcCmd=$(which wc)
local swapLineCount=$($CAT /proc/swaps | $wcCmd -l)
if [ "$swapLineCount" -gt 1 ]; then
result=$($CAT /proc/swaps \
| $AWK 'NR>1 {print "{ \"filename\": \"" $1"\", \"type\": \""$2"\", \"size\": \""$3"\", \"used\": \""$4"\", \"priority\": \""$5"\"}," }'
)
$ECHO [ ${result%?} ] | _parseAndPrint
else
$ECHO [] | _parseAndPrint
fi
}
upload_transfer_rate() {
local files=(/sys/class/net/*)
local pos=$(( ${#files[*]} - 1 ))
local last=${files[$pos]}
local json_output="{"
for interface in "${files[@]}"
do
basename=$(basename "$interface")
# find the number of bytes transfered for this interface
out1=$($CAT /sys/class/net/"$basename"/statistics/tx_bytes)
# wait a second
sleep 1
# check same interface again
out2=$($CAT /sys/class/net/"$basename"/statistics/tx_bytes)
# get the difference (transfer rate)
out_bytes=$((out2 - out1))
# convert transfer rate to KB
out_kbytes=$((out_bytes / 1024))
# convert transfer rate to KB
json_output="$json_output \"$basename\": $out_kbytes"
# if it is not the last line
if [[ ! $interface == $last ]]
then
# add a comma to the line (JSON formatting)
json_output="$json_output,"
fi
done
# close the JSON object & print to screen
$ECHO "$json_output}" | _parseAndPrint
}
user_accounts() {
result=$($AWK -F: '{ \
if ($3<=499){userType="system";} \
else {userType="user";} \
print "{ \"type\": \"" userType "\"" ", \"user\": \"" $1 "\", \"home\": \"" $6 "\" }," }' < /etc/passwd
)
length=$($ECHO ${#result})
if [ $length -eq 0 ]; then
result=$(getent passwd | $AWK -F: '{ if ($3<=499){userType="system";} else {userType="user";} print "{ \"type\": \"" userType "\"" ", \"user\": \"" $1 "\", \"home\": \"" $6 "\" }," }')
fi
$ECHO [ ${result%?} ] | _parseAndPrint
}
fnCalled="$1"
# Check if the function call is indeed a function.
if [ -n "$(type -t $fnCalled)" ] && [ "$(type -t $fnCalled)" = function ]; then
${fnCalled}
else
echo '{\"success\":false,\"status\":\"Invalid module\"}'
fi
================================================
FILE: bin/linux-dash
================================================
#!/usr/bin/env node
require('../app/server/index.js')
================================================
FILE: demo.js
================================================
angular
.module('linuxDashDemo', ['linuxDash', 'ngMockE2E'])
.run(function($httpBackend) {
// signal to not use websockets for demo
$httpBackend.whenGET('/websocket').respond(false)
/** System Status */
$httpBackend.whenGET('server/?module=current_ram').respond(function () {
return [200, {total: 512, used: 200, available: 312 }]
})
$httpBackend.whenGET('server/?module=ram_intensive_processes').respond(function () {
return [200, [ { 'pid': 2782, 'user': 'user1', 'mem%': 2.2, 'rss': 232332, 'vsz': 1118240, 'cmd': 'nginx'}, { 'pid': 2623, 'user': 'user1', 'mem%': 2.0, 'rss': 209732, 'vsz': 1321800, 'cmd': 'nginx'}, { 'pid': 1558, 'user': 'user1', 'mem%': 1.3, 'rss': 140796, 'vsz': 1321400, 'cmd': 'gnome-software'}, { 'pid': 5026, 'user': 'user1', 'mem%': 1.2, 'rss': 126312, 'vsz': 875072, 'cmd': 'nginx'}, { 'pid': 5051, 'user': 'user1', 'mem%': 1.1, 'rss': 112776, 'vsz': 872512, 'cmd': 'nginx'}, { 'pid': 1477, 'user': 'user1', 'mem%': 1.0, 'rss': 108248, 'vsz': 1471548, 'cmd': 'compiz'}, { 'pid': 2997, 'user': 'user1', 'mem%': 1.0, 'rss': 107208, 'vsz': 869076, 'cmd': 'nginx'}, { 'pid': 2298, 'user': 'root', 'mem%': 1.0, 'rss': 101356, 'vsz': 286772, 'cmd': 'aptd'}, { 'pid': 2700, 'user': 'user1', 'mem%': 0.9, 'rss': 95784, 'vsz': 460272, 'cmd': 'nginx'}, { 'pid': 2799, 'user': 'user1', 'mem%': 0.8, 'rss': 83940, 'vsz': 778512, 'cmd': 'nginx'}, { 'pid': 4248, 'user': 'user1', 'mem%': 0.8, 'rss': 82276, 'vsz': 1059420, 'cmd': 'redis'}, { 'pid': 1095, 'user': 'root', 'mem%': 0.7, 'rss': 78424, 'vsz': 457156, 'cmd': 'Xorg'}, { 'pid': 2810, 'user': 'user1', 'mem%': 0.7, 'rss': 72884, 'vsz': 750856, 'cmd': 'nginx'}, { 'pid': 2809, 'user': 'user1', 'mem%': 0.7, 'rss': 72872, 'vsz': 752908, 'cmd': 'nginx'} ]]
})
$httpBackend.whenGET('server/?module=cpu_intensive_processes').respond(function () {
return [200, [ { 'pid': 5026, 'user': 'user1', 'cpu%': 25.5, 'rss': 137944, 'vsz': 907236, 'cmd': 'nginx'}, { 'pid': 2623, 'user': 'user1', 'cpu%': 9.5, 'rss': 229016, 'vsz': 1327948, 'cmd': 'nginx'}, { 'pid': 2700, 'user': 'user1', 'cpu%': 8.8, 'rss': 106324, 'vsz': 474796, 'cmd': 'nginx'}, { 'pid': 4248, 'user': 'user1', 'cpu%': 8.8, 'rss': 96848, 'vsz': 1151460, 'cmd': 'sublime_text'}, { 'pid': 1477, 'user': 'user1', 'cpu%': 8.6, 'rss': 111252, 'vsz': 1476524, 'cmd': 'compiz'}, { 'pid': 1571, 'user': 'user1', 'cpu%': 7.7, 'rss': 62984, 'vsz': 551884, 'cmd': 'orca'}, { 'pid': 1095, 'user': 'root', 'cpu%': 6.2, 'rss': 86048, 'vsz': 457112, 'cmd': 'Xorg'}, { 'pid': 1535, 'user': 'user1', 'cpu%': 3.9, 'rss': 17152, 'vsz': 574624, 'cmd': 'pulseaudio'}, { 'pid': 5051, 'user': 'user1', 'cpu%': 1.6, 'rss': 131828, 'vsz': 888712, 'cmd': 'nginx'}, { 'pid': 2890, 'user': 'user1', 'cpu%': 1.1, 'rss': 52968, 'vsz': 657408, 'cmd': 'unity-panel-ser'}, { 'pid': 2782, 'user': 'user1', 'cpu%': 1.0, 'rss': 226536, 'vsz': 1110556, 'cmd': 'nginx'}, { 'pid': 1438, 'user': 'user1', 'cpu%': 0.9, 'rss': 45840, 'vsz': 660352, 'cmd': 'hud-service'}, { 'pid': 1631, 'user': 'user1', 'cpu%': 0.9, 'rss': 26208, 'vsz': 514684, 'cmd': 'indicator-multi'}, { 'pid': 1804, 'user': 'user1', 'cpu%': 0.9, 'rss': 9752, 'vsz': 523936, 'cmd': 'sd_espeak'} ]]
})
$httpBackend.whenGET('server/?module=disk_partitions').respond(function () {
return [200, [ {'file_system': 'udev', 'size': '4.9G', 'used': '0', 'avail': '4.9G', 'used%': '0%', 'mounted': '/dev'}, {'file_system': '/dev/sda1', 'size': '449G', 'used': '224.5G', 'avail': '224.5G', 'used%': '50%', 'mounted': '/'}, {'file_system': 'tmpfs', 'size': '5.0M', 'used': '4.0K', 'avail': '5.0M', 'used%': '1%', 'mounted': '/run/lock'} ]]
})
$httpBackend.whenGET('server/?module=cpu_temp').respond(function () {
return [200, [ ]]
})
$httpBackend.whenGET('server/?module=cpu_temp').respond(function () {
return [200, [ ]]
})
$httpBackend.whenGET('server/?module=cpu_utilization').respond(function () {
return [200, 25]
})
$httpBackend.whenGET('server/?module=load_avg').respond(function () {
return [200, { '1_min_avg': 40, '5_min_avg': 22, '15_min_avg': 10}]
})
$httpBackend.whenGET('server/?module=docker_processes').respond(function () {
return [200, []]
})
$httpBackend.whenGET('server/?module=swap').respond(function () {
return [200, [ { 'filename': '/dev/sda5', 'type': 'partition', 'size': '10364924', 'used': '0', 'priority': '-1'} ]]
})
/** Basic Info **/
$httpBackend.whenGET('server/?module=cron_history').respond(function () {
return [200, []]
})
$httpBackend.whenGET('server/?module=io_stats').respond(function () {
return [200, [ { 'device': 'sda', 'reads': '75406', 'writes': '119939', 'in_prog.': '0', 'time': '816604'}, { 'device': 'sda1', 'reads': '74587', 'writes': '81983', 'in_prog.': '0', 'time': '796964'}, { 'device': 'sda2', 'reads': '6', 'writes': '0', 'in_prog.': '0', 'time': '304'}, { 'device': 'sda5', 'reads': '73', 'writes': '0', 'in_prog.': '0', 'time': '3344'}, { 'device': 'sdb', 'reads': '276', 'writes': '0', 'in_prog.': '0', 'time': '72'}, { 'device': 'sdb1', 'reads': '234', 'writes': '0', 'in_prog.': '0', 'time': '64'} ]]
})
$httpBackend.whenGET('server/?module=cpu_info').respond(function () {
return [200, { "Architecture": " x86_64", "CPU op-mode(s)": " 32-bit, 64-bit", "Byte Order": " Little Endian", "CPU(s)": " 4", "On-line CPU(s) list": " 0-3", "Thread(s) per core": " 2", "Core(s) per socket": " 2", "Socket(s)": " 1", "NUMA node(s)": " 1", "Vendor ID": " GenuineIntel", "CPU family": " 6", "Model": " 69", "Model name": " Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz", "Stepping": " 1", "CPU MHz": " 1402.101", "CPU max MHz": " 2600.0000", "CPU min MHz": " 800.0000", "BogoMIPS": " 4589.14", "Virtualization": " VT-x", "L1d cache": " 32K", "L1i cache": " 32K", "L2 cache": " 256K", "L3 cache": " 3072K", "NUMA node0 CPU(s)": " 0-3", "Flags": " fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts" }]
})
$httpBackend.whenGET('server/?module=scheduled_crons').respond(function () {
return [200, [ ]]
})
$httpBackend.whenGET('server/?module=memory_info').respond(function () {
return [200, {'MemTotal': '10120412 kB','MemFree': '7257156 kB','MemAvailable': '8173888 kB','Buffers': ' 257824 kB','Cached': ' 1175940 kB','SwapCached': ' 0 kB','Active': ' 1815224 kB','Inactive': ' 783844 kB','Active(anon)': '1174832 kB','Inactive(anon)': ' 334816 kB','Active(file)': ' 640392 kB','Inactive(file)': ' 449028 kB','Unevictable': ' 384 kB','Mlocked': ' 384 kB','SwapTotal': ' 10364924 kB','SwapFree': '10364924 kB','Dirty': ' 156 kB','Writeback': ' 0 kB','AnonPages': '1165736 kB','Mapped': '420416 kB','Shmem': ' 344344 kB','Slab': ' 143556 kB','SReclaimable': ' 109592 kB','SUnreclaim': ' 33964 kB','KernelStack': ' 8560 kB','PageTables': ' 32700 kB','NFS_Unstable': ' 0 kB','Bounce': ' 0 kB','WritebackTmp': ' 0 kB','CommitLimit': '15425128 kB','Committed_AS': '5073516 kB','VmallocTotal': ' 34359738367 kB','VmallocUsed': '0 kB','VmallocChunk': ' 0 kB','HardwareCorrupted': ' 0 kB','AnonHugePages': '352256 kB','CmaTotal': '0 kB','CmaFree': ' 0 kB','HugePages_Total': '0','HugePages_Free': ' 0','HugePages_Rsvd': ' 0','HugePages_Surp': ' 0','Hugepagesize': '2048 kB','DirectMap4k': ' 130800 kB','DirectMap2M': ' 4993024 kB','DirectMap1G': ' 6291456 kB'}]
})
$httpBackend.whenGET('server/?module=general_info').respond(function () {
return [200, { 'OS': 'Ubuntu 16.04.2 LTS 4.4.0-77-generic', 'Hostname': 'webserver-prod-983742', 'Uptime': '5 hours and 32 minutes and 27 seconds ', 'Server Time': 'Sat May 13 00:11:39 EST 2017' }]
})
/** Network **/
$httpBackend.whenGET('server/?module=download_transfer_rate').respond(function () {
return [200, { "lo": 2048, "wlp3s0": 1024 }]
})
$httpBackend.whenGET('server/?module=upload_transfer_rate').respond(function () {
return [200, { "lo": 1024, "wlp3s0": 512 }]
})
$httpBackend.whenGET('server/?module=bandwidth').respond(function () {
return [200, [{ 'interface': 'wlp3s0:', 'tx': 226076758, 'rx': 9037438 },{ 'interface': 'lo:', 'tx': 12016995, 'rx': 12016995 }]]
})
$httpBackend.whenGET('server/?module=ping').respond(function () {
return [200, [{'host': 'google.com', 'ping': 23.234 },{'host': 'yahoo.com', 'ping': 67.412 },{'host': 'twitter.com', 'ping': 34.560 }]]
})
$httpBackend.whenGET('server/?module=ip_addresses').respond(function () {
return [200, [{ 'interface': 'external', 'ip': '70.113.122.2' } ]]
})
$httpBackend.whenGET('server/?module=network_connections').respond(function () {
return [200, [{ 'connections': 1, 'address': '127.0.0.1:48562' }
,{ 'connections': 1, 'address': '127.0.0.1:48564' }
,{ 'connections': 1, 'address': '127.0.0.1:48708' }
,{ 'connections': 3, 'address': '127.0.0.1:8080' }
,{ 'connections': 1, 'address': '192.241.178.140:443' }
,{ 'connections': 2, 'address': '2657:f9b0:9000:802::443' }
,{ 'connections': 1, 'address': '2657:f9b0:0000:80c::443' }
,{ 'connections': 1, 'address': '2657:f9b0:0000:80f:::80' }
,{ 'connections': 2, 'address': '2657:f9b0:0000:816:::80' }
,{ 'connections': 1, 'address': '2657:f9b0:0003:c09:5228' }]]
})
$httpBackend.whenGET('server/?module=arp_cache').respond(function () {
return [200, [ { 'addr': '192.168.0.1', 'hw_type': 'ether', 'hw_addr.': '15:db:45:eb:4d:6a', 'mask': 'wlp3s0' } ]]
})
$httpBackend.whenGET('server/?module=logged_in_users').respond(function () {
return [200, [ {'user': 'user1', 'from': ':0', 'when': 'Fri23'}, {'user': 'user1', 'from': 'webserver-prod-983742', 'when': 'Fri23'}, {'user': 'user1', 'from': 'webserver-prod-983742', 'when': 'Fri23'}, {'user': 'user1', 'from': 'webserver-prod-983742', 'when': '01:19'} ]]
})
$httpBackend.whenGET('server/?module=user_accounts').respond(function () {
return [200, [ {"type":"system", "user":"root", "home":"/root"}, {"type":"system", "user":"daemon", "home":"/usr/sbin"}, {"type":"system", "user":"bin", "home":"/bin"}, {"type":"system", "user":"sys", "home":"/dev"}, {"type":"system", "user":"sync", "home":"/bin"}, {"type":"system", "user":"games", "home":"/usr/games"}, {"type":"system", "user":"man", "home":"/var/cache/man"}, {"type":"system", "user":"lp", "home":"/var/spool/lpd"}, {"type":"system", "user":"mail", "home":"/var/mail"}, {"type":"system", "user":"news", "home":"/var/spool/news"}, {"type":"system", "user":"uucp", "home":"/var/spool/uucp"}, {"type":"system", "user":"proxy", "home":"/bin"}, {"type":"system", "user":"www-data", "home":"/var/www"}, {"type":"system", "user":"backup", "home":"/var/backups"}, {"type":"system", "user":"list", "home":"/var/list"}, {"type":"system", "user":"irc", "home":"/var/run/ircd"}, {"type":"system", "user":"gnats", "home":"/var/lib/gnats"}, {"type":"user", "user":"nobody", "home":"/nonexistent"}, {"type":"system", "user":"systemd-timesync", "home":"/run/systemd"} ]]
})
$httpBackend.whenGET('server/?module=recent_account_logins').respond(function () {
return [200, [ {"type":"system", "user":"root", "home":"/root"}, {"type":"system", "user":"daemon", "home":"/usr/sbin"}, {"type":"system", "user":"bin", "home":"/bin"}, {"type":"system", "user":"sys", "home":"/dev"}, {"type":"system", "user":"sync", "home":"/bin"}, {"type":"system", "user":"games", "home":"/usr/games"}, {"type":"system", "user":"man", "home":"/var/cache/man"}, {"type":"system", "user":"lp", "home":"/var/spool/lpd"}, {"type":"system", "user":"mail", "home":"/var/mail"}, {"type":"system", "user":"news", "home":"/var/spool/news"}, {"type":"system", "user":"uucp", "home":"/var/spool/uucp"}, {"type":"system", "user":"proxy", "home":"/bin"}, {"type":"system", "user":"www-data", "home":"/var/www"}, {"type":"system", "user":"backup", "home":"/var/backups"}, {"type":"system", "user":"list", "home":"/var/list"}, {"type":"system", "user":"irc", "home":"/var/run/ircd"}, {"type":"system", "user":"gnats", "home":"/var/lib/gnats"}, {"type":"user", "user":"nobody", "home":"/nonexistent"}, {"type":"system", "user":"systemd-timesync", "home":"/run/systemd"} ]]
})
/** Applications **/
$httpBackend.whenGET('server/?module=common_applications').respond(function () {
return [200, [ { "binary": "php", "location": "", "installed": "installed" }, { "binary": "node", "location": " /usr/bin/node /usr/include/node /usr/share/man/man1/node.1.gz", "installed": "installed" }, { "binary": "mysql", "location": "", "installed": "installed" }, { "binary": "mongo", "location": "", "installed": "installed" }, { "binary": "vim", "location": " /usr/bin/vim.basic /usr/bin/vim /usr/bin/vim.tiny /etc/vim /usr/share/vim /usr/share/man/man1/vim.1.gz", "installed": "installed" }, { "binary": "python", "location": " /usr/bin/python3.5 /usr/bin/python /usr/bin/python2.7-config /usr/bin/python3.5m /usr/bin/python2.7 /usr/lib/python3.5 /usr/lib/python2.7 /etc/python3.5 /etc/python /etc/python2.7 /usr/local/lib/python3.5 /usr/local/lib/python2.7 /usr/include/python3.5m /usr/include/python2.7 /usr/share/python /usr/share/man/man1/python.1.gz", "installed": "installed" }, { "binary": "ruby", "location": "", "installed": "installed" }, { "binary": "java", "location": " /usr/share/java", "installed": "installed" }, { "binary": "apache2", "location": "", "installed": "installed" }, { "binary": "nginx", "location": "", "installed": "installed" }, { "binary": "openssl", "location": " /usr/bin/openssl /usr/share/man/man1/openssl.1ssl.gz", "installed": "installed" }, { "binary": "vsftpd", "location": "", "installed": "installed" }, { "binary": "make", "location": " /usr/bin/make /usr/share/man/man1/make.1.gz", "installed": "installed" } ]]
})
$httpBackend.whenGET('server/?module=memcached').respond(function () { return [200, {}] })
$httpBackend.whenGET('server/?module=redis').respond(function () { return [200, {}] })
$httpBackend.whenGET('server/?module=pm2_stats').respond(function () { return [200, []] })
})
================================================
FILE: ecosystem.config.js
================================================
module.exports = {
apps : [{
name: 'Linux Dash',
script: './app/server/index.js',
watch: false,
env: {
"LINUX_DASH_SERVER_PORT": 2800
},
}],
};
================================================
FILE: gulpfile.js
================================================
var g = require('gulp')
var concat = require('gulp-concat')
var uglify = require('gulp-uglify')
var cssmin = require('gulp-cssmin')
var gutil = require('gulp-util')
var ngAnnotate = require('gulp-ng-annotate')
var templateCache = require('gulp-angular-templatecache')
g.task('template-cache', function () {
return g.src('src/**/*.html')
.pipe(templateCache('templates.js', {
module: 'linuxDash',
standAlone: false,
root: 'src/'
}))
.pipe(g.dest('temp/'))
})
g.task('generate-js-dist', ['template-cache'], function () {
return g.src([
'node_modules/angular/angular.min.js',
'node_modules/angular-route/angular-route.min.js',
'node_modules/smoothie/smoothie.js',
'node_modules/sortablejs/Sortable.min.js',
'src/js/**/*.js',
'temp/templates.js'
])
.pipe(concat('linuxDash.min.js'))
.pipe(ngAnnotate())
// .pipe(uglify())
.on('error', gutil.log)
.pipe(g.dest('app/'))
})
g.task('generate-css-dist', function () {
return g.src([ 'src/**/*.css' ])
.pipe(cssmin())
.pipe(concat('linuxDash.min.css'))
.pipe(g.dest('app/'))
})
g.task('build', [
'generate-js-dist',
'generate-css-dist'
])
g.task('watch', function () {
g.watch('src/**/*.css', ['generate-css-dist'])
g.watch(['src/**/*.js', 'src/**/*.html'], ['generate-js-dist'])
})
g.task('default', ['build', 'watch'])
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en" ng-app="linuxDashDemo">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Linux Dash : Simple, beautiful server monitoring web dashboard</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Monitor your Linux server through a simple web dashboard. Open source and free!">
<meta name="apple-mobile-web-app-capable" content="yes">
<link href='//fonts.googleapis.com/css?family=Merriweather:300italic,300|Open+Sans:400,600' rel='stylesheet' type='text/css'>
<link href='app/linuxDash.min.css' rel='stylesheet' type='text/css'>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="https://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Javascript-->
</head>
<body>
<nav-bar></nav-bar>
<!-- Templates Get Rendered Here -->
<div id="plugins" ng-view></div>
<script src="app/linuxDash.min.js" type="text/javascript"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular-mocks.js"></script>
<script src="demo.js" type="text/javascript"></script>
</body>
</html>
================================================
FILE: package.json
================================================
{
"name": "linux-dash",
"version": "2.0.0",
"description": "A simple, low overhead web dashboard for linux.",
"main": "app/server/index.js",
"bin": "bin/linux-dash",
"repository": {
"type": "git",
"url": "https://github.com/afaqurk/linux-dash.git"
},
"keywords": [
"linux",
"gnu",
"dashboard",
"monitor",
"server"
],
"author": "Afaq Tariq",
"license": "MIT",
"bugs": {
"url": "https://github.com/afaqurk/linux-dash/issues"
},
"scripts": {
"start": "gulp"
},
"homepage": "https://github.com/afaqurk/linux-dash",
"dependencies": {
"express": "^4.11.1",
"websocket": "^1.0.23",
"yargs": "^8.0.1"
},
"devDependencies": {
"angular": "1.3.4",
"angular-route": "1.3.4",
"del": "^2.2.0",
"gulp": "^3.9.1",
"gulp-angular-templatecache": "^1.8.0",
"gulp-concat": "^2.6.0",
"gulp-cssmin": "^0.1.7",
"gulp-ng-annotate": "^2.0.0",
"gulp-uglify": "^1.5.3",
"gulp-util": "^3.0.7",
"smoothie": "^1.27.0",
"sortablejs": "^1.4.2"
}
}
================================================
FILE: src/css/README.md
================================================
# Global CSS
These are CSS files which set global & base element style rules.
## main.css
Styles for body & html tags.
## plugins-container.css
All plugins rendered on a page are inside of this element.
## tables.css
Base table element styling.
Also contains styling for the `metrics-table` class, which is used to display the tabular data underneath the line and multiline charts.
================================================
FILE: src/css/buttons.css
================================================
button {
outline: none;
}
================================================
FILE: src/css/main.css
================================================
html {
margin-top: 0px;
padding-top: 0;
-webkit-transition: all 1s ease;
-moz-transition: all 1s ease;
-ms-transition: all 1s ease;
-o-transition: all 1s ease;
transition: all 1s ease;
}
body {
letter-spacing: .1rem;
font-family: 'Merriweather', Arial, sans-serif;
padding: 0;
margin: 0;
}
.centered {
margin: 0 auto;
}
================================================
FILE: src/css/plugins-cotnainer.css
================================================
#plugins {
padding-top: 0;
margin-top: 0;
border: 1px;
margin-left: 10%;
}
@media (min-width: 1080px) {
#plugins {
}
}
@media (max-width: 1079px) {
#plugins {
}
}
================================================
FILE: src/css/tables.css
================================================
table {
width: 100%;
font-size: 10px;
margin: 0;
border-collapse: collapse;
text-align: left;
table-layout:fixed;
}
table th,
table td {
padding: 2px;
max-width: 250px;
word-wrap: break-word;
}
table th{
font-weight: 600;
text-transform: uppercase;
border-bottom: 1px solid #f1f1f1;
}
table td {
border-bottom: 1px solid #f1f1f1;
font-family: Arial, sans-serif;
font-size: 10px;
color: rgba(0,0,0,.65);
}
table tbody tr:hover td {
background-color: #fafafa;
}
table.metrics-table {
text-align: center;
}
table.metrics-table tr:last-child td {
border: none;
}
================================================
FILE: src/js/README.md
================================================
# JS (UI)
The root directory for _all_ of the UI JavaScript source files which power Linux Dash
It is an Angular JS (v1.3) SPA
## core/
Contains all of the directives, services, and module declaration for the Angular SPA
This is where you can find all of the base UI elements that power the UI
For example, in this directory, you will find the base `line-chart` directive used by several plugins, the global `navbar` for Linux Dash pages, and other base UI elements.
## plugins/
Contains the code for the individual plugins the user interacts with on the Linux Dash UI.
================================================
FILE: src/js/core/app.js
================================================
function runFn(server, $location, $rootScope) {
server.checkIfWebsocketsAreSupported()
$rootScope.$on("$locationChangeSuccess", function(event, next, current) {
var nextRoute = next.split('#')[1]
if (nextRoute !== '/loading') {
localStorage.setItem('currentTab', nextRoute)
}
});
$location.path('/loading')
}
angular
.module('linuxDash', ['ngRoute'])
.run([ 'server', '$location', '$rootScope', runFn])
.config(['$compileProvider', function ($compileProvider) {
$compileProvider.debugInfoEnabled(false)
}])
================================================
FILE: src/js/core/features/key-value-list/key-value-list.directive.js
================================================
angular.module('linuxDash').directive('keyValueList', ['server', '$rootScope', function (server, $rootScope) {
return {
scope: {
heading: '@',
info: '@',
moduleName: '@',
},
templateUrl: 'src/js/core/features/key-value-list/key-value-list.html',
link: function(scope, element) {
scope.getData = function() {
delete scope.tableRows
server.get(scope.moduleName, function(serverResponseData) {
scope.tableRows = serverResponseData
scope.lastGet = new Date().getTime()
if (Object.keys(serverResponseData).length === 0) {
scope.emptyResult = true
}
if (!scope.$$phase && !$rootScope.$$phase) scope.$digest()
})
}
scope.getData()
}
}
}])
================================================
FILE: src/js/core/features/key-value-list/key-value-list.html
================================================
<plugin
heading="{{ heading }}"
last-updated="lastGet"
on-refresh="getData()"
info="{{ info }}">
<loader ng-if="!tableRows"></loader>
<div ng-show="tableRows">
<table class="key-value-list">
<tbody>
<tr ng-repeat="(name, value) in tableRows">
<td><strong>{{ name }}</strong></td>
<td>{{ value }}</td>
</tr>
</tbody>
</table>
</div>
<span ng-show="emptyResult">No data</span>
</plugin>
================================================
FILE: src/js/core/features/line-chart/line-chart-plugin.directive.js
================================================
angular.module('linuxDash').directive('lineChartPlugin', [
'$interval', '$compile', 'server', '$window',
function ($interval, $compile, server, $window) {
return {
scope: {
heading: '@',
moduleName: '@',
refreshRate: '=',
maxValue: '=',
minValue: '=',
getDisplayValue: '=',
metrics: '=',
color: '@'
},
templateUrl: 'src/js/core/features/line-chart/line-chart-plugin.html',
link: function(scope, element) {
scope.initializing = true
// wrap the entire plugin into an initializing function
var start_rendering_line_chart = function () {
if (!scope.color)
scope.color = '0, 255, 0'
var series, w, h, canvas
angular.element($window).bind('resize', function() {
canvas.width = w
canvas.height = h
})
// smoothieJS - Create new chart
var chart = new SmoothieChart({
borderVisible: false,
sharpLines: true,
grid: {
fillStyle: '#ffffff',
strokeStyle: 'rgba(232,230,230,0.93)',
sharpLines: true,
millisPerLine: 3000,
borderVisible: false
},
labels: {
fontSize: 11,
precision: 0,
fillStyle: '#0f0e0e'
},
maxValue: parseInt(scope.maxValue),
minValue: parseInt(scope.minValue),
horizontalLines: [{
value: 5,
color: '#eff',
lineWidth: 1
}]
})
var initializeChart = function () {
// smoothieJS - set up canvas element for chart
var checkForCanvasReadyState = $interval(function () {
if (element.find('canvas')[0]) {
canvas = element.find('canvas')[0]
series = series || new TimeSeries()
w = canvas.width
h = canvas.height
if (chart.seriesSet.length > 0)
chart.removeTimeSeries(chart.seriesSet[0].timeSeries)
chart.addTimeSeries(series, {
strokeStyle: 'rgba(' + scope.color + ', 1)',
fillStyle: 'rgba(' + scope.color + ', 0.2)',
lineWidth: 2
})
chart.streamTo(canvas, 1000)
$interval.cancel(checkForCanvasReadyState)
}
}, 100)
}
scope.reInitializeChart = function () {
initializeChart()
}
if (!scope.isHidden)
initializeChart()
var dataCallInProgress = false
// update data on chart
scope.getData = function() {
if(scope.initializing)
scope.initializing = false
if (dataCallInProgress || !element.find('canvas')[0]) return
dataCallInProgress = true
server.get(scope.moduleName, function(serverResponseData) {
if (serverResponseData.length < 1) {
scope.emptyResult = true
return
}
dataCallInProgress = false
scope.lastGet = new Date().getTime()
// change graph colour depending on usage
if (scope.maxValue / 4 * 3 < scope.getDisplayValue(serverResponseData)) {
chart.seriesSet[0].options.strokeStyle = 'rgba(255, 89, 0, 1)'
chart.seriesSet[0].options.fillStyle = 'rgba(255, 89, 0, 0.2)'
} else if (scope.maxValue / 3 < scope.getDisplayValue(serverResponseData)) {
chart.seriesSet[0].options.strokeStyle = 'rgba(255, 238, 0, 1)'
chart.seriesSet[0].options.fillStyle = 'rgba(255, 238, 0, 0.2)'
} else {
chart.seriesSet[0].options.strokeStyle = 'rgba(' + scope.color + ', 1)'
chart.seriesSet[0].options.fillStyle = 'rgba(' + scope.color + ', 0.2)'
}
scope.newData = scope.getDisplayValue(serverResponseData)
// update chart with this response
series.append(scope.lastGet, scope.newData)
// update the metrics for this chart
scope.metrics.forEach(function(metricObj) {
metricObj.data = metricObj.generate(serverResponseData)
})
})
}
// set the directive-provided interval
// at which to run the chart update
var intervalRef = $interval(scope.getData, scope.refreshRate)
var removeInterval = function() {
$interval.cancel(intervalRef)
}
element.on("$destroy", removeInterval)
}
// only start rendering plugin when we know the scale of max/min for the canvas chart (smoothie)
var stopWatching = scope.$watch('maxValue', function (n, o) {
if (n) {
start_rendering_line_chart()
stopWatching()
}
})
}
}
}
])
================================================
FILE: src/js/core/features/line-chart/line-chart-plugin.html
================================================
<plugin chart-plugin>
<loader ng-if="!maxValue || initializing"></loader>
<canvas
ng-show="!initializing && !emptyResult"
class="canvas"
width="400"
height="150">
</canvas>
<table ng-show="!initializing && !emptyResult" border="0" class="metrics-table">
<tbody>
<tr ng-repeat="metric in metrics">
<td><strong>{{ metric.name }}</strong></td>
<td>{{ metric.data }}</td>
</tr>
</tbody>
</table>
<span ng-show="emptyResult">No data</span>
</plugin>
================================================
FILE: src/js/core/features/loader/loader.css
================================================
.spinner {
margin: 100px auto;
width: 50px;
height: 30px;
text-align: center;
font-size: 10px;
}
.spinner > div {
background-color: #009587;
height: 100%;
width: 6px;
display: inline-block;
-webkit-animation: stretchdelay 1.2s infinite ease-in-out;
animation: stretchdelay 1.2s infinite ease-in-out;
}
.spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spinner .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
@-webkit-keyframes stretchdelay {
0%, 40%, 100% { -webkit-transform: scaleY(0.4) }
20% { -webkit-transform: scaleY(1.0) }
}
@keyframes stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
} 20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
================================================
FILE: src/js/core/features/loader/loader.directive.js
================================================
angular.module('linuxDash').directive('loader', function() {
return {
scope: {
width: '@'
},
template: '\
<div class="spinner">\
<div class="rect1"></div>\
<div class="rect2"></div>\
<div class="rect3"></div>\
<div class="rect4"></div>\
<div class="rect5"></div>\
</div>\
'
}
})
================================================
FILE: src/js/core/features/multi-line-chart/multi-line-chart-plugin.directive.js
================================================
angular.module('linuxDash').directive('multiLineChartPlugin', [
'$interval', '$compile', 'server', '$window',
function ($interval, $compile, server, $window) {
return {
scope: {
heading: '@',
moduleName: '@',
refreshRate: '=',
getDisplayValue: '=',
units: '=',
delay: '='
},
templateUrl: 'src/js/core/features/multi-line-chart/multi-line-chart-plugin.html',
link: function(scope, element) {
var w, h, canvas
angular.element($window).bind('resize', function() {
canvas.width = w
canvas.height = h
})
// smoothieJS - Create new chart
var chart = new SmoothieChart({
borderVisible: false,
sharpLines: true,
grid: {
fillStyle: '#ffffff',
strokeStyle: 'rgba(232,230,230,0.93)',
sharpLines: true,
borderVisible: false
},
labels: {
fontSize: 12,
precision: 0,
fillStyle: '#0f0e0e'
},
maxValue: 100,
minValue: 0,
horizontalLines: [{
value: 1,
color: '#ecc',
lineWidth: 1
}]
})
var seriesOptions = [
{
strokeStyle: 'rgba(255, 0, 0, 1)',
lineWidth: 2
},
{
strokeStyle: 'rgba(0, 255, 0, 1)',
lineWidth: 2
},
{
strokeStyle: 'rgba(0, 0, 255, 1)',
lineWidth: 2
},
{
strokeStyle: 'rgba(255, 255, 0, 1)',
lineWidth: 1
}
]
// smoothieJS - set up canvas element for chart
scope.seriesArray = []
scope.metricsArray = []
var delay = 1000
if (angular.isDefined(scope.delay))
delay = scope.delay
var initializeChart = function () {
// smoothieJS - set up canvas element for chart
var checkForCanvasReadyState = $interval(function () {
if (element.find('canvas')[0]) {
canvas = element.find('canvas')[0]
w = canvas.width
h = canvas.height
// get the data once to set up # of lines on chart
server.get(scope.moduleName, function(serverResponseData) {
var numberOfLines = Object.keys(serverResponseData).length
for (var x = 0; x < numberOfLines; x++) {
var keyForThisLine = Object.keys(serverResponseData)[x];
scope.seriesArray[x] = new TimeSeries();
chart.addTimeSeries(scope.seriesArray[x], seriesOptions[x]);
scope.metricsArray[x] = {
name: keyForThisLine,
color: seriesOptions[x].strokeStyle,
}
}
})
chart.streamTo(canvas, delay)
$interval.cancel(checkForCanvasReadyState)
}
}, 100)
}
scope.reInitializeChart = function () {
chart.seriesSet.forEach(function (ts) {
chart.removeTimeSeries(ts.timeSeries)
})
initializeChart()
}
if (!scope.isHidden)
initializeChart()
var dataCallInProgress = false
// update data on chart
scope.getData = function() {
if (dataCallInProgress) return
if (!scope.seriesArray.length) return
dataCallInProgress = true
server.get(scope.moduleName, function(serverResponseData) {
dataCallInProgress = false
scope.lastGet = new Date().getTime()
var keyCount = 0
var maxAvg = 100
// update chart with current response
for (var key in serverResponseData) {
scope.seriesArray[keyCount].append(scope.lastGet, serverResponseData[key])
keyCount++
maxAvg = Math.max(maxAvg, serverResponseData[key])
}
// update the metrics for this chart
scope.metricsArray.forEach(function(metricObj) {
metricObj.data = serverResponseData[metricObj.name].toString() + ' ' + scope.units
})
// round up the average and set the maximum scale
var len = parseInt(Math.log(maxAvg) / Math.log(10))
var div = Math.pow(10, len)
chart.options.maxValue = Math.ceil(maxAvg / div) * div
})
}
var refreshRate = (angular.isDefined(scope.refreshRate)) ? scope.refreshRate : 1000
var intervalRef = $interval(scope.getData, refreshRate)
var removeInterval = function() {
$interval.cancel(intervalRef)
}
element.on("$destroy", removeInterval)
}
}
}])
================================================
FILE: src/js/core/features/multi-line-chart/multi-line-chart-plugin.html
================================================
<plugin chart-plugin>
<canvas class="canvas" width="400" height="145"></canvas>
<table class="metrics-table" border="0">
<tbody>
<tr ng-repeat="metric in metricsArray">
<td>
<div
class="metric-square"
style="display: inline-block; border: 1px solid {{metric.color}}; width: 8px; height: 8px; background: {{metric.color}}">
</div>
</td>
<td>{{ metric.name }}</td>
<td>{{ metric.data }}</td>
</tr>
</tbody>
</table>
</plugin>
================================================
FILE: src/js/core/features/navbar/navbar.css
================================================
nav-bar {
display: block;
background-color: #f6f8f8;
height: 40px;
padding-left: 10%;
padding-top: 3px;
padding-right: 5%;
}
nav-bar ul {
margin-left: 10px;
padding: 0;
list-style-type: none;
display: inline;
}
nav-bar ul li a {
font-size: 11px;
text-transform: uppercase;
text-decoration: none;
line-height: 2.3rem;
color: grey;
}
nav-bar ul li a:hover {
color: rgb(0,0,0);
}
nav-bar ul li.active {
border-bottom: 2px solid #009587;
}
nav-bar ul li.active a {
color: #009587;
}
nav-bar ul li {
margin-left: 20px;
display: inline;
padding-left: 5px;
padding-bottom: 10px;
}
nav-bar .title {
color: #009587;
font-weight: 300;
font-size: 20px;
letter-spacing: .1rem;
float: left;
padding-top: 5px;
}
nav-bar .right-content {
float: right;
}
nav-bar .right-content,
nav-bar .right-content a {
font-size: 11px;
color: grey;
padding-top: 10px;
}
nav-bar .right-content a:hover {
color: #000;
}
================================================
FILE: src/js/core/features/navbar/navbar.directive.js
================================================
angular.module('linuxDash').directive('navBar', ['$location', function($location) {
return {
template: '\
\
<span class="title">Linux Dash</span>\
\
<ul> \
<li ng-class="{active: isActive(navItem) }" ng-repeat="navItem in items"> \
<a href="#/{{navItem}}" ng-bind="getNavItemName(navItem)"></a> \
</li> \
</ul> \
<span class="right-content">\
Resources:\
<a target="_blank" href="https://github.com/afaqurk/linux-dash">GitHub</a> | \
<a target="_blank" href="https://gitter.im/afaqurk/linux-dash">Gitter Chat Room</a> | \
<a target="_blank" href="https://github.com/afaqurk/linux-dash/wiki">Docs</a> \
</span>\
',
link: function(scope) {
scope.items = [
'system-status',
'basic-info',
'network',
'accounts',
'apps'
]
scope.getNavItemName = function(url) {
return url.replace('-', ' ')
}
scope.isActive = function(route) {
return '/' + route === $location.path()
}
}
}
}])
================================================
FILE: src/js/core/features/plugin/plugin.css
================================================
.plugin {
transition: all .1s linear;
vertical-align: text-top;
width: 400px;
display: inline-block;
padding: 0;
color: black;
text-align: center;
border-radius: 2px;
box-shadow: 0 1px 10px rgba(0,0,0,.13),0px 5px 10px rgba(0,0,0,.16);
margin: 30px 15px 0 0;
max-height: 400px;
overflow: hidden;
resize: both;
padding-bottom: 15px;
background-color: rgba(0,0,0,0.015);
}
.plugin-hidden .top-bar .heading {
color: grey;
font-style: italic;
}
.plugin-enlarged {
width: 50%;
}
@media (max-width: 768px) {
.plugin {
max-width: 80%;
float: none;
margin: 0 auto;
margin-bottom: 10px;
}
.plugin-body {
max-height: 323px;
}
.plugin-enlarged {
width: 80%;
}
}
@media (max-width: 950px) {
.plugin {
margin: 15px 10px 0 0;
}
.plugin-enlarged {
width: 90%;
}
}
.plugin.chart-plugin {
padding: 0px;
resize: none;
padding-bottom: 0;
}
.plugin-body {
background-color: #fff;
height: 323px;
font-size: 12px;
padding: 10px;
line-height: 30px;
overflow: auto;
border-top: 1px solid #ececec;
}
.plugin.chart-plugin,
.plugin.chart-plugin .plugin-body {
overflow: hidden;
padding: 0;
}
.plugin-body-short {
height: 263px;
}
.plugin last-update{
font-size: 11px;
float: left;
}
.plugin ::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.plugin ::-webkit-scrollbar-track {
background: #eee;
border: thin solid lightgray;
box-shadow: 0px 0px 3px #dfdfdf inset;
}
.plugin ::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.1);
border: thin solid rgba(0,0,0,0.1);
border-radius: 0;
}
.plugin ::-webkit-scrollbar-thumb:hover {
background: rgba(0,0,0,0.2);
}
================================================
FILE: src/js/core/features/plugin/plugin.directive.js
================================================
angular.module('linuxDash').directive('plugin', ['$rootScope', function($rootScope) {
return {
transclude: true,
templateUrl: 'src/js/core/features/plugin/plugin.html',
link: function (s, el, attr) {
if (attr.hasOwnProperty('chartPlugin'))
s.isChartPlugin = true
if ($rootScope.hiddenPlugins.indexOf(s.moduleName) > -1)
s.isHidden = true
s.toggleWidth = function () {
el.find('div')[0].removeAttribute('style')
s.enlarged = !s.enlarged
}
var setPluginVisibility = function (shouldShow) {
s.isHidden = !shouldShow
if (shouldShow) {
$rootScope.$emit('show-plugin', s.moduleName)
if (s.isChartPlugin) s.reInitializeChart()
} else {
$rootScope.$emit('hide-plugin', s.moduleName)
}
}
s.toggleVisibility = function () {
setPluginVisibility(s.isHidden)
}
s.$watch('emptyResult', function (n, o) {
if (n) {
setPluginVisibility(false)
}
})
}
}
}])
================================================
FILE: src/js/core/features/plugin/plugin.html
================================================
<div
class="plugin"
ng-class="{
'plugin-hidden': isHidden,
'plugin-enlarged': enlarged,
'chart-plugin': isChartPlugin,
}"
>
<top-bar
heading="heading"
last-updated="lastGet"
info="info"
toggle-visibility="toggleVisibility()"
is-hidden="isHidden"
toggle-width="toggleWidth()"
refresh="getData()"
is-chart="isChartPlugin">
</top-bar>
<div
ng-if="!isHidden"
class="plugin-body"
ng-class="{ 'plugin-body-short no-padding chart-plugin': isChartPlugin, }"
ng-transclude>
</div>
</div>
================================================
FILE: src/js/core/features/progress-bar/progress-bar-plugin.directive.js
================================================
angular.module('linuxDash').directive('progressBarPlugin', function() {
return {
scope: {
width: '@',
moduleName: '@',
name: '@',
value: '@',
max: '@'
},
template: '\
<div class="progress-bar-container">\
<div class="progress-bar" style="width:{{width}};">\
<div style="width: {{ (value/max) * 100 }}%;"></div>\
</div>\
</div>\
'
}
})
================================================
FILE: src/js/core/features/progress-bar/progress-bar.css
================================================
.progress-bar {
background-color: #eec;
border-radius: 10px;
padding: 0px;
clear: both;
display: inline-block;
overflow: hidden;
white-space: nowrap;
}
.progress-bar > div {
background-color: #1EAEDB;
width: 0%;
height: 5px;
border-radius: 5px;
}
================================================
FILE: src/js/core/features/table-data/table-data.css
================================================
.table-data-plugin .filter-container {
padding-bottom: 0;
margin: 0;
}
.table-data-plugin .filter,
.table-data-plugin .filter:focus,
.table-data-plugin .filter:active {
height: 20px;
padding: 5px;
margin: 5px;
border: none;
outline-color: transparent;
background: transparent;
width: 100%;
margin: 0;
text-align: center;
font-size: 15px;
}
.table-data-plugin .filter:focus {
border-bottom: 1px solid #ff5722;
}
.table-data-plugin thead tr th a,
.table-data-plugin thead tr th a:visited {
color: black;
text-decoration: none;
}
.table-data-plugin .column-sort-caret {
font-size: 10px;
color: #1EAEDB;
}
================================================
FILE: src/js/core/features/table-data/table-data.directive.js
================================================
angular.module('linuxDash').directive('tableData', ['server', '$rootScope', function (server, $rootScope) {
return {
scope: {
heading: '@',
info: '@',
moduleName: '@',
width: '@',
height: '@'
},
templateUrl: 'src/js/core/features/table-data/table-data.html',
link: function(scope, element) {
scope.sortByColumn = null
scope.sortReverse = null
// set the column to sort by
scope.setSortColumn = function(column) {
// if the column is already being sorted
// reverse the order
if (column === scope.sortByColumn) {
scope.sortReverse = !scope.sortReverse
} else {
scope.sortByColumn = column
}
scope.sortTableRows()
}
scope.sortTableRows = function() {
scope.tableRows.sort(function(currentRow, nextRow) {
var sortResult = 0
if (currentRow[scope.sortByColumn] < nextRow[scope.sortByColumn]) {
sortResult = -1
} else if (currentRow[scope.sortByColumn] === nextRow[scope.sortByColumn]) {
sortResult = 0
} else {
sortResult = 1
}
if (scope.sortReverse) {
sortResult = -1 * sortResult
}
return sortResult
})
}
scope.getData = function() {
delete scope.tableRows
server.get(scope.moduleName, function(serverResponseData) {
if (serverResponseData.length > 0) {
scope.tableHeaders = Object.keys(serverResponseData[0])
}
scope.tableRows = serverResponseData
if (scope.sortByColumn) {
scope.sortTableRows()
}
scope.lastGet = new Date().getTime()
if (serverResponseData.length < 1) {
scope.emptyResult = true
}
if (!scope.$$phase && !$rootScope.$$phase) scope.$digest()
})
}
scope.getData()
}
}
}])
================================================
FILE: src/js/core/features/table-data/table-data.html
================================================
<plugin
heading="{{ heading }}"
last-updated="lastGet"
on-refresh="getData()"
info="{{ info }}">
<loader ng-if="!tableRows"></loader>
<div ng-show="tableRows">
<table class="table-data-plugin" width="{{ width }}" height="{{ height }}">
<thead>
<tr class="table-data-filter-container" ng-show="tableRows.length">
<th colspan="{{ tableHeaders.length }}" class="filter-container">
<input class="filter" ng-model="keyword" placeholder="Search">
</th>
</tr>
<tr>
<th ng-repeat="header in tableHeaders track by $index">
<a href="" ng-click="setSortColumn(header)">{{ header }}</a>
<span class="column-sort-caret">
{{ (header === sortByColumn && !sortReverse) ? '▲': ''; }}
{{ (header === sortByColumn && sortReverse) ? '▼': ''; }}
</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in tableRows | filter:keyword">
<td ng-repeat="header in tableHeaders track by $index">
{{ row[header] }}
</td>
</tr>
</tbody>
</table>
</div>
<span ng-show="emptyResult">No data</span>
</plugin>
================================================
FILE: src/js/core/features/top-bar/topbar.css
================================================
.top-bar {
height: 15px;
padding: 15px;
font-size: 13px;
text-transform: capitalize;
color: #009587;
background-color: #f6f8f8;
}
.top-bar .heading {
float: left;
cursor: grab;
cursor: -moz-grab;
cursor: -webkit-grab;
}
.ld-top-bar-btn {
float: right;
font-size: 17px;
color: #009587;
background: #fff;
border: 1px solid #eee;
border-radius: 50%;
width: 30px;
height: 30px;
margin-top: -5px;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-ms-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
cursor: pointer;
transition: all 0.5s ease;
}
.minimize-btn {
font-size: 19px;
}
.minimize-btn.active {
}
.ld-refresh-btn:hover {
background-color: #ffeb3b;
color: black;
}
.ld-refresh-btn:active {
background-color: #0f9d58;
}
.plugin-hidden .width-toggle-btn {
display: none;
}
.plugin-hidden .minimize-btn,
.plugin-enlarged .width-toggle-btn {
background: #eee;
color: #000;
}
================================================
FILE: src/js/core/features/top-bar/topbar.directive.js
================================================
angular.module('linuxDash').directive('topBar', ['$rootScope', function($rootScope) {
return {
scope: {
heading: '=',
refresh: '&',
lastUpdated: '=',
toggleVisibility: '&',
isHidden: '=',
toggleWidth: '&',
isChart: '=',
info: '=', // not being used; needs a good ui solution
},
template: '\
<div class="top-bar"> \
<span class="heading"> ☰ {{ heading }}</span> \
\
<button \
class="ld-top-bar-btn minimize-btn" \
ng-click="toggleVisibility()" \
ng-class="{ active: isHidden }">-</button> \
\
\
<button class="ld-top-bar-btn width-toggle-btn" ng-if="toggleWidth && !isChart" ng-click="toggleWidth()">↔</button> \
<button ng-if="!isChart && !isHidden" class="ld-top-bar-btn refresh-btn" ng-click="refresh()">↺</button> \
</div> \
',
}
}])
================================================
FILE: src/js/core/rootscope-event-handlers/hide-plugin.run.js
================================================
angular
.module('linuxDash')
.run(['$rootScope', '$location', function ($rootScope, $location) {
var key = 'hiddenPlugins'
var getHiddenPlugins = function () {
var hiddenPluginsCSV = localStorage.getItem(key) || ''
return hiddenPluginsCSV.split(',')
}
var updateHiddenPlugins = function (hiddenPlugins) {
localStorage.setItem(key, hiddenPlugins.join(','))
}
$rootScope.$on('hide-plugin', function (e, m) {
var hiddenPlugins = getHiddenPlugins()
if(hiddenPlugins.indexOf(m) < 0)
hiddenPlugins.push(m)
updateHiddenPlugins(hiddenPlugins)
})
$rootScope.$on('show-plugin', function (e, m) {
var hiddenPlugins = getHiddenPlugins()
var indexOfPlugin = hiddenPlugins.indexOf(m)
if(indexOfPlugin > -1)
hiddenPlugins.splice(indexOfPlugin, 1)
updateHiddenPlugins(hiddenPlugins)
})
$rootScope.hiddenPlugins = getHiddenPlugins()
}])
================================================
FILE: src/js/core/rootscope-event-handlers/make-plugins-draggable.run.js
================================================
angular
.module('linuxDash')
.run(['$rootScope', '$location', function ($rootScope, $location) {
$rootScope.$on('$routeChangeSuccess', function () {
var intervalId = setInterval(function () {
var el = document.getElementById('plugins')
if (el) {
var sortable = Sortable.create(el, {
group: 'plugin-order-' + $location.path().replace('/', ''),
handle: '.heading',
ghostClass: 'ld-ghost',
chosenClass: 'ld-chosen',
dataIdAttr: 'sortablejs-id',
animation: 1050,
store: {
get: function (sortable) {
var order = localStorage.getItem(sortable.options.group.name);
return order ? order.split('|') : [];
},
set: function (sortable) {
var order = sortable.toArray();
localStorage.setItem(sortable.options.group.name, order.join('|'));
}
}
})
clearInterval(intervalId)
}
})
})
}])
================================================
FILE: src/js/core/routes.js
================================================
function appLoadController($scope, $location, $rootScope) {
var loadUrl = localStorage.getItem('currentTab') || 'system-status'
var loadLinuxDash = function () {
$location.path(loadUrl)
}
$rootScope.$on('start-linux-dash', loadLinuxDash)
}
function routesFn($routeProvider) {
$routeProvider
.when('/loading', {
template: [
'<div class="lead" style="text-align: center;">',
'<loader></loader>',
'Loading...',
'</div>',
].join(''),
controller: ['$scope', '$location', '$rootScope', appLoadController],
})
.when('/system-status', {
template: [
'<ram-chart sortablejs-id="ram-chart"></ram-chart> ',
'<cpu-avg-load-chart sortablejs-id="cpu-avg-load-chart"></cpu-avg-load-chart> ',
'<cpu-utilization-chart sortablejs-id="cpu-util-chart"></cpu-utilization-chart> ',
'<cpu-temp sortablejs-id="cpu-temp"></cpu-temp> ',
'<ram-intensive-processes sortablejs-id="ram-intensive-processes"></ram-intensive-processes> ',
'<cpu-intensive-processes sortablejs-id="cpu-intensive-processes"></cpu-intensive-processes> ',
'<disk-space sortablejs-id="disk-space"></disk-space> ',
'<swap-usage sortablejs-id="swap-usage"></swap-usage> ',
'<docker-processes sortablejs-id="docker"></docker-processes> ',
].join(''),
})
.when('/basic-info', {
template: [
'<machine-info sortablejs-id="machine-info"></machine-info>',
'<memory-info sortablejs-id="memory-info"></memory-info>',
'<cpu-info sortablejs-id="cpu-info"></cpu-info>',
'<scheduled-crons sortablejs-id="scheduled-crons"></scheduled-crons>',
'<cron-history sortablejs-id="cron-history"></cron-history>',
'<io-stats sortablejs-id="io-stats"></io-stats>',
].join(''),
})
.when('/network', {
template: [
'<upload-transfer-rate-chart sortablejs-id="upload"></upload-transfer-rate-chart> ',
'<download-transfer-rate-chart sortablejs-id="download"></download-transfer-rate-chart> ',
'<ip-addresses sortablejs-id="ip-addresses"></ip-addresses> ',
'<network-connections sortablejs-id="net-cons"></network-connections> ',
'<arp-cache-table sortablejs-id="arp"></arp-cache-table> ',
'<ping-speeds sortablejs-id="ping"></ping-speeds> ',
'<bandwidth sortablejs-id="bandwidth"></bandwidth> ',
].join(''),
})
.when('/accounts', {
template: [
'<server-accounts sortablejs-id="server-accounts"></server-accounts> ',
'<logged-in-accounts sortablejs-id="logged-in"></logged-in-accounts> ',
'<recent-logins sortablejs-id="recent"></recent-logins> ',
].join(''),
})
.when('/apps', {
template: [
'<common-applications sortablejs-id="common-applications"></common-applications>',
'<memcached sortablejs-id="memcached"></memcached>',
'<redis sortablejs-id="redis"></redis>',
'<pm2 sortablejs-id="pm2"></pm2>',
].join(''),
})
.otherwise({
redirectTo: '/loading'
})
}
angular.module('linuxDash').config(['$routeProvider', routesFn])
================================================
FILE: src/js/core/server.service.js
================================================
angular
.module('linuxDash')
.service('server', [
'$http', '$rootScope', '$location',
function($http, $rootScope, $location) {
var websocket = {
connection: null,
onMessageEventHandlers: {}
};
/**
* @description:
* Establish a websocket connection with server
*
* @return Null
*/
var establishWebsocketConnection = function() {
var websocketUrl = (location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.hostname + ':' + window.location.port;
if (websocket.connection === null) {
websocket.connection = new WebSocket(websocketUrl);
websocket.connection.onopen = function() {
$rootScope.$broadcast("start-linux-dash", {});
$rootScope.$apply();
console.info('Websocket connection is open');
};
websocket.connection.onmessage = function(event) {
var response = JSON.parse(event.data);
var moduleName = response.moduleName;
var moduleData = JSON.parse(response.output);
if (!!websocket.onMessageEventHandlers[moduleName]) {
websocket.onMessageEventHandlers[moduleName](moduleData);
} else {
console.info("Websocket could not find module", moduleName, "in:", websocket.onMessageEventHandlers);
}
};
websocket.connection.onclose = function() {
websocket.connection = null;
}
}
};
/**
* @description:
* Check if websockets are supported
* If so, call establishWebsocketConnection()
*
* @return Null
*/
this.checkIfWebsocketsAreSupported = function() {
var websocketSupport = {
browser: null,
server: null,
};
// does browser support websockets?
if (window.WebSocket) {
websocketSupport.browser = true;
// does backend support websockets?
$http.get("/websocket").then(function(response) {
// if websocket_support property exists and is trurthy
// websocketSupport.server will equal true.
websocketSupport.server = !!response.data["websocket_support"];
}).catch(function websocketNotSupportedByServer() {
websocketSupport.server = false;
$rootScope.$broadcast("start-linux-dash", {});
}).then(function finalDecisionOnWebsocket() {
if (websocketSupport.browser && websocketSupport.server) {
establishWebsocketConnection();
} else {
$rootScope.$broadcast("start-linux-dash", {});
}
});
}
};
/**
* Handles requests from modules for data from server
*
* @param {String} moduleName
* @param {Function} callback
* @return {[ Null || callback(server response) ]}
*/
this.get = function(moduleName, callback) {
// if we have a websocket connection
if (websocket.connection) {
// and the connection is ready
if (websocket.connection.readyState === 1) {
// set the callback as the event handler
// for server response.
//
// Callback instance needs to be overwritten
// each time for this to work. Not sure why.
websocket.onMessageEventHandlers[moduleName] = callback;
//
websocket.connection.send(moduleName);
} else {
console.log("Websocket not ready yet.", moduleName);
}
}
// otherwise
else {
var moduleAddress = 'server/?module=' + moduleName;
return $http.get(moduleAddress).then(function(response) {
return callback(response.data);
});
}
};
}
])
================================================
FILE: src/js/plugins/README.md
================================================
# Plugins
These are the individual plugins which display server data to the user in the UI.
Majority of the implementations of plugins are very simple since they leverage the Linux Dash core (`src/js/core/features`)
### disk-space
Shows the disk
================================================
FILE: src/js/plugins/cpu-avg-load-chart.directive.js
================================================
angular.module('linuxDash').directive('cpuAvgLoadChart', ['server', function(server) {
return {
restrict: 'E',
scope: {},
template: '\
<multi-line-chart-plugin \
heading="CPU Avg Load" \
module-name="load_avg" \
units="units"> \
</multi-line-chart-plugin> \
',
link: function(scope) {
scope.units = '%'
}
}
}])
================================================
FILE: src/js/plugins/cpu-temp.directive.js
================================================
angular.module('linuxDash').directive('cpuTemp', ['server', function(server) {
return {
restrict: 'E',
scope: {},
template: ' \
<line-chart-plugin \
\
heading="CPU temp" \
module-name="cpu_temp" \
color="0,255,0" \
\
max-value="max" \
min-value="min" \
refresh-rate="1500" \
\
get-display-value="displayValue" \
metrics="utilMetrics"> \
</line-chart-plugin> \
',
link: function(scope) {
scope.min = 0
scope.max = 100
scope.displayValue = function (serverResponseData) {
return serverResponseData
}
scope.utilMetrics = [{
name: 'Temprature',
generate: function (serverResponseData) {
return serverResponseData + ' °C'
}
}]
}
}
}])
================================================
FILE: src/js/plugins/cpu-utilization-chart.directive.js
================================================
angular.module('linuxDash').directive('cpuUtilizationChart', ['server', function(server) {
return {
restrict: 'E',
scope: {},
template: ' \
<line-chart-plugin \
\
heading="CPU Utilization" \
module-name="cpu_utilization" \
color="0,255,0" \
\
max-value="max" \
min-value="min" \
refresh-rate="1500" \
\
get-display-value="displayValue" \
metrics="utilMetrics"> \
</line-chart-plugin> \
',
link: function(scope) {
scope.min = 0
scope.max = 100
scope.displayValue = function(serverResponseData) {
return serverResponseData
}
scope.utilMetrics = [{
name: 'Usage',
generate: function(serverResponseData) {
return serverResponseData + ' %'
}
}]
}
}
}])
================================================
FILE: src/js/plugins/disk-space/disk-space.directive.js
================================================
angular.module('linuxDash').directive('diskSpace', ['server', function(server) {
return {
restrict: 'E',
scope: {},
templateUrl: 'src/js/plugins/disk-space/disk-space.html',
link: function(scope) {
var getKBMultiplierFn = function (size, power) {
return function () {
return size * Math.pow(1024, power)
}
}
var kbDictionary = {
'M': function () { return getKBMultiplierFn(size, 1) },
'G': function () { return getKBMultiplierFn(size, 2) },
'T': function () { return getKBMultiplierFn(size, 3) },
'P': function () { return getKBMultiplierFn(size, 4) },
'E': function () { return getKBMultiplierFn(size, 5) },
'Z': function () { return getKBMultiplierFn(size, 6) },
'Y': function () { return getKBMultiplierFn(size, 7) },
}
scope.heading = "Disk Partitions"
scope.moduleName = 'disk_partitions'
scope.getData = function() {
server.get(scope.moduleName, function(serverResponseData) {
scope.diskSpaceData = serverResponseData
})
scope.lastGet = new Date().getTime()
}
scope.getData()
scope.getKB = function(stringSize) {
var lastChar = stringSize.slice(-1)
var size = parseFloat(stringSize.replace(",", "."))
try {
return kbDictionary[lastChar](size)
} catch (err) {
return size
}
}
}
}
}])
================================================
FILE: src/js/plugins/disk-space/disk-space.html
================================================
<plugin
heading="Disk Partitions"
last-updated="lastGet"
on-refresh="getData()">
<loader ng-hide="diskSpaceData"></loader>
<table ng-show="diskSpaceData">
<thead>
<tr>
<th>Name</th>
<th></th>
<th>Stats</th>
<th>Used</th>
<th>Mount</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="partition in diskSpaceData">
<td>{{partition['file_system']}}</td>
<td>
<progress-bar-plugin
width="70px"
value="{{ getKB(partition['used']) }}"
max="{{ getKB(partition['size']) }}">
</progress-bar-plugin>
</td>
<td>
{{ partition['used'] }} / {{ partition['size'] }}
</td>
<td>
{{ partition['used%'] }}
</td>
<td>{{ partition['mounted'] }}</td>
</tr>
</tbody>
</table>
</plugin>
================================================
FILE: src/js/plugins/download-transfer-rate-chart.directive.js
================================================
angular.module('linuxDash').directive('downloadTransferRateChart', ['server', function(server) {
return {
restrict: 'E',
scope: {},
template: ' \
<multi-line-chart-plugin \
heading="Download Transfer Rate" \
module-name="download_transfer_rate" \
units="units"> \
</multi-line-chart-plugin> \
',
link: function(scope) {
scope.delay = 2000
scope.units = 'KB/s'
}
}
}])
================================================
FILE: src/js/plugins/ram-chart.directive.js
================================================
angular.module('linuxDash').directive('ramChart', ['server', function (server) {
return {
restrict: 'E',
scope: {},
template: '\
<line-chart-plugin \
\
heading="RAM Usage" \
module-name="current_ram" \
color="0,255,0" \
\
max-value="maxRam" \
min-value="minRam" \
refresh-rate="1000" \
\
get-display-value="ramToDisplay" \
metrics="ramMetrics"> \
</line-chart-plugin> \
',
link: function(scope) {
// get max ram available on machine before we
// can start charting
server.get('current_ram', function(resp) {
scope.maxRam = resp.total
scope.minRam = 0
})
scope.ramToDisplay = function(serverResponseData) {
return serverResponseData.used
}
var humanizeRam = function (ramInMB) {
var ram = {
value: parseInt(ramInMB, 10),
unit: 'MB',
}
// if ram > 1,000 MB, use GB
if (ram.value > 1000) {
ram = {
value: (ramInMB/1024).toFixed(2),
unit: 'GB',
}
}
return ram.value + ' ' + ram.unit
}
scope.ramMetrics = [{
name: 'Used',
generate: function(serverResponseData) {
var ratio = serverResponseData.used / serverResponseData.total
var percentage = parseInt(ratio * 100)
var usedRam = humanizeRam(serverResponseData.used)
return usedRam + ' (' + percentage.toString() + '%)'
}
},
{
name: 'Available',
generate: function(serverResponseData) {
var availableRam = humanizeRam(serverResponseData.available)
var totalRam = humanizeRam(serverResponseData.total)
return availableRam + ' of ' + totalRam
}
}]
}
}
}])
================================================
FILE: src/js/plugins/simple-table-data-plugins.directive.js
================================================
var simpleTableModules = [
{
name: 'machineInfo',
template: '<key-value-list heading="General Info." module-name="general_info" info="System Information"></key-value-list>'
},
{
name: 'ipAddresses',
template: '<table-data heading="IP Addresses" module-name="ip_addresses" info="IPs assigned to this server"></table-data>'
},
{
name: 'ramIntensiveProcesses',
template: '<table-data heading="RAM Processes" module-name="ram_intensive_processes" info="Processes which are using most RAM."></table-data>'
},
{
name: 'cpuIntensiveProcesses',
template: '<table-data heading="CPU Processes" module-name="cpu_intensive_processes" info="Processes which are using most CPU."></table-data>'
},
{
name: 'dockerProcesses',
template: '<table-data heading="Docker Processes" module-name="docker_processes" info="Processes in Docker Containers sorted by CPU."></table-data>'
},
{
name: 'networkConnections',
template: '<table-data heading="Network Connections" module-name="network_connections"></table-data>'
},
{
name: 'serverAccounts',
template: '<table-data heading="Accounts" module-name="user_accounts" info="User accounts on this server."></table-data>'
},
{
name: 'loggedInAccounts',
template: '<table-data heading="Logged In Accounts" module-name="logged_in_users" info="Users currently logged in."></table-data>'
},
{
name: 'recentLogins',
template: '<table-data heading="Recent Logins" module-name="recent_account_logins" info="Recent user sessions."></table-data>'
},
{
name: 'arpCacheTable',
template: '<table-data heading="ARP Cache Table" module-name="arp_cache"></table-data>'
},
{
name: 'commonApplications',
template: '<table-data heading="Common Applications" module-name="common_applications" info="List of commonly installed applications."></table-data>'
},
{
name: 'pingSpeeds',
template: '<table-data heading="Ping Speeds" module-name="ping" info="Ping speed in milliseconds."></table-data>'
},
{
name: 'bandwidth',
template: '<table-data heading="Bandwidth" module-name="bandwidth"></table-data>'
},
{
name: 'swapUsage',
template: '<table-data heading="Swap Usage" module-name="swap"></table-data>'
},
{
name: 'internetSpeed',
template: '<key-value-list heading="Internet Speed" module-name="internet_speed" info="Internet connection speed of server."></key-value-list>'
},
{
name: 'memcached',
template: '<key-value-list heading="Memcached" module-name="memcached"></key-value-list>'
},
{
name: 'redis',
template: '<key-value-list heading="Redis" module-name="redis"></key-value-list>'
},
{
name: 'pm2',
template: '<table-data heading="PM2" module-name="pm2_stats" info="Process Manager 2 (PM2) Node Module stats"></table-data>'
},
{
name: 'memoryInfo',
template: '<key-value-list heading="Memory Info" module-name="memory_info" info="/proc/meminfo read-out."></key-value-list>'
},
{
name: 'cpuInfo',
template: '<key-value-list heading="CPU Info" module-name="cpu_info" info="/usr/bin/lscpu read-out."></key-value-list>'
},
{
name: 'ioStats',
template: '<table-data heading="IO Stats" module-name="io_stats" info="/proc/diskstats read-out."></table-data>'
},
{
name: 'scheduledCrons',
template: '<table-data heading="Scheduled Cron Jobs" module-name="scheduled_crons" info="Crons for all users on the server."></table-data>'
},
{
name: 'cronHistory',
template: '<table-data heading="Cron Job History" module-name="cron_history" info="Crons which have run recently."></table-data>'
}
]
simpleTableModules.forEach(function(module, key) {
angular.module('linuxDash').directive(module.name, ['server', function(server) {
var moduleDirective = {
restrict: 'E',
scope: {}
}
moduleDirective['template'] = module.template
return moduleDirective
}])
})
================================================
FILE: src/js/plugins/upload-transfer-rate-chart.directive.js
================================================
angular.module('linuxDash').directive('uploadTransferRateChart', ['server', function(server) {
return {
restrict: 'E',
scope: {},
template: ' \
<multi-line-chart-plugin \
heading="Upload Transfer Rate" \
module-name="upload_transfer_rate" \
units="units"> \
</multi-line-chart-plugin> \
',
link: function(scope) {
scope.delay = 2000
scope.units = 'KB/s'
}
}
}])
gitextract_txcbka2j/
├── .gitignore
├── LICENSE.md
├── README.md
├── app/
│ ├── index.html
│ └── server/
│ ├── config/
│ │ └── ping_hosts
│ ├── index.go
│ ├── index.js
│ ├── index.php
│ ├── index.py
│ └── linux_json_api.sh
├── bin/
│ └── linux-dash
├── demo.js
├── ecosystem.config.js
├── gulpfile.js
├── index.html
├── package.json
└── src/
├── css/
│ ├── README.md
│ ├── buttons.css
│ ├── main.css
│ ├── plugins-cotnainer.css
│ └── tables.css
└── js/
├── README.md
├── core/
│ ├── app.js
│ ├── features/
│ │ ├── key-value-list/
│ │ │ ├── key-value-list.directive.js
│ │ │ └── key-value-list.html
│ │ ├── line-chart/
│ │ │ ├── line-chart-plugin.directive.js
│ │ │ └── line-chart-plugin.html
│ │ ├── loader/
│ │ │ ├── loader.css
│ │ │ └── loader.directive.js
│ │ ├── multi-line-chart/
│ │ │ ├── multi-line-chart-plugin.directive.js
│ │ │ └── multi-line-chart-plugin.html
│ │ ├── navbar/
│ │ │ ├── navbar.css
│ │ │ └── navbar.directive.js
│ │ ├── plugin/
│ │ │ ├── plugin.css
│ │ │ ├── plugin.directive.js
│ │ │ └── plugin.html
│ │ ├── progress-bar/
│ │ │ ├── progress-bar-plugin.directive.js
│ │ │ └── progress-bar.css
│ │ ├── table-data/
│ │ │ ├── table-data.css
│ │ │ ├── table-data.directive.js
│ │ │ └── table-data.html
│ │ └── top-bar/
│ │ ├── topbar.css
│ │ └── topbar.directive.js
│ ├── rootscope-event-handlers/
│ │ ├── hide-plugin.run.js
│ │ └── make-plugins-draggable.run.js
│ ├── routes.js
│ └── server.service.js
└── plugins/
├── README.md
├── cpu-avg-load-chart.directive.js
├── cpu-temp.directive.js
├── cpu-utilization-chart.directive.js
├── disk-space/
│ ├── disk-space.directive.js
│ └── disk-space.html
├── download-transfer-rate-chart.directive.js
├── ram-chart.directive.js
├── simple-table-data-plugins.directive.js
└── upload-transfer-rate-chart.directive.js
SYMBOL INDEX (9 symbols across 5 files)
FILE: app/server/index.go
function init (line 17) | func init() {
function main (line 21) | func main() {
FILE: app/server/index.js
function getPluginData (line 36) | function getPluginData(pluginName, callback) {
FILE: app/server/index.py
class ThreadedHTTPServer (line 19) | class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
class MainHandler (line 22) | class MainHandler(BaseHTTPRequestHandler):
method do_GET (line 23) | def do_GET(self):
FILE: src/js/core/app.js
function runFn (line 1) | function runFn(server, $location, $rootScope) {
FILE: src/js/core/routes.js
function appLoadController (line 1) | function appLoadController($scope, $location, $rootScope) {
function routesFn (line 10) | function routesFn($routeProvider) {
Condensed preview — 57 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (107K chars).
[
{
"path": ".gitignore",
"chars": 47,
"preview": "node_modules\n*.log\n*/linuxDash.min.js.map\ntemp\n"
},
{
"path": "LICENSE.md",
"chars": 1074,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2014 afaqurk\n\nPermission is hereby granted, free of charge, to any person obtaining"
},
{
"path": "README.md",
"chars": 3392,
"preview": "<h1 align=\"center\">\n <a href=\"https://afaqurk.github.io/linux-dash\">\n <img src=\"https://raw.githubusercontent.com/af"
},
{
"path": "app/index.html",
"chars": 1161,
"preview": "<!DOCTYPE html>\n<html lang=\"en\" ng-app=\"linuxDash\">\n <head>\n <meta http-equiv=\"Content-Type\" content=\"text/htm"
},
{
"path": "app/server/config/ping_hosts",
"chars": 33,
"preview": "google.com\nyahoo.com\ntwitter.com\n"
},
{
"path": "app/server/index.go",
"chars": 1245,
"preview": "package main\r\n\r\nimport (\r\n\t\"bytes\"\r\n\t\"flag\"\r\n\t\"fmt\"\r\n\t\"net/http\"\r\n\t\"os\"\r\n\t\"os/exec\"\r\n)\r\n\r\nvar (\r\n\tlistenAddress = flag.S"
},
{
"path": "app/server/index.js",
"chars": 1880,
"preview": "var express = require('express')\nvar app = require('express')()\nvar server = require('http').Server(app)\nvar path "
},
{
"path": "app/server/index.php",
"chars": 265,
"preview": "<?php\n\theader(\"Cache-Control: no-store, no-cache, must-revalidate\");\n\theader(\"Pragma: no-cache\");\n\n\t$shell_file = dirnam"
},
{
"path": "app/server/index.py",
"chars": 1904,
"preview": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport os\nimport sys\nfrom BaseHTTPServer import BaseHTTPReq"
},
{
"path": "app/server/linux_json_api.sh",
"chars": 18530,
"preview": "#!/bin/bash\n\nECHO=$(type -P echo)\nSED=$(type -P sed)\nGREP=$(type -P grep)\nTR=$(type -P tr)\nAWK=$(type -P awk)\nCAT=$(type"
},
{
"path": "bin/linux-dash",
"chars": 54,
"preview": "#!/usr/bin/env node\nrequire('../app/server/index.js')\n"
},
{
"path": "demo.js",
"chars": 14633,
"preview": "angular\n .module('linuxDashDemo', ['linuxDash', 'ngMockE2E'])\n .run(function($httpBackend) {\n\n// signal to not use web"
},
{
"path": "ecosystem.config.js",
"chars": 174,
"preview": "module.exports = {\n apps : [{\n name: 'Linux Dash',\n script: './app/server/index.js',\n watch: false,\n env: {"
},
{
"path": "gulpfile.js",
"chars": 1411,
"preview": "var g = require('gulp')\nvar concat = require('gulp-concat')\nvar uglify = require('gulp-uglify'"
},
{
"path": "index.html",
"chars": 1334,
"preview": "<!DOCTYPE html>\n<html lang=\"en\" ng-app=\"linuxDashDemo\">\n <head>\n <meta http-equiv=\"Content-Type\" content=\"text"
},
{
"path": "package.json",
"chars": 1052,
"preview": "{\n \"name\": \"linux-dash\",\n \"version\": \"2.0.0\",\n \"description\": \"A simple, low overhead web dashboard for linux.\",\n \"m"
},
{
"path": "src/css/README.md",
"chars": 391,
"preview": "# Global CSS\n\nThese are CSS files which set global & base element style rules.\n\n## main.css\n\nStyles for body & html tags"
},
{
"path": "src/css/buttons.css",
"chars": 28,
"preview": "button {\n outline: none;\n}\n"
},
{
"path": "src/css/main.css",
"chars": 347,
"preview": "html {\n margin-top: 0px;\n padding-top: 0;\n\n -webkit-transition: all 1s ease;\n -moz-transition: all 1s ease;\n -ms-tr"
},
{
"path": "src/css/plugins-cotnainer.css",
"chars": 181,
"preview": "#plugins {\n padding-top: 0;\n margin-top: 0;\n border: 1px;\n margin-left: 10%;\n}\n\n@media (min-width: 1080px) {\n #plug"
},
{
"path": "src/css/tables.css",
"chars": 603,
"preview": "table {\n width: 100%;\n font-size: 10px;\n margin: 0;\n border-collapse: collapse;\n text-align: left;\n table-layout:f"
},
{
"path": "src/js/README.md",
"chars": 578,
"preview": "# JS (UI)\n\nThe root directory for _all_ of the UI JavaScript source files which power Linux Dash\n\nIt is an Angular JS (v"
},
{
"path": "src/js/core/app.js",
"chars": 548,
"preview": "function runFn(server, $location, $rootScope) {\n server.checkIfWebsocketsAreSupported()\n\n $rootScope.$on(\"$locationCha"
},
{
"path": "src/js/core/features/key-value-list/key-value-list.directive.js",
"chars": 783,
"preview": "angular.module('linuxDash').directive('keyValueList', ['server', '$rootScope', function (server, $rootScope) {\n return "
},
{
"path": "src/js/core/features/key-value-list/key-value-list.html",
"chars": 461,
"preview": "<plugin\n heading=\"{{ heading }}\"\n last-updated=\"lastGet\"\n on-refresh=\"getData()\"\n info=\"{{ info }}\">\n\n <loader ng-i"
},
{
"path": "src/js/core/features/line-chart/line-chart-plugin.directive.js",
"chars": 5114,
"preview": "angular.module('linuxDash').directive('lineChartPlugin', [\n '$interval', '$compile', 'server', '$window',\n function ($"
},
{
"path": "src/js/core/features/line-chart/line-chart-plugin.html",
"chars": 514,
"preview": "<plugin chart-plugin>\n\n <loader ng-if=\"!maxValue || initializing\"></loader>\n\n <canvas\n ng-show=\"!initializing && !e"
},
{
"path": "src/js/core/features/loader/loader.css",
"chars": 969,
"preview": ".spinner {\n margin: 100px auto;\n width: 50px;\n height: 30px;\n text-align: center;\n font-size: 10px;\n}\n\n.spinner > d"
},
{
"path": "src/js/core/features/loader/loader.directive.js",
"chars": 352,
"preview": "angular.module('linuxDash').directive('loader', function() {\n return {\n scope: {\n width: '@'\n },\n templat"
},
{
"path": "src/js/core/features/multi-line-chart/multi-line-chart-plugin.directive.js",
"chars": 4869,
"preview": "angular.module('linuxDash').directive('multiLineChartPlugin', [\n '$interval', '$compile', 'server', '$window',\n functi"
},
{
"path": "src/js/core/features/multi-line-chart/multi-line-chart-plugin.html",
"chars": 529,
"preview": "<plugin chart-plugin>\n\n <canvas class=\"canvas\" width=\"400\" height=\"145\"></canvas>\n\n <table class=\"metrics-table\" borde"
},
{
"path": "src/js/core/features/navbar/navbar.css",
"chars": 965,
"preview": "nav-bar {\n display: block;\n background-color: #f6f8f8;\n height: 40px;\n padding-left: 10%;\n padding-top: 3px;\n padd"
},
{
"path": "src/js/core/features/navbar/navbar.directive.js",
"chars": 1086,
"preview": "angular.module('linuxDash').directive('navBar', ['$location', function($location) {\n return {\n template: '\\\n \\\n"
},
{
"path": "src/js/core/features/plugin/plugin.css",
"chars": 1683,
"preview": "\n.plugin {\n transition: all .1s linear;\n\n vertical-align: text-top;\n width: 400px;\n display: inline-block;\n padding"
},
{
"path": "src/js/core/features/plugin/plugin.directive.js",
"chars": 1055,
"preview": "angular.module('linuxDash').directive('plugin', ['$rootScope', function($rootScope) {\n return {\n transclude: true,\n "
},
{
"path": "src/js/core/features/plugin/plugin.html",
"chars": 558,
"preview": "<div\n class=\"plugin\"\n ng-class=\"{\n 'plugin-hidden': isHidden,\n 'plugin-enlarged': enlarged,\n 'chart-plugin': "
},
{
"path": "src/js/core/features/progress-bar/progress-bar-plugin.directive.js",
"chars": 423,
"preview": "angular.module('linuxDash').directive('progressBarPlugin', function() {\n return {\n scope: {\n width: '@',\n "
},
{
"path": "src/js/core/features/progress-bar/progress-bar.css",
"chars": 270,
"preview": ".progress-bar {\n background-color: #eec;\n border-radius: 10px;\n padding: 0px;\n clear: both;\n display: inline-block;"
},
{
"path": "src/js/core/features/table-data/table-data.css",
"chars": 640,
"preview": ".table-data-plugin .filter-container {\n padding-bottom: 0;\n margin: 0;\n}\n\n.table-data-plugin .filter,\n.table-data-plug"
},
{
"path": "src/js/core/features/table-data/table-data.directive.js",
"chars": 1979,
"preview": "angular.module('linuxDash').directive('tableData', ['server', '$rootScope', function (server, $rootScope) {\n return {\n "
},
{
"path": "src/js/core/features/table-data/table-data.html",
"chars": 1238,
"preview": "<plugin\n heading=\"{{ heading }}\"\n last-updated=\"lastGet\"\n on-refresh=\"getData()\"\n info=\"{{ info }}\">\n\n <loader ng-i"
},
{
"path": "src/js/core/features/top-bar/topbar.css",
"chars": 981,
"preview": ".top-bar {\n\n height: 15px;\n padding: 15px;\n\n font-size: 13px;\n text-transform: capitalize;\n color: #009587;\n\n back"
},
{
"path": "src/js/core/features/top-bar/topbar.directive.js",
"chars": 914,
"preview": "angular.module('linuxDash').directive('topBar', ['$rootScope', function($rootScope) {\n return {\n scope: {\n head"
},
{
"path": "src/js/core/rootscope-event-handlers/hide-plugin.run.js",
"chars": 953,
"preview": "angular\n .module('linuxDash')\n .run(['$rootScope', '$location', function ($rootScope, $location) {\n\n var key = 'hid"
},
{
"path": "src/js/core/rootscope-event-handlers/make-plugins-draggable.run.js",
"chars": 1074,
"preview": "angular\n .module('linuxDash')\n .run(['$rootScope', '$location', function ($rootScope, $location) {\n\n $rootScope.$on"
},
{
"path": "src/js/core/routes.js",
"chars": 3169,
"preview": "function appLoadController($scope, $location, $rootScope) {\n var loadUrl = localStorage.getItem('currentTab') || 'syste"
},
{
"path": "src/js/core/server.service.js",
"chars": 3939,
"preview": "angular\n .module('linuxDash')\n .service('server', [\n '$http', '$rootScope', '$location',\n function($http, $rootS"
},
{
"path": "src/js/plugins/README.md",
"chars": 250,
"preview": "# Plugins\n\nThese are the individual plugins which display server data to the user in the UI.\n\nMajority of the implementa"
},
{
"path": "src/js/plugins/cpu-avg-load-chart.directive.js",
"chars": 387,
"preview": "angular.module('linuxDash').directive('cpuAvgLoadChart', ['server', function(server) {\n return {\n restrict: 'E',\n "
},
{
"path": "src/js/plugins/cpu-temp.directive.js",
"chars": 815,
"preview": "angular.module('linuxDash').directive('cpuTemp', ['server', function(server) {\n return {\n restrict: 'E',\n scope: "
},
{
"path": "src/js/plugins/cpu-utilization-chart.directive.js",
"chars": 852,
"preview": "angular.module('linuxDash').directive('cpuUtilizationChart', ['server', function(server) {\n return {\n restrict: 'E',"
},
{
"path": "src/js/plugins/disk-space/disk-space.directive.js",
"chars": 1465,
"preview": "angular.module('linuxDash').directive('diskSpace', ['server', function(server) {\n return {\n restrict: 'E',\n scope"
},
{
"path": "src/js/plugins/disk-space/disk-space.html",
"chars": 895,
"preview": "<plugin\n heading=\"Disk Partitions\"\n last-updated=\"lastGet\"\n on-refresh=\"getData()\">\n\n <loader ng-hide=\"diskSpaceData"
},
{
"path": "src/js/plugins/download-transfer-rate-chart.directive.js",
"chars": 444,
"preview": "angular.module('linuxDash').directive('downloadTransferRateChart', ['server', function(server) {\n return {\n restrict"
},
{
"path": "src/js/plugins/ram-chart.directive.js",
"chars": 1853,
"preview": "angular.module('linuxDash').directive('ramChart', ['server', function (server) {\n return {\n restrict: 'E',\n scope"
},
{
"path": "src/js/plugins/simple-table-data-plugins.directive.js",
"chars": 3967,
"preview": "var simpleTableModules = [\n {\n name: 'machineInfo',\n template: '<key-value-list heading=\"General Info.\" module-na"
},
{
"path": "src/js/plugins/upload-transfer-rate-chart.directive.js",
"chars": 444,
"preview": "angular.module('linuxDash').directive('uploadTransferRateChart', ['server', function(server) {\n return {\n restrict: "
}
]
About this extraction
This page contains the full source code of the tariqbuilds/linux-dash GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 57 files (94.5 KB), approximately 29.0k tokens, and a symbol index with 9 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.