start work on traffic-view refactoring
Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
@@ -26,7 +26,7 @@
|
|||||||
const AREA = 'customer';
|
const AREA = 'customer';
|
||||||
require __DIR__ . '/lib/init.php';
|
require __DIR__ . '/lib/init.php';
|
||||||
|
|
||||||
use Froxlor\Database\Database;
|
use Froxlor\Traffic\Traffic;
|
||||||
use Froxlor\Settings;
|
use Froxlor\Settings;
|
||||||
use Froxlor\UI\Panel\UI;
|
use Froxlor\UI\Panel\UI;
|
||||||
use Froxlor\UI\Request;
|
use Froxlor\UI\Request;
|
||||||
@@ -37,13 +37,19 @@ if (Settings::IsInList('panel.customer_hide_options', 'traffic')) {
|
|||||||
Response::redirectTo('customer_index.php');
|
Response::redirectTo('customer_index.php');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$range = Request::get('range', 'days:30');
|
||||||
|
|
||||||
if ($page === null || $page == 'overview') {
|
if ($page === null || $page == 'overview') {
|
||||||
|
|
||||||
} elseif ($page == 'current') {
|
} elseif ($page == 'current') {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UI::view('user/traffic.html.twig', [
|
try {
|
||||||
'metrics' => \Froxlor\Traffic\Traffic::getCustomerMetrics($userinfo),
|
$context = Traffic::getCustomerStats($userinfo, $range);
|
||||||
'chart' => \Froxlor\Traffic\Traffic::getCustomerChart($userinfo, 30),
|
} catch (Exception $e) {
|
||||||
]);
|
Response::dynamicError($e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass metrics to the view
|
||||||
|
UI::view('user/traffic.html.twig', $context);
|
||||||
|
|||||||
@@ -32,18 +32,33 @@ class Traffic
|
|||||||
{
|
{
|
||||||
public static function getCustomerStats($userinfo, $range = null): array
|
public static function getCustomerStats($userinfo, $range = null): array
|
||||||
{
|
{
|
||||||
$trafficCollection = (new Collection(\Froxlor\Api\Commands\Traffic::class, $userinfo, self::getParamsByRange($range, ['customer_traffic' => true,])))
|
$trafficCollectionObj = (new Collection(\Froxlor\Api\Commands\Traffic::class, $userinfo, self::getParamsByRange($range, ['customer_traffic' => true,])));
|
||||||
->has('customer', Customers::class, 'customerid', 'customerid')
|
if ($userinfo['adminsession'] == 1) {
|
||||||
->get();
|
$trafficCollectionObj->has('customer', Customers::class, 'customerid', 'customerid');
|
||||||
|
}
|
||||||
|
$trafficCollection = $trafficCollectionObj->get();
|
||||||
|
|
||||||
// build stats for each user
|
// build stats for each user
|
||||||
$users = [];
|
$users = [];
|
||||||
|
$years = [];
|
||||||
|
$months = [];
|
||||||
foreach ($trafficCollection['data']['list'] as $item) {
|
foreach ($trafficCollection['data']['list'] as $item) {
|
||||||
|
// per user total
|
||||||
$users[$item['customerid']]['loginname'] = $item['customer']['loginname'];
|
$users[$item['customerid']]['loginname'] = $item['customer']['loginname'];
|
||||||
$users[$item['customerid']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
$users[$item['customerid']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
||||||
$users[$item['customerid']]['http'] += $item['http'];
|
$users[$item['customerid']]['http'] += $item['http'];
|
||||||
$users[$item['customerid']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
$users[$item['customerid']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||||
$users[$item['customerid']]['mail'] += $item['mail'];
|
$users[$item['customerid']]['mail'] += $item['mail'];
|
||||||
|
// per year
|
||||||
|
$years[$item['year']]['total']['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
||||||
|
$years[$item['year']]['total']['http'] += $item['http'];
|
||||||
|
$years[$item['year']]['total']['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||||
|
$years[$item['year']]['total']['mail'] += $item['mail'];
|
||||||
|
// per month
|
||||||
|
$months[$item['year']][$item['month']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
|
||||||
|
$months[$item['year']][$item['month']]['http'] += $item['http'];
|
||||||
|
$months[$item['year']][$item['month']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
|
||||||
|
$months[$item['year']][$item['month']]['mail'] += $item['mail'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate overview for given range from users
|
// calculate overview for given range from users
|
||||||
@@ -58,10 +73,12 @@ class Traffic
|
|||||||
return [
|
return [
|
||||||
'metrics' => $metrics,
|
'metrics' => $metrics,
|
||||||
'users' => $users,
|
'users' => $users,
|
||||||
|
'years' => $years,
|
||||||
|
'months' => $months
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getParamsByRange(string $range, array $params = [])
|
private static function getParamsByRange($range = null, array $params = [])
|
||||||
{
|
{
|
||||||
$dateParams = [];
|
$dateParams = [];
|
||||||
|
|
||||||
|
|||||||
@@ -356,6 +356,7 @@ return [
|
|||||||
'store_defaultindex' => 'Standard-Index-Datei im Kundenordner erstellen',
|
'store_defaultindex' => 'Standard-Index-Datei im Kundenordner erstellen',
|
||||||
'phpfpm_settings' => 'PHP-FPM',
|
'phpfpm_settings' => 'PHP-FPM',
|
||||||
'traffic' => 'Traffic',
|
'traffic' => 'Traffic',
|
||||||
|
'traffic_sub' => 'Details der Traffic-Nutzung',
|
||||||
'customertraffic' => 'Kunden',
|
'customertraffic' => 'Kunden',
|
||||||
'assignedmax' => 'Zugewiesen / Max.',
|
'assignedmax' => 'Zugewiesen / Max.',
|
||||||
'usedmax' => 'Benutzt / Max.',
|
'usedmax' => 'Benutzt / Max.',
|
||||||
|
|||||||
@@ -358,6 +358,7 @@ return [
|
|||||||
'store_defaultindex' => 'Store default index-file to customers docroot',
|
'store_defaultindex' => 'Store default index-file to customers docroot',
|
||||||
'phpfpm_settings' => 'PHP-FPM',
|
'phpfpm_settings' => 'PHP-FPM',
|
||||||
'traffic' => 'Traffic',
|
'traffic' => 'Traffic',
|
||||||
|
'traffic_sub' => 'Details on traffic usage',
|
||||||
'domaintraffic' => 'Domains',
|
'domaintraffic' => 'Domains',
|
||||||
'customertraffic' => 'Customers',
|
'customertraffic' => 'Customers',
|
||||||
'assignedmax' => 'Assigned / Max',
|
'assignedmax' => 'Assigned / Max',
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h5 class="mb-1">
|
<h5 class="mb-1">
|
||||||
<i class="fa fa-chart-area me-1"></i> Traffic
|
<i class="fa fa-chart-area me-1"></i>
|
||||||
|
{{ lng('admin.traffic') }}
|
||||||
</h5>
|
</h5>
|
||||||
<span class="text-muted">View your traffic</span>
|
<span class="text-muted">{{ lng('admin.traffic_sub') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -29,6 +30,15 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row row-cols-2 g-0 bg-white rounded shadow-sm mb-4">
|
||||||
|
<div class="col p-3 border-end">
|
||||||
|
<canvas id="trafficsummary" style="max-height:30vh;"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="col p-3 border-end">
|
||||||
|
<canvas id="customersummary" style="max-height:30vh;"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Overview for given range -->
|
<!-- Overview for given range -->
|
||||||
<div class="row row-cols-4 g-0 bg-white rounded shadow-sm mb-4">
|
<div class="row row-cols-4 g-0 bg-white rounded shadow-sm mb-4">
|
||||||
<div class="col p-3 border-end">
|
<div class="col p-3 border-end">
|
||||||
@@ -49,40 +59,213 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Overview for given range by user -->
|
{% if userinfo.adminsession == 1 %}
|
||||||
<h4 class="page-header">Traffic by customers</h4>
|
<!-- Overview for given range by user -->
|
||||||
{% if users is not empty %}
|
<h4 class="page-header">Traffic by customers</h4>
|
||||||
<div class="card table-responsive">
|
{% if users is not empty %}
|
||||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
<div class="card table-responsive">
|
||||||
<thead>
|
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||||
<tr>
|
<thead>
|
||||||
<th scope="col">{{ lng('login.username') }}</th>
|
<tr>
|
||||||
<th scope="col">Total</th>
|
<th scope="col">{{ lng('login.username') }}</th>
|
||||||
<th scope="col">HTTP</th>
|
<th scope="col">Total</th>
|
||||||
<th scope="col">FTP</th>
|
<th scope="col">HTTP</th>
|
||||||
<th scope="col">Mail
|
<th scope="col">FTP</th>
|
||||||
</th>
|
<th scope="col">Mail
|
||||||
</tr>
|
</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{% for user in users %}
|
<tbody>
|
||||||
<tr>
|
{% for uid,user in users %}
|
||||||
<td>{{ user.loginname }}</td>
|
<tr>
|
||||||
<td>{{ user.total|formatBytes }}</td>
|
<td>
|
||||||
<td>{{ user.http|formatBytes }}</td>
|
<a href="{{ linker({'section':'customers','page':'customers','action':'su','id':uid}) }}">{{ user.loginname }}</a>
|
||||||
<td>{{ user.ftp|formatBytes }}</td>
|
</td>
|
||||||
<td>{{ user.mail|formatBytes }}</td>
|
<td>{{ user.total|formatBytes }}</td>
|
||||||
</tr>
|
<td>{{ user.http|formatBytes }}</td>
|
||||||
{% endfor %}
|
<td>{{ user.ftp|formatBytes }}</td>
|
||||||
</tbody>
|
<td>{{ user.mail|formatBytes }}</td>
|
||||||
</table>
|
</tr>
|
||||||
</div>
|
{% endfor %}
|
||||||
{% else %}
|
</tbody>
|
||||||
<div class="card">
|
</table>
|
||||||
<div class="card-body">
|
|
||||||
<p>No data for given range found.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% else %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<p>No data for given range found.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const labelsS = ['HTTP', 'FTP', 'Mail'];
|
||||||
|
|
||||||
|
const dataS = {
|
||||||
|
labels: labelsS,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Traffic summary',
|
||||||
|
backgroundColor: ['rgb(255, 99, 132)', 'rgb(200, 199, 132)', 'rgb(255, 99, 0)'],
|
||||||
|
data: [{value: {{ metrics.http }}, formatted: '{{ metrics.http|formatBytes }}'}, {value: {{ metrics.ftp }}, formatted: '{{ metrics.ftp|formatBytes }}'}, {value: {{ metrics.mail }}, formatted: '{{ metrics.mail|formatBytes }}'}]
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
const configS = {
|
||||||
|
type: 'pie',
|
||||||
|
data: dataS,
|
||||||
|
options: {
|
||||||
|
parsing: {
|
||||||
|
key: 'value'
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Total traffic'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: 'right'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
usePointStyle: true,
|
||||||
|
callbacks: {
|
||||||
|
label: (data) => {
|
||||||
|
return data.label + ' ' + data.raw.formatted
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sChart = new Chart(document.getElementById('trafficsummary'), configS);
|
||||||
|
|
||||||
|
{% if userinfo.adminsession == 1 %}
|
||||||
|
const labelsC = [];
|
||||||
|
const dataValues = [];
|
||||||
|
{% for user in users|sort((a, b) => a.total <=> b.total)|slice(0, 5) %}
|
||||||
|
labelsC.push('{{ user.loginname }}');
|
||||||
|
dataValues.push({value: {{ user.total }}, formatted: '{{ user.total|formatBytes }}'});
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
const dataC = {
|
||||||
|
labels: labelsC,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Top 5 customers',
|
||||||
|
backgroundColor: ['rgb(255, 99, 132)', 'rgb(200, 199, 132)', 'rgb(255, 99, 0)', 'rgb(100, 100, 132)', 'rgb(240, 150, 232)'],
|
||||||
|
data: dataValues
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
const configC = {
|
||||||
|
type: 'pie',
|
||||||
|
data: dataC,
|
||||||
|
options: {
|
||||||
|
parsing: {
|
||||||
|
key: 'value'
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'Top 5 customers'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: 'right'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
usePointStyle: true,
|
||||||
|
callbacks: {
|
||||||
|
label: (data) => {
|
||||||
|
return data.label + ' ' + data.raw.formatted
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cChart = new Chart(document.getElementById('customersummary'), configC);
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
const labelsC = [];
|
||||||
|
const dataValues = [];
|
||||||
|
{% for yr,year in years %}
|
||||||
|
labelsC.push('{{ yr }}');
|
||||||
|
//dataValues.push({value: {{ year.total.total }}, formatted: '{{ year.total.total|formatBytes }}', year: '{{ yr }}'});
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
const dataC = {
|
||||||
|
labels: labelsC,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: 'HTTP traffic',
|
||||||
|
backgroundColor: 'rgb(255, 99, 132)',
|
||||||
|
data: [{% for yr,year in years %}{value: {{ year.total.http }}, formatted: '{{ year.total.http|formatBytes }}', year: '{{ yr }}'},{% endfor %}],
|
||||||
|
parsing: {
|
||||||
|
xAxisKey: 'year'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'FTP traffic',
|
||||||
|
backgroundColor: 'rgb(200, 199, 132)',
|
||||||
|
data: [{% for yr,year in years %}{value: {{ year.total.ftp }}, formatted: '{{ year.total.ftp|formatBytes }}', year: '{{ yr }}'},{% endfor %}],
|
||||||
|
parsing: {
|
||||||
|
xAxisKey: 'year'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Mail traffic',
|
||||||
|
backgroundColor: 'rgb(255, 99, 0)',
|
||||||
|
data: [{% for yr,year in years %}{value: {{ year.total.mail }}, formatted: '{{ year.total.mail|formatBytes }}', year: '{{ yr }}'},{% endfor %}],
|
||||||
|
parsing: {
|
||||||
|
xAxisKey: 'year'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const configC = {
|
||||||
|
type: 'bar',
|
||||||
|
data: dataC,
|
||||||
|
options: {
|
||||||
|
parsing: {
|
||||||
|
yAxisKey: 'value'
|
||||||
|
},
|
||||||
|
responsive: true,
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
stacked: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: 'All-time by year'
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
usePointStyle: true,
|
||||||
|
callbacks: {
|
||||||
|
label: (data) => {
|
||||||
|
return data.raw.formatted
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const cChart = new Chart(document.getElementById('customersummary'), configC);
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user