Create vnstat-dashboard
This commit is contained in:
parent
b3cbd615c8
commit
31f2ee91dd
|
@ -0,0 +1,42 @@
|
|||
name: "vnstat-dashboard docker build"
|
||||
|
||||
env:
|
||||
PROJECT: vnstat-dashboard
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set tag
|
||||
id: tag
|
||||
run: |
|
||||
TAG=$(cat ${{ env.PROJECT }}/Dockerfile | awk 'NR==4 {print $3}')
|
||||
echo "::set-env name=TAG::$TAG"
|
||||
- name: Docker Hub login
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: |
|
||||
echo "${DOCKER_PASSWORD}" | docker login --username ${DOCKER_USERNAME} --password-stdin
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v1
|
||||
with:
|
||||
buildx-version: latest
|
||||
- name: Build Dockerfile
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
run: |
|
||||
docker buildx build \
|
||||
--platform=linux/amd64,linux/arm64 \
|
||||
--output "type=image,push=true" \
|
||||
--file ${{ env.PROJECT }}/Dockerfile ./${{ env.PROJECT }} \
|
||||
--tag $(echo "${DOCKER_USERNAME}" | tr '[:upper:]' '[:lower:]')/${{ env.PROJECT }}:latest \
|
||||
--tag $(echo "${DOCKER_USERNAME}" | tr '[:upper:]' '[:lower:]')/${{ env.PROJECT }}:${TAG}
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
|
@ -0,0 +1,17 @@
|
|||
FROM php:7.0-apache
|
||||
MAINTAINER Alex Marston <alexander.marston@gmail.com>
|
||||
|
||||
ENV VERSION 1.0
|
||||
# Install Git
|
||||
RUN apt-get update && apt-get install -y git unzip
|
||||
|
||||
# Install Composer to handle dependencies
|
||||
RUN curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
|
||||
|
||||
# Copy application source code to html directory
|
||||
COPY ./app/ /var/www/html/
|
||||
|
||||
# Install dependencies
|
||||
RUN composer install
|
||||
|
||||
RUN mkdir -p /var/lib/vnstat
|
|
@ -0,0 +1,8 @@
|
|||
# vnstat-dashboard for docker
|
||||
|
||||
GitHub [stilleshan/dockerfile](https://github.com/stilleshan/dockerfile)
|
||||
Docker [stilleshan/vnstat-dashboard](https://hub.docker.com/r/stilleshan/vnstat-dashboard)
|
||||
> *docker image support for X86 and ARM*
|
||||
|
||||
## 使用
|
||||
本仓库参考 [tomangert/vnstat-dashboard](https://github.com/tomangert/vnstat-dashboard) 对原作者仓库 [alexandermarston/vnstat-dashboard](https://github.com/alexandermarston/vnstat-dashboard) 进行部分 bug 修复后构建 docker 镜像,主要用与自用和存档备份,具体使用教程请参考原作者仓库`README`文件.
|
|
@ -0,0 +1 @@
|
|||
theme: jekyll-theme-slate
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright (C) 2019 Alexander Marston (alexander.marston@gmail.com)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* http://stackoverflow.com/questions/17206631/why-are-bootstrap-tabs-displaying-tab-pane-divs-with-incorrect-widths-when-using */
|
||||
/* bootstrap hack: fix content width inside hidden tabs */
|
||||
.tab-content > .tab-pane:not(.active),
|
||||
.pill-content > .pill-pane:not(.active) {
|
||||
display: block;
|
||||
height: 0;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
/* bootstrap hack end */
|
||||
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
body {
|
||||
margin-bottom: 60px; /* Margin bottom by footer height */
|
||||
}
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px; /* Set the fixed height of the footer here */
|
||||
line-height: 60px; /* Vertically center the text there */
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.nav-tabs {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.navbar {
|
||||
margin-bottom: 25px;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"require": {
|
||||
"smarty/smarty": "~3.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Alexander Marston (alexander.marston@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Uncomment to enable error reporting to the screen
|
||||
/*ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
ferror_reporting(E_ALL);*/
|
||||
|
||||
// Set the default system Timezone
|
||||
date_default_timezone_set('Europe/London');
|
||||
|
||||
// Path of vnstat
|
||||
$vnstat_bin_dir = '/usr/bin/vnstat';
|
||||
|
||||
// Path of config file
|
||||
/*$vnstat_config = '/etc/vnstat.conf';*/
|
||||
|
||||
// linear or logarithmic graphs. Uncomment for logarithmic
|
||||
/*$graph_type = 'log';*/
|
||||
|
||||
// Set to true to set your own interfaces
|
||||
$use_predefined_interfaces = false;
|
||||
|
||||
if ($use_predefined_interfaces == true) {
|
||||
$interface_list = ["eth0", "eth1"];
|
||||
|
||||
$interface_name['eth0'] = "Internal #1";
|
||||
$interface_name['eth1'] = "Internal #2";
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Alexander Marston (alexander.marston@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
$logk = log(1024);
|
||||
|
||||
function getScale($bytes)
|
||||
{
|
||||
global $logk;
|
||||
|
||||
$ui = floor(round(log($bytes)/$logk,3));
|
||||
if ($ui < 0) { $ui = 0; }
|
||||
if ($ui > 8) { $ui = 8; }
|
||||
|
||||
return $ui;
|
||||
}
|
||||
|
||||
// Get the largest value in an array
|
||||
function getLargestValue($array) {
|
||||
return $max = array_reduce($array, function ($a, $b) {
|
||||
return $a > $b['total'] ? $a : $b['total'];
|
||||
});
|
||||
}
|
||||
|
||||
function getBaseValue($array, $scale)
|
||||
{
|
||||
$big = pow(1024,9);
|
||||
|
||||
// Find the smallest non-zero value
|
||||
$sml = array_reduce($array, function ($a, $b) {
|
||||
if ((1 <= $b['rx']) && ($b['rx'] < $b['tx'])) {
|
||||
$sm = $b['rx'];
|
||||
} else {
|
||||
$sm = $b['tx'];
|
||||
}
|
||||
if (($sm < 1) || ($a < $sm)) {
|
||||
return $a;
|
||||
} else {
|
||||
return $sm;
|
||||
}
|
||||
}, $big);
|
||||
|
||||
if ($sml >= $big/2) {
|
||||
$sml = 1;
|
||||
}
|
||||
|
||||
// divide by scale then round down to a power of 10
|
||||
$base = pow(10,floor(round(log10($sml/pow(1024,$scale)),3)));
|
||||
|
||||
// convert back to bytes
|
||||
$baseByte = $base * pow(1024, $scale);
|
||||
|
||||
// Don't make the bar invisable - must be > 5% difference
|
||||
if ($sml / $baseByte < 1.05) {
|
||||
$base = $base / 10;
|
||||
}
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
||||
function formatSize($bytes, $vnstatJsonVersion, $decimals = 2) {
|
||||
|
||||
// json version 1 = convert from KiB
|
||||
// json version 2 = convert from bytes
|
||||
if ($vnstatJsonVersion == 1) {
|
||||
$bytes *= 1024; // convert from kibibytes to bytes
|
||||
}
|
||||
|
||||
return formatBytes($bytes, $decimals);
|
||||
}
|
||||
|
||||
function getLargestPrefix($scale)
|
||||
{
|
||||
$suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
return $suffixes[$scale];
|
||||
}
|
||||
|
||||
function formatBytes($bytes, $decimals = 3) {
|
||||
|
||||
$scale = getScale($bytes);
|
||||
|
||||
return round($bytes/pow(1024, $scale), $decimals) .' '. getLargestPrefix($scale);
|
||||
}
|
||||
|
||||
function formatBytesTo($bytes, $scale, $decimals = 4) {
|
||||
|
||||
if ($bytes == 0) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
return number_format(($bytes / pow(1024, $scale)), $decimals, ".", "");
|
||||
}
|
||||
|
||||
function kibibytesToBytes($kibibytes, $vnstatJsonVersion) {
|
||||
if ($vnstatJsonVersion == 1) {
|
||||
return $kibibytes *= 1024;
|
||||
} else {
|
||||
return $kibibytes;
|
||||
}
|
||||
}
|
||||
|
||||
function sortingFunction($item1, $item2) {
|
||||
if ($item1['time'] == $item2['time']) {
|
||||
return 0;
|
||||
} else {
|
||||
return $item1['time'] > $item2['time'] ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
?>
|
|
@ -0,0 +1,284 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Alexander Marston (alexander.marston@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class vnStat {
|
||||
protected $executablePath;
|
||||
protected $vnstatVersion;
|
||||
protected $vnstatJsonVersion;
|
||||
protected $vnstatData;
|
||||
|
||||
public function __construct ($executablePath) {
|
||||
if (isset($executablePath)) {
|
||||
$this->executablePath = $executablePath;
|
||||
|
||||
// Execute a command to output a json dump of the vnstat data
|
||||
$vnstatStream = popen("$this->executablePath --json", 'r');
|
||||
|
||||
// Is the stream valid?
|
||||
if (is_resource($vnstatStream)) {
|
||||
$streamBuffer = '';
|
||||
|
||||
while (!feof($vnstatStream)) {
|
||||
$streamBuffer .= fgets($vnstatStream);
|
||||
}
|
||||
|
||||
// Close the handle
|
||||
pclose($vnstatStream);
|
||||
|
||||
$this->processVnstatData($streamBuffer);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
private function processVnstatData($vnstatJson) {
|
||||
$decodedJson = json_decode($vnstatJson, true);
|
||||
|
||||
// Check the JSON is valid
|
||||
if (json_last_error() != JSON_ERROR_NONE) {
|
||||
throw new Exception('JSON is invalid');
|
||||
}
|
||||
|
||||
$this->vnstatData = $decodedJson;
|
||||
$this->vnstatVersion = $decodedJson['vnstatversion'];
|
||||
$this->vnstatJsonVersion = $decodedJson['jsonversion'];
|
||||
}
|
||||
|
||||
public function getVnstatVersion() {
|
||||
return $this->vnstatVersion;
|
||||
}
|
||||
|
||||
public function getVnstatJsonVersion() {
|
||||
return $this->vnstatJsonVersion;
|
||||
}
|
||||
|
||||
public function getInterfaces() {
|
||||
// Create a placeholder array
|
||||
$vnstatInterfaces = [];
|
||||
|
||||
foreach($this->vnstatData['interfaces'] as $interface) {
|
||||
if ($this->vnstatJsonVersion == 1) {
|
||||
array_push($vnstatInterfaces, $interface['id']);
|
||||
} else {
|
||||
array_push($vnstatInterfaces, $interface['name']);
|
||||
}
|
||||
}
|
||||
|
||||
return $vnstatInterfaces;
|
||||
}
|
||||
|
||||
public function getInterfaceData($timeperiod, $type, $interface) {
|
||||
// If json version equals 1, add an 's' onto the end of each type.
|
||||
// e.g. 'top' becomes 'tops'
|
||||
$typeAppend = '';
|
||||
if ($this->vnstatJsonVersion == 1) {
|
||||
$typeAppend = 's';
|
||||
}
|
||||
|
||||
// Blank placeholder
|
||||
$trafficData = [];
|
||||
$i = -1;
|
||||
|
||||
// Get the array index for the chosen interface
|
||||
if ($this->vnstatJsonVersion == 1) {
|
||||
$arrayIndex = array_search($interface, array_column($this->vnstatData['interfaces'], 'id'));
|
||||
} else {
|
||||
$arrayIndex = array_search($interface, array_column($this->vnstatData['interfaces'], 'name'));
|
||||
}
|
||||
|
||||
if ($timeperiod == 'top10') {
|
||||
if ($type == 'table') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['top'.$typeAppend] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
$trafficData[$i]['label'] = date('d/m/Y', strtotime($traffic['date']['month'] . "/" . $traffic['date']['day'] . "/" . $traffic['date']['year']));;
|
||||
$trafficData[$i]['rx'] = formatSize($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = formatSize($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = formatSize(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['totalraw'] = ($traffic['rx'] + $traffic['tx']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (($this->vnstatJsonVersion > 1) && ($timeperiod == 'five')) {
|
||||
if ($type == 'table') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['fiveminute'] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
$trafficData[$i]['label'] = date("d/m/Y H:i", mktime($traffic['time']['hour'], $traffic['time']['minute'], 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']));
|
||||
$trafficData[$i]['time'] = mktime($traffic['time']['hour'], $traffic['time']['minute'], 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = formatSize($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = formatSize($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = formatSize(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
} else if ($type == 'graph') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['fiveminute'] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
$trafficData[$i]['label'] = sprintf("Date(%d, %d, %d, %d, %d)", $traffic['date']['year'], $traffic['date']['month']-1, $traffic['date']['day'], $traffic['time']['hour'], $traffic['time']['minute']);
|
||||
$trafficData[$i]['time'] = mktime($traffic['time']['hour'], $traffic['time']['minute'], 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = kibibytesToBytes($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = kibibytesToBytes($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = kibibytesToBytes(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($timeperiod == 'hourly') {
|
||||
if ($type == 'table') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['hour'.$typeAppend] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
if ($this->vnstatJsonVersion == 1) {
|
||||
$hour = $traffic['id'];
|
||||
} else {
|
||||
$hour = $traffic['time']['hour'];
|
||||
}
|
||||
|
||||
$trafficData[$i]['label'] = date("d/m/Y H:i", mktime($hour, 0, 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']));
|
||||
$trafficData[$i]['time'] = mktime($hour, 0, 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = formatSize($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = formatSize($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = formatSize(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else if ($type == 'graph') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['hour'.$typeAppend] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
if ($this->vnstatJsonVersion == 1) {
|
||||
$hour = $traffic['id'];
|
||||
} else {
|
||||
$hour = $traffic['time']['hour'];
|
||||
}
|
||||
|
||||
$trafficData[$i]['label'] = sprintf("Date(%d, %d, %d, %d)", $traffic['date']['year'], $traffic['date']['month']-1, $traffic['date']['day'], $hour);
|
||||
$trafficData[$i]['time'] = mktime($hour, 0, 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = kibibytesToBytes($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = kibibytesToBytes($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = kibibytesToBytes(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($timeperiod == 'daily') {
|
||||
if ($type == 'table') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['day'.$typeAppend] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
$trafficData[$i]['label'] = date('d/m/Y', mktime(0, 0, 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']));
|
||||
$trafficData[$i]['time'] = mktime(0, 0, 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = formatSize($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = formatSize($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = formatSize(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
} else if ($type == 'graph') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['day'.$typeAppend] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
$trafficData[$i]['label'] = sprintf("Date(%d, %d, %d)", $traffic['date']['year'], $traffic['date']['month']-1, $traffic['date']['day']);
|
||||
$trafficData[$i]['time'] = mktime(0, 0, 0, $traffic['date']['month'], $traffic['date']['day'], $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = kibibytesToBytes($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = kibibytesToBytes($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = kibibytesToBytes(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($timeperiod == 'monthly') {
|
||||
if ($type == 'table') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['month'.$typeAppend] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
$trafficData[$i]['label'] = date('F Y', mktime(0, 0, 0, $traffic['date']['month'], 10, $traffic['date']['year']));
|
||||
$trafficData[$i]['time'] = mktime(0, 0, 0, $traffic['date']['month'], 10, $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = formatSize($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = formatSize($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = formatSize(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
} else if ($type == 'graph') {
|
||||
foreach ($this->vnstatData['interfaces'][$arrayIndex]['traffic']['month'.$typeAppend] as $traffic) {
|
||||
if (is_array($traffic)) {
|
||||
$i++;
|
||||
|
||||
$trafficData[$i]['label'] = sprintf("Date(%d, %d, %d)", $traffic['date']['year'], $traffic['date']['month'] - 1, 10);
|
||||
$trafficData[$i]['time'] = mktime(0, 0, 0, $traffic['date']['month'], 10, $traffic['date']['year']);
|
||||
$trafficData[$i]['rx'] = kibibytesToBytes($traffic['rx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['tx'] = kibibytesToBytes($traffic['tx'], $this->vnstatJsonVersion);
|
||||
$trafficData[$i]['total'] = kibibytesToBytes(($traffic['rx'] + $traffic['tx']), $this->vnstatJsonVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($timeperiod != 'top10') {
|
||||
usort($trafficData, 'sortingFunction');
|
||||
}
|
||||
|
||||
if ($type == 'graph') {
|
||||
// Get the largest value and then prefix (B, KB, MB, GB, etc)
|
||||
$trafficLargestValue = getLargestValue($trafficData);
|
||||
$trafficScale = getScale($trafficLargestValue);
|
||||
$trafficLargestPrefix = getLargestPrefix($trafficScale);
|
||||
$trafficBase = getBaseValue($trafficData, $trafficScale);
|
||||
if (($trafficBase < .0099) && ($trafficScale >= 1))
|
||||
{
|
||||
$trafficScale = $trafficScale - 1;
|
||||
$trafficLargestPrefix = getLargestPrefix($trafficScale);
|
||||
$trafficBase = getBaseValue($trafficData, $trafficScale);
|
||||
}
|
||||
|
||||
foreach($trafficData as &$value) {
|
||||
$value['rx'] = formatBytesTo($value['rx'], $trafficScale);
|
||||
$value['tx'] = formatBytesTo($value['tx'], $trafficScale);
|
||||
$value['total'] = formatBytesTo($value['total'], $trafficScale);
|
||||
}
|
||||
|
||||
unset($value);
|
||||
$trafficData[0]['delimiter'] = $trafficLargestPrefix;
|
||||
$trafficData[0]['base'] = $trafficBase;
|
||||
}
|
||||
|
||||
return $trafficData;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019 Alexander Marston (alexander.marston@gmail.com)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Require includes
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
require __DIR__ . '/includes/utilities.php';
|
||||
require __DIR__ . '/includes/vnstat.php';
|
||||
require __DIR__ . '/includes/config.php';
|
||||
|
||||
if (isset($vnstat_config)) {
|
||||
$vnstat_cmd = $vnstat_bin_dir.' --config '.$vnstat_config;
|
||||
} else {
|
||||
$vnstat_cmd = $vnstat_bin_dir;
|
||||
}
|
||||
|
||||
if (empty($graph_type)) {
|
||||
$graph_type = 'linear';
|
||||
}
|
||||
|
||||
// Initiaite vnStat class
|
||||
$vnstat = new vnStat($vnstat_cmd);
|
||||
|
||||
// Initiate Smarty
|
||||
$smarty = new Smarty();
|
||||
|
||||
// Set the current year
|
||||
$smarty->assign('year', date("Y"));
|
||||
|
||||
// Set the list of interfaces
|
||||
$interface_list = $vnstat->getInterfaces();
|
||||
|
||||
// Set the current interface
|
||||
$thisInterface = "";
|
||||
|
||||
if (isset($_GET['i'])) {
|
||||
$interfaceChosen = rawurldecode($_GET['i']);
|
||||
|
||||
if (in_array($interfaceChosen, $interface_list, true)) {
|
||||
$thisInterface = $interfaceChosen;
|
||||
} else {
|
||||
$thisInterface = reset($interface_list);
|
||||
}
|
||||
} else {
|
||||
// Assume they mean the first interface
|
||||
$thisInterface = reset($interface_list);
|
||||
}
|
||||
|
||||
$smarty->assign('graph_type', $graph_type);
|
||||
|
||||
$smarty->assign('current_interface', $thisInterface);
|
||||
|
||||
// Assign interface options
|
||||
$smarty->assign('interface_list', $interface_list);
|
||||
|
||||
// JsonVersion
|
||||
$smarty->assign('jsonVersion', $vnstat->getVnstatJsonVersion());
|
||||
|
||||
// Populate table data
|
||||
if ($vnstat->getVnstatJsonVersion() > 1) {
|
||||
$fiveData = $vnstat->getInterfaceData('five', 'table', $thisInterface);
|
||||
$smarty->assign('fiveTableData', $fiveData);
|
||||
}
|
||||
|
||||
$hourlyData = $vnstat->getInterfaceData('hourly', 'table', $thisInterface);
|
||||
$smarty->assign('hourlyTableData', $hourlyData);
|
||||
|
||||
$dailyData = $vnstat->getInterfaceData('daily', 'table', $thisInterface);
|
||||
$smarty->assign('dailyTableData', $dailyData);
|
||||
|
||||
$monthlyData = $vnstat->getInterfaceData('monthly', 'table', $thisInterface);
|
||||
$smarty->assign('monthlyTableData', $monthlyData);
|
||||
|
||||
$top10Data = $vnstat->getInterfaceData('top10', 'table', $thisInterface);
|
||||
$smarty->assign('top10TableData', $top10Data);
|
||||
|
||||
// Populate graph data
|
||||
if ($vnstat->getVnstatJsonVersion() > 1) {
|
||||
$fiveGraphData = $vnstat->getInterfaceData('five', 'graph', $thisInterface);
|
||||
$smarty->assign('fiveGraphData', $fiveGraphData);
|
||||
$smarty->assign('fiveLargestPrefix', $fiveGraphData[0]['delimiter']);
|
||||
$smarty->assign('fiveBase', $fiveGraphData[0]['base']);
|
||||
}
|
||||
|
||||
$hourlyGraphData = $vnstat->getInterfaceData('hourly', 'graph', $thisInterface);
|
||||
$smarty->assign('hourlyGraphData', $hourlyGraphData);
|
||||
$smarty->assign('hourlyLargestPrefix', $hourlyGraphData[0]['delimiter']);
|
||||
$smarty->assign('hourlyBase', $hourlyGraphData[0]['base']);
|
||||
|
||||
$dailyGraphData = $vnstat->getInterfaceData('daily', 'graph', $thisInterface);
|
||||
$smarty->assign('dailyGraphData', $dailyGraphData);
|
||||
$smarty->assign('dailyLargestPrefix', $dailyGraphData[0]['delimiter']);
|
||||
$smarty->assign('dailyBase', $dailyGraphData[0]['base']);
|
||||
|
||||
$monthlyGraphData = $vnstat->getInterfaceData('monthly', 'graph', $thisInterface);
|
||||
$smarty->assign('monthlyGraphData', $monthlyGraphData);
|
||||
$smarty->assign('monthlyLargestPrefix', $monthlyGraphData[0]['delimiter']);
|
||||
|
||||
// Display the page
|
||||
$smarty->display('templates/site_index.tpl');
|
||||
|
||||
?>
|
|
@ -0,0 +1,16 @@
|
|||
<footer class="footer">
|
||||
<div class="container">
|
||||
<span class="text-muted">Copyright (C) {$year} Alexander Marston -
|
||||
<a href="https://github.com/alexandermarston/vnstat-dashboard">vnstat-dashboard</a>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||||
{include file="module_graph_js.tpl"}
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
<div class="container">
|
||||
<ul class="nav nav-tabs" id="graphTab" role="tablist">
|
||||
{if $jsonVersion gt 1}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="five-graph-tab" data-toggle="tab" href="#five-graph" role="tab" aria-controls="five-graph" aria-selected="true">Five Minute Graph</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="hourly-graph-tab" data-toggle="tab" href="#hourly-graph" role="tab" aria-controls="hourly-graph" aria-selected="false">Hourly Graph</a>
|
||||
</li>
|
||||
{else}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="hourly-graph-tab" data-toggle="tab" href="#hourly-graph" role="tab" aria-controls="hourly-graph" aria-selected="true">Hourly Graph</a>
|
||||
</li>
|
||||
{/if}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="daily-graph-tab" data-toggle="tab" href="#daily-graph" role="tab" aria-controls="daily-graph" aria-selected="false">Daily Graph</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="monthly-graph-tab" data-toggle="tab" href="#monthly-graph" role="tab" aria-controls="monthly-graph" aria-selected="false">Monthly Graph</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
{if $jsonVersion gt 1}
|
||||
<div class="tab-pane fade show active" id="five-graph" role="tabpanel" aria-labelledby="five-graph-tab">
|
||||
<div id="fiveNetworkTrafficGraph" style="height: 300px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="hourly-graph" role="tabpanel" aria-labelledby="hourly-graph-tab">
|
||||
<div id="hourlyNetworkTrafficGraph" style="height: 300px;"></div>
|
||||
</div>
|
||||
{else}
|
||||
<div class="tab-pane fade show active" id="hourly-graph" role="tabpanel" aria-labelledby="hourly-graph-tab">
|
||||
<div id="hourlyNetworkTrafficGraph" style="height: 300px;"></div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="tab-pane fade" id="daily-graph" role="tabpanel" aria-labelledby="daily-graph-tab">
|
||||
<div id="dailyNetworkTrafficGraph" style="height: 300px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="monthly-graph" role="tabpanel" aria-labelledby="monthly-graph-tab">
|
||||
<div id="monthlyNetworkTrafficGraph" style="height: 300px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,256 @@
|
|||
<script type="text/javascript">
|
||||
google.charts.load('current', { packages: [ 'bar' ] });
|
||||
google.charts.load("current", { packages: [ 'corechart' ] });
|
||||
|
||||
google.charts.setOnLoadCallback(drawFiveChart);
|
||||
google.charts.setOnLoadCallback(drawHourlyChart);
|
||||
google.charts.setOnLoadCallback(drawDailyChart);
|
||||
google.charts.setOnLoadCallback(drawMonthlyChart);
|
||||
|
||||
function drawFiveChart()
|
||||
{
|
||||
{if $jsonVersion gt 1}
|
||||
var data = new google.visualization.DataTable();
|
||||
|
||||
data.addColumn('datetime', 'Time');
|
||||
data.addColumn('number', 'Traffic In');
|
||||
data.addColumn('number', 'Traffic Out');
|
||||
data.addColumn('number', 'Total Traffic');
|
||||
|
||||
data.addRows([
|
||||
{foreach from=$fiveGraphData key=key item=value}
|
||||
[new {$value.label}, {$value.rx}, {$value.tx}, {$value.total}],
|
||||
{/foreach}
|
||||
]);
|
||||
|
||||
let endD = (new {$fiveGraphData[0]['label']}).getTime();
|
||||
|
||||
let options = {
|
||||
title: 'Five minute Network Traffic',
|
||||
orientation: 'horizontal',
|
||||
legend: { position: 'right' },
|
||||
explorer: {
|
||||
axis: 'horizontal',
|
||||
zoomDelta: 1.1,
|
||||
maxZoomIn: 0.1,
|
||||
maxZoomOut: 10.0
|
||||
},
|
||||
vAxis: {
|
||||
format: '###.### {$fiveLargestPrefix}'
|
||||
{if $graph_type == 'log'}
|
||||
,scaleType: 'log',
|
||||
baseline: {$fiveBase}
|
||||
{/if}
|
||||
},
|
||||
hAxis: {
|
||||
direction: -1,
|
||||
format: 'd/H:mm',
|
||||
{if $jsonVersion > 1}
|
||||
title: 'Day/Hour:Minute (Scroll to zoom, Drag to pan)',
|
||||
{else}
|
||||
title: 'Day/Hour:Minute',
|
||||
{/if}
|
||||
viewWindow: {
|
||||
min: 'Date('+(endD-7050000).toString()+')',
|
||||
max: 'Date('+(endD+150000).toString()+')'
|
||||
},
|
||||
ticks: [
|
||||
{foreach from=$fiveGraphData key=key item=value}
|
||||
new {$value.label},
|
||||
{/foreach}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var formatDate = new google.visualization.DateFormat({ pattern: 'dd/MM/yyyy HH:mm' });
|
||||
formatDate.format(data, 0);
|
||||
|
||||
var formatNumber = new google.visualization.NumberFormat({ pattern: '##.## {$fiveLargestPrefix}' });
|
||||
formatNumber.format(data, 1);
|
||||
formatNumber.format(data, 2);
|
||||
formatNumber.format(data, 3);
|
||||
|
||||
let chart = new google.visualization.BarChart(document.getElementById('fiveNetworkTrafficGraph'));
|
||||
chart.draw(data, google.charts.Bar.convertOptions(options));
|
||||
{/if}
|
||||
}
|
||||
|
||||
function drawHourlyChart()
|
||||
{
|
||||
var data = new google.visualization.DataTable();
|
||||
|
||||
data.addColumn('date', 'Hour');
|
||||
data.addColumn('number', 'Traffic In');
|
||||
data.addColumn('number', 'Traffic Out');
|
||||
data.addColumn('number', 'Total Traffic');
|
||||
|
||||
data.addRows([
|
||||
{foreach from=$hourlyGraphData key=key item=value}
|
||||
[new {$value.label}, {$value.rx}, {$value.tx}, {$value.total}],
|
||||
{/foreach}
|
||||
]);
|
||||
|
||||
let endD = (new {$hourlyGraphData[0]['label']}).getTime();
|
||||
|
||||
let options = {
|
||||
title: 'Hourly Network Traffic',
|
||||
orientation: 'horizontal',
|
||||
legend: { position: 'right' },
|
||||
explorer: {
|
||||
axis: 'horizontal',
|
||||
zoomDelta: 1.1,
|
||||
maxZoomIn: 0.1,
|
||||
maxZoomOut: 10.0
|
||||
},
|
||||
vAxis: {
|
||||
format: '###.### {$hourlyLargestPrefix}'
|
||||
{if $graph_type == 'log'}
|
||||
,scaleType: 'log',
|
||||
baseline: {$hourlyBase}
|
||||
{/if}
|
||||
},
|
||||
hAxis: {
|
||||
{if $jsonVersion > 1}
|
||||
title: 'Day/Hour (Scroll to zoom, Drag to pan)',
|
||||
{else}
|
||||
title: 'Day/Hour',
|
||||
{/if}
|
||||
format: 'd/H',
|
||||
direction: -1,
|
||||
viewWindow: {
|
||||
min: 'Date('+(endD-84600000).toString()+')',
|
||||
max: 'Date('+(endD+1800000).toString()+')'
|
||||
},
|
||||
ticks: [
|
||||
{foreach from=$hourlyGraphData key=key item=value}
|
||||
new {$value.label},
|
||||
{/foreach}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var formatDate = new google.visualization.DateFormat({ pattern: 'dd/MM/yyyy HH:mm' });
|
||||
formatDate.format(data, 0);
|
||||
|
||||
var formatNumber = new google.visualization.NumberFormat({ pattern: '##.## {$hourlyLargestPrefix}' });
|
||||
formatNumber.format(data, 1);
|
||||
formatNumber.format(data, 2);
|
||||
formatNumber.format(data, 3);
|
||||
|
||||
let chart = new google.visualization.BarChart(document.getElementById('hourlyNetworkTrafficGraph'));
|
||||
chart.draw(data, google.charts.Bar.convertOptions(options));
|
||||
}
|
||||
|
||||
function drawDailyChart()
|
||||
{
|
||||
var data = new google.visualization.DataTable();
|
||||
|
||||
data.addColumn('date', 'Day');
|
||||
data.addColumn('number', 'Traffic In');
|
||||
data.addColumn('number', 'Traffic Out');
|
||||
data.addColumn('number', 'Total Traffic');
|
||||
|
||||
data.addRows([
|
||||
{foreach from=$dailyGraphData key=key item=value}
|
||||
[new {$value.label}, {$value.rx}, {$value.tx}, {$value.total}],
|
||||
{/foreach}
|
||||
]);
|
||||
|
||||
let endD = (new {$dailyGraphData[0]['label']}).getTime();
|
||||
|
||||
let options = {
|
||||
title: 'Daily Network Traffic',
|
||||
orientation: 'horizontal',
|
||||
legend: { position: 'right' },
|
||||
explorer: {
|
||||
axis: 'horizontal',
|
||||
zoomDelta: 1.1,
|
||||
maxZoomIn: 0.1,
|
||||
maxZoomOut: 10.0
|
||||
},
|
||||
vAxis: {
|
||||
format: '###.### {$dailyLargestPrefix}'
|
||||
{if $graph_type == 'log'}
|
||||
,scaleType: 'log',
|
||||
baseline: {$dailyBase}
|
||||
{/if}
|
||||
},
|
||||
hAxis: {
|
||||
{if $jsonVersion > 1}
|
||||
title: 'Day (Scroll to zoom, Drag to pan)',
|
||||
{else}
|
||||
title: 'Day',
|
||||
{/if}
|
||||
format: 'dd/MM/YYYY',
|
||||
viewWindow: {
|
||||
min: 'Date('+(endD-2548800000).toString()+')',
|
||||
max: 'Date('+(endD+43200000).toString()+')'
|
||||
},
|
||||
direction: -1,
|
||||
ticks: [
|
||||
{foreach from=$dailyGraphData key=key item=value}
|
||||
new {$value.label},
|
||||
{/foreach}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
var formatDate = new google.visualization.DateFormat({ pattern: 'dd/MM/yyyy' });
|
||||
formatDate.format(data, 0);
|
||||
|
||||
var formatNumber = new google.visualization.NumberFormat({ pattern: '##.## {$dailyLargestPrefix}' });
|
||||
formatNumber.format(data, 1);
|
||||
formatNumber.format(data, 2);
|
||||
formatNumber.format(data, 3);
|
||||
|
||||
let chart = new google.visualization.BarChart(document.getElementById('dailyNetworkTrafficGraph'));
|
||||
chart.draw(data, google.charts.Bar.convertOptions(options));
|
||||
}
|
||||
|
||||
function drawMonthlyChart()
|
||||
{
|
||||
var data = new google.visualization.DataTable();
|
||||
|
||||
data.addColumn('date', 'Month');
|
||||
data.addColumn('number', 'Traffic In');
|
||||
data.addColumn('number', 'Traffic Out');
|
||||
data.addColumn('number', 'Total Traffic');
|
||||
|
||||
data.addRows([
|
||||
{foreach from=$monthlyGraphData key=key item=value}
|
||||
[new {$value.label}, {$value.rx}, {$value.tx}, {$value.total}],
|
||||
{/foreach}
|
||||
]);
|
||||
|
||||
let options = {
|
||||
title: 'Monthly Network Traffic',
|
||||
orientation: 'horizontal',
|
||||
legend: { position: 'right' },
|
||||
explorer: {
|
||||
axis: 'horizontal',
|
||||
zoomDelta: 1.1,
|
||||
maxZoomIn: 0.1,
|
||||
maxZoomOut: 10.0
|
||||
},
|
||||
vAxis: {
|
||||
format: '##.## {$monthlyLargestPrefix}'
|
||||
},
|
||||
hAxis: {
|
||||
title: 'Month',
|
||||
format: 'MMMM YYYY',
|
||||
direction: -1
|
||||
}
|
||||
};
|
||||
|
||||
var formatDate = new google.visualization.DateFormat({ pattern: 'MMMM YYYY' });
|
||||
formatDate.format(data, 0);
|
||||
|
||||
var formatNumber = new google.visualization.NumberFormat({ pattern: '##.## {$monthlyLargestPrefix}' });
|
||||
formatNumber.format(data, 1);
|
||||
formatNumber.format(data, 2);
|
||||
formatNumber.format(data, 3);
|
||||
|
||||
let chart = new google.visualization.BarChart(document.getElementById('monthlyNetworkTrafficGraph'));
|
||||
chart.draw(data, google.charts.Bar.convertOptions(options));
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Network Traffic</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="./assets/css/style.css">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar sticky-top navbar-light bg-light">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="#">Network Traffic ({$current_interface})</a>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Interface Selection
|
||||
</button>
|
||||
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
{foreach from=$interface_list item=value}
|
||||
<a class="dropdown-item" href="?i={$value}">{$value}</a>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
|
@ -0,0 +1,146 @@
|
|||
<div class="container">
|
||||
<ul class="nav nav-tabs" id="tableTab" role="tablist">
|
||||
{if $jsonVersion gt 1}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="five-table-tab" data-toggle="tab" href="#five-table" role="tab" aria-controls="five-table" aria-selected="true">Five Minute</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="hourly-table-tab" data-toggle="tab" href="#hourly-table" role="tab" aria-controls="hourly-table" aria-selected="false">Hourly</a>
|
||||
</li>
|
||||
{else}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="hourly-table-tab" data-toggle="tab" href="#hourly-table" role="tab" aria-controls="hourly-table" aria-selected="true">Hourly</a>
|
||||
</li>
|
||||
{/if}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="daily-table-tab" data-toggle="tab" href="#daily-table" role="tab" aria-controls="daily-table" aria-selected="false">Daily</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="monthly-table-tab" data-toggle="tab" href="#monthly-table" role="tab" aria-controls="monthly-table" aria-selected="false">Monthly</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="top10-table-tab" data-toggle="tab" href="#top10-table" role="tab" aria-controls="top10-table" aria-selected="false">Top 10</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="tableTabContent">
|
||||
{if $jsonVersion gt 1}
|
||||
<div class="tab-pane fade show active" id="five-table" role="tabpanel" aria-labelledby="five-table-tab">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Received</th>
|
||||
<th>Sent</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach from=$fiveTableData key=key item=value}
|
||||
<tr>
|
||||
<td>{$value.label}</td>
|
||||
<td>{$value.rx}</td>
|
||||
<td>{$value.tx}</td>
|
||||
<td>{$value.total}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="hourly-table" role="tabpanel" aria-labelledby="hourly-table-tab">
|
||||
{else}
|
||||
<div class="tab-pane fade show active" id="hourly-table" role="tabpanel" aria-labelledby="hourly-table-tab">
|
||||
{/if}
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hour</th>
|
||||
<th>Received</th>
|
||||
<th>Sent</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach from=$hourlyTableData key=key item=value}
|
||||
<tr>
|
||||
<td>{$value.label}</td>
|
||||
<td>{$value.rx}</td>
|
||||
<td>{$value.tx}</td>
|
||||
<td>{$value.total}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="daily-table" role="tabpanel" aria-labelledby="daily-table-tab">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Day</th>
|
||||
<th>Received</th>
|
||||
<th>Sent</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach from=$dailyTableData key=key item=value}
|
||||
<tr>
|
||||
<td>{$value.label}</td>
|
||||
<td>{$value.rx}</td>
|
||||
<td>{$value.tx}</td>
|
||||
<td>{$value.total}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="monthly-table" role="tabpanel" aria-labelledby="monthly-table-tab">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Month</th>
|
||||
<th>Received</th>
|
||||
<th>Sent</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach from=$monthlyTableData key=key item=value}
|
||||
<tr>
|
||||
<td>{$value.label}</td>
|
||||
<td>{$value.rx}</td>
|
||||
<td>{$value.tx}</td>
|
||||
<td>{$value.total}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="top10-table" role="tabpanel" aria-labelledby="top10-table-tab">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Day</th>
|
||||
<th>Received</th>
|
||||
<th>Sent</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach from=$top10TableData key=key item=value}
|
||||
<tr>
|
||||
<td>{$value.label}</td>
|
||||
<td>{$value.rx}</td>
|
||||
<td>{$value.tx}</td>
|
||||
<td>{$value.total}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
{include file="module_header.tpl"}
|
||||
|
||||
{include file="module_graph.tpl"}
|
||||
|
||||
{include file="module_table.tpl"}
|
||||
|
||||
{include file="module_footer.tpl"}
|
|
@ -0,0 +1,11 @@
|
|||
version: '3'
|
||||
services:
|
||||
vnstat-dashboard:
|
||||
build: .
|
||||
network_mode: "host"
|
||||
container_name: vnstat-dashboard
|
||||
volumes:
|
||||
- /var/lib/vnstat:/var/lib/vnstat
|
||||
- /usr/bin/vnstat:/usr/local/bin/vnstat
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
Loading…
Reference in New Issue