mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-06-28 22:11:36 +02:00
Fixed Date format to mm/dd/YYYY.
Added /api that dumps a json with all the nodes.
This commit is contained in:
@@ -23,6 +23,14 @@ class Node(Base):
|
||||
channel: Mapped[str] = mapped_column(nullable=True)
|
||||
last_update: Mapped[datetime] = mapped_column(nullable=True)
|
||||
|
||||
def to_dict(self):
|
||||
"""Convert SQLAlchemy object to a dictionary, excluding last_update."""
|
||||
return {
|
||||
column.name: getattr(self, column.name)
|
||||
for column in self.__table__.columns
|
||||
if column.name != "last_update" # Exclude last_update
|
||||
}
|
||||
|
||||
class Packet(Base):
|
||||
__tablename__ = "packet"
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="row chat-packet">
|
||||
<span class="col-2 timestamp">{{packet.import_time.strftime('%-I:%M:%S %p - %d-%m-%Y')}} </span>
|
||||
<span class="col-2 timestamp">{{packet.import_time.strftime('%-I:%M:%S %p - %m-%d-%Y')}} </span>
|
||||
<span class="col-1 timestamp"><a href="/packet/{{packet.id}}">✉️</a> {{packet.from_node.channel}}</span>
|
||||
<span class="col-2 username"><a href="/packet_list/{{packet.from_node_id}}">{{packet.from_node.long_name or (packet.from_node_id | node_id_to_hex) }}</a></span>
|
||||
<span class="col-6 message">{{packet.payload}}</span>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
{% block css %}
|
||||
|
||||
/* Set the maximum width of the page to 900px */
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto; /* Center the content horizontally */
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
@@ -82,7 +90,7 @@
|
||||
<option value="{{ value }}" {% if value == portnum %}selected{% endif %}>{{ name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="button" id="auto-refresh-button" class="col-2 m-2 btn btn-primary">Enable Auto-Refresh</button>
|
||||
<button type="button" id="auto-refresh-button">Enable Auto-Refresh</button>
|
||||
</form>
|
||||
<div class="row">
|
||||
<div class="col-xs" id="packet_list">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="row chat-packet">
|
||||
<span class="col-2 timestamp">{{packet.import_time.strftime('%-I:%M:%S %p - %d-%m-%Y')}} </span>
|
||||
<span class="col-2 timestamp">{{packet.import_time.strftime('%-I:%M:%S %p - %m-%d-%Y')}} </span>
|
||||
<span class="col-1 timestamp"><a href="/packet/{{packet.id}}">✉️</a> {{packet.from_node.channel}}</span>
|
||||
<span class="col-2 username"><a href="/packet_list/{{packet.from_node_id}}">{{packet.from_node.long_name or (packet.from_node_id | node_id_to_hex) }}</a></span>
|
||||
<span class="col-6 message">{{packet.payload}}</span>
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
div.tab-pane > dl {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Set the maximum width of the page to 900px */
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto; /* Center the content horizontally */
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
@@ -30,12 +36,7 @@
|
||||
{% include "search_form.html" %}
|
||||
|
||||
<div id="node" class="container text-center">
|
||||
<div class="container"
|
||||
{% if node %}
|
||||
hx-ext="sse"
|
||||
sse-connect="/events?node_id={{node_id}}{% if portnum %}&portnum={{portnum}}{% endif %}"
|
||||
{% endif %}
|
||||
>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col mb-3">
|
||||
<!-- Node Information Card -->
|
||||
@@ -106,7 +107,7 @@
|
||||
const brightness = Math.round(((parseInt(text_color[0]) * 299) +
|
||||
(parseInt(text_color[1]) * 587) +
|
||||
(parseInt(text_color[2]) * 114)) / 1000);
|
||||
|
||||
|
||||
if (brightness > 125) {
|
||||
var textColor = '#212529'
|
||||
node_color.style.color = textColor
|
||||
@@ -230,7 +231,6 @@
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -49,8 +49,14 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<section>
|
||||
<h2 class="table-title">{{ traffic[0].long_name }} (last 24 hours)</h2>
|
||||
<section>
|
||||
<h2 class="table-title">
|
||||
{% if traffic %}
|
||||
{{ traffic[0].long_name }} (last 24 hours)
|
||||
{% else %}
|
||||
No Traffic Data Available
|
||||
{% endif %}
|
||||
</h2>
|
||||
<table class="traffic-table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -80,10 +86,6 @@
|
||||
MAP_REPORT_APP
|
||||
{% elif port.portnum == 0 %}
|
||||
UNKNOWN_APP
|
||||
{% elif port.portnum == 0 %}
|
||||
UNKNOWN_APP
|
||||
{% elif port.portnum == 0 %}
|
||||
UNKNOWN_APP
|
||||
{% else %}
|
||||
{{ port.portnum }}
|
||||
{% endif %}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="card-text text-start">
|
||||
<dl>
|
||||
<dt>Import Time</dt>
|
||||
<dd>{{packet.import_time.strftime('%-I:%M:%S %p - %d-%m-%Y')}}</dd>
|
||||
<dd>{{packet.import_time.strftime('%-I:%M:%S %p - %m-%d-%Y')}}</dd>
|
||||
<dt>packet</dt>
|
||||
<dd><pre>{{packet.data}}</pre></dd>
|
||||
<dt>payload</dt>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="card-text text-start">
|
||||
<dl>
|
||||
<dt>Import Time</dt>
|
||||
<dd>{{seen.import_time.strftime('%-I:%M:%S %p - %d-%m-%Y')}}</dd>
|
||||
<dd>{{seen.import_time.strftime('%-I:%M:%S %p - %m-%d-%Y')}}</dd>
|
||||
<dt>rx_time</dt>
|
||||
<dd>{{seen.rx_time|format_timestamp}}</dd>
|
||||
<dt>hop_limit</dt>
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
{% if map_center %}
|
||||
<script>
|
||||
var details_map = L.map('details_map').setView({{ map_center | tojson }}, 12);
|
||||
var details_map = L.map('details_map').setView({{ map_center | tojson }}, 10);
|
||||
var markers = L.featureGroup();
|
||||
markers.addTo(details_map);
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
// Ensure markers are added before adjusting map bounds
|
||||
setTimeout(() => {
|
||||
if (markers.getLayers().length > 0) {
|
||||
details_map.fitBounds(markers.getBounds().pad(0.1), { animate: true });
|
||||
details_map.fitBounds(markers.getBounds().pad(0.3), { animate: true });
|
||||
}
|
||||
}, 500); // Delay to ensure markers load
|
||||
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
{% block css %}
|
||||
|
||||
/* Set the maximum width of the page to 900px */
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto; /* Center the content horizontally */
|
||||
}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
@@ -5,56 +5,102 @@
|
||||
height: 95vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
/* Set the maximum width of the page to 600px */
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto; /* Center the content horizontally */
|
||||
}
|
||||
|
||||
/* Adjust styling for sections */
|
||||
.card-section {
|
||||
background-color: #272b2f;
|
||||
border: 1px solid #474b4e;
|
||||
padding: 15px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.section-value {
|
||||
font-weight: 700;
|
||||
color: #03dac6;
|
||||
}
|
||||
|
||||
.percentage {
|
||||
font-size: 12px;
|
||||
color: #ffeb3b;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.main-container {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.main-header {
|
||||
font-size: 22px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div style="font-family: 'Segoe UI', Tahoma, sans-serif; color: #fff; background: linear-gradient(135deg, #6a11cb, #2575fc); padding: 25px; border-radius: 15px; max-width: 350px; margin: auto; text-align: center; box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);">
|
||||
<h2 style="font-size: 22px; margin-bottom: 20px; font-weight: 600;">
|
||||
Mesh Statistics
|
||||
</h2>
|
||||
<div class="main-container">
|
||||
<h2 class="main-header">Mesh Statistics</h2>
|
||||
|
||||
<!-- Section for Total Nodes -->
|
||||
<div style="background: rgba(255, 255, 255, 0.1); padding: 15px; border-radius: 10px; margin-bottom: 15px;">
|
||||
<p style="font-size: 16px; margin: 0; font-weight: 500;">
|
||||
<div class="card-section">
|
||||
<p class="section-header">
|
||||
Total Active Nodes (Last 24 hours):
|
||||
<span style="font-weight: 700; color: #ffeb3b;">{{ total_nodes }}</span>
|
||||
<span class="section-value">{{ total_nodes }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Section for Total Nodes LongFast -->
|
||||
<div style="background: rgba(255, 255, 255, 0.1); padding: 15px; border-radius: 10px; margin-bottom: 10px;">
|
||||
<p style="font-size: 14px; margin: 0; font-weight: 500;">
|
||||
<div class="card-section">
|
||||
<p class="section-header">
|
||||
Total Active Nodes LongFast: <br>
|
||||
<span style="font-weight: 700; color: #03dac6;">{{ total_nodes_longfast }}</span>
|
||||
<span style="font-size: 12px; color: #ffeb3b; font-weight: 400;">
|
||||
<span class="section-value">{{ total_nodes_longfast }}</span>
|
||||
<span class="percentage">
|
||||
({{ (total_nodes_longfast / total_nodes * 100) | round(2) }}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Section for Total Nodes MediumSlow -->
|
||||
<div style="background: rgba(255, 255, 255, 0.1); padding: 15px; border-radius: 10px; margin-bottom: 10px;">
|
||||
<p style="font-size: 14px; margin: 0; font-weight: 500;">
|
||||
<div class="card-section">
|
||||
<p class="section-header">
|
||||
Total Active Nodes MediumSlow: <br>
|
||||
<span style="font-weight: 700; color: #03dac6;">{{ total_nodes_mediumslow }}</span>
|
||||
<span style="font-size: 12px; color: #ffeb3b; font-weight: 400;">
|
||||
<span class="section-value">{{ total_nodes_mediumslow }}</span>
|
||||
<span class="percentage">
|
||||
({{ (total_nodes_mediumslow / total_nodes * 100) | round(2) }}%)
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Section for Total Packets -->
|
||||
<div style="background: rgba(255, 255, 255, 0.1); padding: 15px; border-radius: 10px; margin-bottom: 10px;">
|
||||
<p style="font-size: 14px; margin: 0; font-weight: 500;">
|
||||
<div class="card-section">
|
||||
<p class="section-header">
|
||||
Total Packets:
|
||||
<span style="font-weight: 700; color: #03dac6;">{{ total_packets }}</span>
|
||||
<span class="section-value">{{ total_packets }}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Section for Total MQTT Reports -->
|
||||
<div style="background: rgba(255, 255, 255, 0.1); padding: 15px; border-radius: 10px;">
|
||||
<p style="font-size: 14px; margin: 0; font-weight: 500;">
|
||||
<div class="card-section">
|
||||
<p class="section-header">
|
||||
Total MQTT Reports:
|
||||
<span style="font-weight: 700; color: #03dac6;">{{ total_packets_seen }}</span>
|
||||
<span class="section-value">{{ total_packets_seen }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock body %}
|
||||
{% endblock %}
|
||||
|
||||
+26
-1
@@ -1093,6 +1093,32 @@ async def nodelist(request):
|
||||
)
|
||||
|
||||
|
||||
import traceback
|
||||
|
||||
|
||||
@routes.get("/api")
|
||||
async def api(request):
|
||||
try:
|
||||
role = request.query.get("role")
|
||||
channel = request.query.get("channel")
|
||||
hw_model = request.query.get("hw_model")
|
||||
|
||||
nodes = await store.get_nodes(role, channel, hw_model)
|
||||
|
||||
nodes_json = [node.to_dict() for node in nodes]
|
||||
return web.json_response({"nodes": nodes_json})
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
print("Error in /api endpoint:", str(e))
|
||||
print(traceback.format_exc())
|
||||
return web.Response(
|
||||
text=f"An error occurred: {str(e)}",
|
||||
status=500,
|
||||
content_type="text/plain",
|
||||
)
|
||||
|
||||
|
||||
@routes.get("/net")
|
||||
async def net(request):
|
||||
try:
|
||||
@@ -1193,7 +1219,6 @@ async def top(request):
|
||||
if node_id:
|
||||
# If node_id is provided, fetch traffic data for the specific node
|
||||
node_traffic = await store.get_node_traffic(int(node_id))
|
||||
print(node_traffic)
|
||||
template = env.get_template("node_traffic.html") # Render a different template
|
||||
html_content = template.render(traffic=node_traffic, node_id=node_id, site_config = CONFIG)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user