start work on traffic-view refactoring

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann 2022-09-07 12:56:02 +02:00
parent 5f95293e0d
commit 6f2652f9dd
No known key found for this signature in database
GPG Key ID: C121F97338D7A352
5 changed files with 252 additions and 44 deletions

View File

@ -26,7 +26,7 @@
const AREA = 'customer';
require __DIR__ . '/lib/init.php';
use Froxlor\Database\Database;
use Froxlor\Traffic\Traffic;
use Froxlor\Settings;
use Froxlor\UI\Panel\UI;
use Froxlor\UI\Request;
@ -37,13 +37,19 @@ if (Settings::IsInList('panel.customer_hide_options', 'traffic')) {
Response::redirectTo('customer_index.php');
}
$range = Request::get('range', 'days:30');
if ($page === null || $page == 'overview') {
} elseif ($page == 'current') {
}
UI::view('user/traffic.html.twig', [
'metrics' => \Froxlor\Traffic\Traffic::getCustomerMetrics($userinfo),
'chart' => \Froxlor\Traffic\Traffic::getCustomerChart($userinfo, 30),
]);
try {
$context = Traffic::getCustomerStats($userinfo, $range);
} catch (Exception $e) {
Response::dynamicError($e->getMessage());
}
// pass metrics to the view
UI::view('user/traffic.html.twig', $context);

View File

@ -32,18 +32,33 @@ class Traffic
{
public static function getCustomerStats($userinfo, $range = null): array
{
$trafficCollection = (new Collection(\Froxlor\Api\Commands\Traffic::class, $userinfo, self::getParamsByRange($range, ['customer_traffic' => true,])))
->has('customer', Customers::class, 'customerid', 'customerid')
->get();
$trafficCollectionObj = (new Collection(\Froxlor\Api\Commands\Traffic::class, $userinfo, self::getParamsByRange($range, ['customer_traffic' => true,])));
if ($userinfo['adminsession'] == 1) {
$trafficCollectionObj->has('customer', Customers::class, 'customerid', 'customerid');
}
$trafficCollection = $trafficCollectionObj->get();
// build stats for each user
$users = [];
$years = [];
$months = [];
foreach ($trafficCollection['data']['list'] as $item) {
// per user total
$users[$item['customerid']]['loginname'] = $item['customer']['loginname'];
$users[$item['customerid']]['total'] += ($item['http'] + $item['ftp_up'] + $item['ftp_down'] + $item['mail']);
$users[$item['customerid']]['http'] += $item['http'];
$users[$item['customerid']]['ftp'] += ($item['ftp_up'] + $item['ftp_down']);
$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
@ -58,10 +73,12 @@ class Traffic
return [
'metrics' => $metrics,
'users' => $users,
'years' => $years,
'months' => $months
];
}
private static function getParamsByRange(string $range, array $params = [])
private static function getParamsByRange($range = null, array $params = [])
{
$dateParams = [];

View File

@ -356,6 +356,7 @@ return [
'store_defaultindex' => 'Standard-Index-Datei im Kundenordner erstellen',
'phpfpm_settings' => 'PHP-FPM',
'traffic' => 'Traffic',
'traffic_sub' => 'Details der Traffic-Nutzung',
'customertraffic' => 'Kunden',
'assignedmax' => 'Zugewiesen / Max.',
'usedmax' => 'Benutzt / Max.',

View File

@ -358,6 +358,7 @@ return [
'store_defaultindex' => 'Store default index-file to customers docroot',
'phpfpm_settings' => 'PHP-FPM',
'traffic' => 'Traffic',
'traffic_sub' => 'Details on traffic usage',
'domaintraffic' => 'Domains',
'customertraffic' => 'Customers',
'assignedmax' => 'Assigned / Max',

View File

@ -4,9 +4,10 @@
<div>
<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>
<span class="text-muted">View your traffic</span>
<span class="text-muted">{{ lng('admin.traffic_sub') }}</span>
</div>
{% endblock %}
@ -29,6 +30,15 @@
</select>
</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 -->
<div class="row row-cols-4 g-0 bg-white rounded shadow-sm mb-4">
<div class="col p-3 border-end">
@ -49,40 +59,213 @@
</div>
</div>
<!-- Overview for given range by user -->
<h4 class="page-header">Traffic by customers</h4>
{% if users is not empty %}
<div class="card table-responsive">
<table class="table table-borderless table-striped align-middle mb-0 px-3">
<thead>
<tr>
<th scope="col">{{ lng('login.username') }}</th>
<th scope="col">Total</th>
<th scope="col">HTTP</th>
<th scope="col">FTP</th>
<th scope="col">Mail
</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.loginname }}</td>
<td>{{ user.total|formatBytes }}</td>
<td>{{ user.http|formatBytes }}</td>
<td>{{ user.ftp|formatBytes }}</td>
<td>{{ user.mail|formatBytes }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="card">
<div class="card-body">
<p>No data for given range found.</p>
{% if userinfo.adminsession == 1 %}
<!-- Overview for given range by user -->
<h4 class="page-header">Traffic by customers</h4>
{% if users is not empty %}
<div class="card table-responsive">
<table class="table table-borderless table-striped align-middle mb-0 px-3">
<thead>
<tr>
<th scope="col">{{ lng('login.username') }}</th>
<th scope="col">Total</th>
<th scope="col">HTTP</th>
<th scope="col">FTP</th>
<th scope="col">Mail
</th>
</tr>
</thead>
<tbody>
{% for uid,user in users %}
<tr>
<td>
<a href="{{ linker({'section':'customers','page':'customers','action':'su','id':uid}) }}">{{ user.loginname }}</a>
</td>
<td>{{ user.total|formatBytes }}</td>
<td>{{ user.http|formatBytes }}</td>
<td>{{ user.ftp|formatBytes }}</td>
<td>{{ user.mail|formatBytes }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% else %}
<div class="card">
<div class="card-body">
<p>No data for given range found.</p>
</div>
</div>
{% 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 %}