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 ================================================

v2.0
A simple & low-overhead web dashboard for linux systems

Demo  |  Docs

linux-dash Gitter chat


## 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 ================================================ Linux Dash : Simple, beautiful server monitoring web dashboard
================================================ 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 ================================================ 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=$(/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 # */ [[ -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}" # */ # 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 ================================================ Linux Dash : Simple, beautiful server monitoring web dashboard
================================================ 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 ================================================
{{ name }} {{ value }}
No data
================================================ 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 ================================================
{{ metric.name }} {{ metric.data }}
No data
================================================ 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: '\
\
\
\
\
\
\
\ ' } }) ================================================ 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 ================================================
{{ metric.name }} {{ metric.data }}
================================================ 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: '\ \ Linux Dash\ \ \ \ Resources:\ GitHub | \ Gitter Chat Room | \ Docs \ \ ', 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 ================================================
================================================ 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: '\
\
\
\
\
\ ' } }) ================================================ 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 ================================================
{{ header }} {{ (header === sortByColumn && !sortReverse) ? '▲': ''; }} {{ (header === sortByColumn && sortReverse) ? '▼': ''; }}
{{ row[header] }}
No data
================================================ 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: '\
\ ☰ {{ heading }} \ \ \ \ \ \ \
\ ', } }]) ================================================ 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: [ '
', '', 'Loading...', '
', ].join(''), controller: ['$scope', '$location', '$rootScope', appLoadController], }) .when('/system-status', { template: [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ].join(''), }) .when('/basic-info', { template: [ '', '', '', '', '', '', ].join(''), }) .when('/network', { template: [ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ].join(''), }) .when('/accounts', { template: [ ' ', ' ', ' ', ].join(''), }) .when('/apps', { template: [ '', '', '', '', ].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: '\ \ \ ', 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: ' \ \ \ ', 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: ' \ \ \ ', 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 ================================================
Name Stats Used Mount
{{partition['file_system']}} {{ partition['used'] }} / {{ partition['size'] }} {{ partition['used%'] }} {{ partition['mounted'] }}
================================================ FILE: src/js/plugins/download-transfer-rate-chart.directive.js ================================================ angular.module('linuxDash').directive('downloadTransferRateChart', ['server', function(server) { return { restrict: 'E', scope: {}, template: ' \ \ \ ', 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: '\ \ \ ', 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: '' }, { name: 'ipAddresses', template: '' }, { name: 'ramIntensiveProcesses', template: '' }, { name: 'cpuIntensiveProcesses', template: '' }, { name: 'dockerProcesses', template: '' }, { name: 'networkConnections', template: '' }, { name: 'serverAccounts', template: '' }, { name: 'loggedInAccounts', template: '' }, { name: 'recentLogins', template: '' }, { name: 'arpCacheTable', template: '' }, { name: 'commonApplications', template: '' }, { name: 'pingSpeeds', template: '' }, { name: 'bandwidth', template: '' }, { name: 'swapUsage', template: '' }, { name: 'internetSpeed', template: '' }, { name: 'memcached', template: '' }, { name: 'redis', template: '' }, { name: 'pm2', template: '' }, { name: 'memoryInfo', template: '' }, { name: 'cpuInfo', template: '' }, { name: 'ioStats', template: '' }, { name: 'scheduledCrons', template: '' }, { name: 'cronHistory', template: '' } ] 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: ' \ \ \ ', link: function(scope) { scope.delay = 2000 scope.units = 'KB/s' } } }])