mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-03-04 23:27:46 +01:00
add API code for /api/packets
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>API Documentation - Packet Stats</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
background: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
#swagger-ui {
|
||||
background: #ffffff;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Override Swagger UI colors for white background */
|
||||
.swagger-ui {
|
||||
background-color: #ffffff !important;
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
.swagger-ui .topbar,
|
||||
.swagger-ui .info,
|
||||
.swagger-ui .opblock-summary-description,
|
||||
.swagger-ui .parameters-col_description,
|
||||
.swagger-ui .response-col_description {
|
||||
color: #000000 !important;
|
||||
}
|
||||
|
||||
.swagger-ui .opblock {
|
||||
background-color: #f9f9f9 !important;
|
||||
border-color: #ddd !important;
|
||||
}
|
||||
|
||||
.swagger-ui .opblock-summary {
|
||||
background-color: #eaeaea !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.swagger-ui .opblock-section-header {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.swagger-ui .parameters,
|
||||
.swagger-ui .response {
|
||||
background-color: #fafafa !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.swagger-ui table {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.swagger-ui a {
|
||||
color: #1a0dab !important; /* classic link blue */
|
||||
}
|
||||
|
||||
.swagger-ui input,
|
||||
.swagger-ui select,
|
||||
.swagger-ui textarea {
|
||||
background-color: #fff !important;
|
||||
color: #000 !important;
|
||||
border: 1px solid #ccc !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
|
||||
<script>
|
||||
const spec = {
|
||||
openapi: "3.0.0",
|
||||
info: {
|
||||
title: "Packet Statistics API",
|
||||
version: "1.0.0",
|
||||
description: "API for retrieving packet statistics over a given period with optional filters."
|
||||
},
|
||||
paths: {
|
||||
"/api/stats": {
|
||||
get: {
|
||||
summary: "Get packet statistics",
|
||||
description: "Returns packet statistics for a given period type and length, with optional filters.",
|
||||
parameters: [
|
||||
{
|
||||
name: "period_type",
|
||||
in: "query",
|
||||
required: false,
|
||||
description: "Type of period to group by (`hour` or `day`). Default is `hour`.",
|
||||
schema: {
|
||||
type: "string",
|
||||
enum: ["hour", "day"]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "length",
|
||||
in: "query",
|
||||
required: false,
|
||||
description: "Number of periods to include. Default is 24.",
|
||||
schema: {
|
||||
type: "integer",
|
||||
default: 24
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "channel",
|
||||
in: "query",
|
||||
required: false,
|
||||
description: "Filter by channel name.",
|
||||
schema: {
|
||||
type: "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "portnum",
|
||||
in: "query",
|
||||
required: false,
|
||||
description: "Filter by port number.",
|
||||
schema: {
|
||||
type: "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "to_node",
|
||||
in: "query",
|
||||
required: false,
|
||||
description: "Filter by destination node ID.",
|
||||
schema: {
|
||||
type: "integer"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "from_node",
|
||||
in: "query",
|
||||
required: false,
|
||||
description: "Filter by source node ID.",
|
||||
schema: {
|
||||
type: "integer"
|
||||
}
|
||||
}
|
||||
],
|
||||
responses: {
|
||||
"200": {
|
||||
description: "Successful response",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
hourly: {
|
||||
type: "object",
|
||||
properties: {
|
||||
period_type: { type: "string" },
|
||||
length: { type: "integer" },
|
||||
filters: { type: "object" },
|
||||
data: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
period: { type: "string", example: "2025-08-06 19:00" },
|
||||
node_id: { type: "integer" },
|
||||
long_name: { type: "string" },
|
||||
short_name: { type: "string" },
|
||||
packets: { type: "integer" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
description: "Invalid request parameters",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
error: { type: "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = () => {
|
||||
SwaggerUIBundle({
|
||||
spec,
|
||||
dom_id: '#swagger-ui',
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIBundle.SwaggerUIStandalonePreset
|
||||
],
|
||||
layout: "BaseLayout"
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -50,30 +50,49 @@
|
||||
|
||||
{% block body %}
|
||||
<div class="main-container">
|
||||
<h2 class="main-header">Mesh Statistics</h2>
|
||||
<h2 class="main-header">Mesh Statistics - Hourly Packet Counts (Last 24 Hours)</h2>
|
||||
|
||||
<!-- Hourly Chart -->
|
||||
<!-- Overall hourly packets -->
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Hour (Last 24 Hours)</p>
|
||||
<div id="chart_hourly" class="chart"></div>
|
||||
<p class="section-header">Packets per Hour - All Ports</p>
|
||||
<div id="chart_hourly_all" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- PortNum 1 Hourly Chart -->
|
||||
<!-- Hourly charts by portnum -->
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Hour for Text Messages (Last 24 Hours)</p>
|
||||
<p class="section-header">Packets per Hour - Text Messages (Port 1)</p>
|
||||
<div id="chart_portnum_1" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- Daily Chart -->
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Day (Last 14 Days)</p>
|
||||
<div id="chart_daily" class="chart"></div>
|
||||
<p class="section-header">Packets per Hour - Position (Port 3)</p>
|
||||
<div id="chart_portnum_3" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- PortNum 1 Daily Chart -->
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Day for Text Messages (Last 14 Days)</p>
|
||||
<div id="chart_daily_portnum_1" class="chart"></div>
|
||||
<p class="section-header">Packets per Hour - Node Info (Port 4)</p>
|
||||
<div id="chart_portnum_4" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Hour - Telemetry (Port 67)</p>
|
||||
<div id="chart_portnum_67" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Hour - Traceroute (Port 70)</p>
|
||||
<div id="chart_portnum_70" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Hour - Neighbor Info (Port 71)</p>
|
||||
<div id="chart_portnum_71" class="chart"></div>
|
||||
</div>
|
||||
|
||||
<!-- Daily packets chart -->
|
||||
<div class="card-section">
|
||||
<p class="section-header">Packets per Day - All Ports (Last 14 Days)</p>
|
||||
<div id="chart_daily_all" class="chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -86,7 +105,7 @@
|
||||
}
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) {
|
||||
console.error(`Failed to fetch ${period_type} stats:`, res.status, res.statusText);
|
||||
console.error(`Failed to fetch ${period_type} stats portnum ${portnum}:`, res.status, res.statusText);
|
||||
return [];
|
||||
}
|
||||
const json = await res.json();
|
||||
@@ -97,20 +116,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
let chartHourly = null;
|
||||
// Chart references
|
||||
let chartHourlyAll = null;
|
||||
let chartPortnum1 = null;
|
||||
let chartDaily = null;
|
||||
let chartDailyPortnum1 = null;
|
||||
let chartPortnum3 = null;
|
||||
let chartPortnum4 = null;
|
||||
let chartPortnum67 = null;
|
||||
let chartPortnum70 = null;
|
||||
let chartPortnum71 = null;
|
||||
let chartDailyAll = null;
|
||||
|
||||
function renderChart(domId, data, type, color, isHourly) {
|
||||
const el = document.getElementById(domId);
|
||||
if (!el) return;
|
||||
|
||||
const chart = echarts.init(el);
|
||||
if (domId === 'chart_hourly') chartHourly = chart;
|
||||
else if (domId === 'chart_portnum_1') chartPortnum1 = chart;
|
||||
else if (domId === 'chart_daily') chartDaily = chart;
|
||||
else if (domId === 'chart_daily_portnum_1') chartDailyPortnum1 = chart;
|
||||
switch(domId) {
|
||||
case 'chart_hourly_all': chartHourlyAll = chart; break;
|
||||
case 'chart_portnum_1': chartPortnum1 = chart; break;
|
||||
case 'chart_portnum_3': chartPortnum3 = chart; break;
|
||||
case 'chart_portnum_4': chartPortnum4 = chart; break;
|
||||
case 'chart_portnum_67': chartPortnum67 = chart; break;
|
||||
case 'chart_portnum_70': chartPortnum70 = chart; break;
|
||||
case 'chart_portnum_71': chartPortnum71 = chart; break;
|
||||
case 'chart_daily_all': chartDailyAll = chart; break;
|
||||
}
|
||||
|
||||
const periods = data.map(d => {
|
||||
const p = (d && (d.period || d.period === 0)) ? d.period.toString() : '';
|
||||
@@ -151,24 +181,42 @@
|
||||
}
|
||||
|
||||
async function init() {
|
||||
const hourlyData = await fetchStats('hour', 24);
|
||||
renderChart('chart_hourly', hourlyData, 'bar', '#03dac6', true);
|
||||
// Overall hourly packets
|
||||
const hourlyAllData = await fetchStats('hour', 24);
|
||||
renderChart('chart_hourly_all', hourlyAllData, 'bar', '#03dac6', true);
|
||||
|
||||
const portnum1Data = await fetchStats('hour', 24, 1);
|
||||
renderChart('chart_portnum_1', portnum1Data, 'bar', '#ff5722', true);
|
||||
// Hourly packets by portnum in parallel
|
||||
const portnums = [1, 3, 4, 67, 70, 71];
|
||||
const colors = ['#ff5722', '#2196f3', '#9c27b0', '#ffeb3b', '#795548', '#4caf50'];
|
||||
const domIds = [
|
||||
'chart_portnum_1',
|
||||
'chart_portnum_3',
|
||||
'chart_portnum_4',
|
||||
'chart_portnum_67',
|
||||
'chart_portnum_70',
|
||||
'chart_portnum_71'
|
||||
];
|
||||
|
||||
const dailyData = await fetchStats('day', 14);
|
||||
renderChart('chart_daily', dailyData, 'line', '#ffeb3b', false);
|
||||
const allData = await Promise.all(portnums.map(pn => fetchStats('hour', 24, pn)));
|
||||
|
||||
const dailyPortnum1Data = await fetchStats('day', 14, 1);
|
||||
renderChart('chart_daily_portnum_1', dailyPortnum1Data, 'bar', '#ff7043', false);
|
||||
for (let i = 0; i < portnums.length; i++) {
|
||||
renderChart(domIds[i], allData[i], 'bar', colors[i], true);
|
||||
}
|
||||
|
||||
// Daily packets (all ports, last 14 days)
|
||||
const dailyAllData = await fetchStats('day', 14);
|
||||
renderChart('chart_daily_all', dailyAllData, 'line', '#66bb6a', false);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (chartHourly) chartHourly.resize();
|
||||
if (chartHourlyAll) chartHourlyAll.resize();
|
||||
if (chartPortnum1) chartPortnum1.resize();
|
||||
if (chartDaily) chartDaily.resize();
|
||||
if (chartDailyPortnum1) chartDailyPortnum1.resize();
|
||||
if (chartPortnum3) chartPortnum3.resize();
|
||||
if (chartPortnum4) chartPortnum4.resize();
|
||||
if (chartPortnum67) chartPortnum67.resize();
|
||||
if (chartPortnum70) chartPortnum70.resize();
|
||||
if (chartPortnum71) chartPortnum71.resize();
|
||||
if (chartDailyAll) chartDailyAll.resize();
|
||||
});
|
||||
|
||||
init();
|
||||
|
||||
Reference in New Issue
Block a user