Finishing up all the pages for the 3.0 release.

Now all pages are functional.
This commit is contained in:
Pablo Revilla
2025-11-21 15:41:25 -08:00
parent 14aabc3b10
commit 0eed8f8001
2 changed files with 74 additions and 26 deletions

View File

@@ -30,6 +30,7 @@
}
.packet-table tr:nth-of-type(odd) { background-color: #272b2f; }
.packet-table tr:nth-of-type(even) { background-color: #212529; }
.port-tag {
display: inline-block;
padding: 1px 6px;
@@ -68,6 +69,17 @@
font-weight: bold;
}
.toggle-btn:hover { color: #fff; }
/* Link next to port tag */
.inline-link {
margin-left: 6px;
font-weight: bold;
text-decoration: none;
color: #9fd4ff;
}
.inline-link:hover {
color: #c7e6ff;
}
{% endblock %}
{% block body %}
@@ -85,7 +97,6 @@
<th data-translate-lang="from">From</th>
<th data-translate-lang="to">To</th>
<th data-translate-lang="port">Port</th>
<th data-translate-lang="links">Links</th>
</tr>
</thead>
<tbody id="packet_list"></tbody>
@@ -106,7 +117,6 @@ function applyTranslations(translations, root=document) {
});
}
// --- Load translations for firehose section ---
async function loadTranslations() {
try {
const cfg = await window._siteConfigPromise;
@@ -155,14 +165,13 @@ const PORT_COLORS = {
78: "#795548"
};
// --- Load node names ---
// Load node names
async function loadNodes() {
const res = await fetch("/api/nodes");
if (!res.ok) return;
const data = await res.json();
for (const n of data.nodes || []) {
const name = n.long_name || n.short_name || n.id || n.node_id;
nodeMap[n.node_id] = name;
nodeMap[n.node_id] = n.long_name || n.short_name || n.id || n.node_id;
}
nodeMap[4294967295] = "All";
}
@@ -172,12 +181,18 @@ function nodeName(id) {
return nodeMap[id] || id;
}
function portLabel(portnum, payload) {
function portLabel(portnum, payload, linksHtml) {
const name = PORT_MAP[portnum] || "Unknown";
const color = PORT_COLORS[portnum] || "#6c757d";
const safePayload = payload ? payload.replace(/"/g, "&quot;") : "";
return `<span class="port-tag" style="background-color:${color}" title="${safePayload}">${name}</span>
<span class="text-secondary">(${portnum})</span>`;
return `
<span class="port-tag" style="background-color:${color}" title="${safePayload}">
${name}
</span>
<span class="text-secondary">(${portnum})</span>
${linksHtml || ""}
`;
}
function formatLocalTime(importTimeUs) {
@@ -191,7 +206,6 @@ async function configureFirehose() {
const cfg = await window._siteConfigPromise;
const intervalSec = cfg?.site?.firehose_interval;
if (intervalSec && !isNaN(intervalSec)) updateInterval = parseInt(intervalSec) * 1000;
console.log("Firehose update interval:", updateInterval, "ms");
} catch (err) {
console.warn("Failed to read firehose interval:", err);
}
@@ -213,33 +227,36 @@ async function fetchUpdates() {
const list = document.getElementById("packet_list");
for (const pkt of packets.reverse()) {
const fromNodeId = pkt.from_node_id;
const toNodeId = pkt.to_node_id;
const from = pkt.from_node_id === 4294967295
? `<span class="to-mqtt">All</span>`
: `<a href="/new_node/${pkt.from_node_id}" style="text-decoration:underline; color:inherit;">${nodeMap[pkt.from_node_id] || pkt.from_node_id}</a>`;
let from = fromNodeId === 4294967295 ? `<span class="to-mqtt">All</span>` :
`<a href="/new_node/${fromNodeId}" style="text-decoration:underline; color:inherit;">${nodeMap[fromNodeId] || fromNodeId}</a>`;
const to = pkt.to_node_id === 1
? `<span class="to-mqtt">direct to MQTT</span>`
: pkt.to_node_id === 4294967295
? `<span class="to-mqtt">All</span>`
: `<a href="/new_node/${pkt.to_node_id}" style="text-decoration:underline; color:inherit;">${nodeMap[pkt.to_node_id] || pkt.to_node_id}</a>`;
let to = toNodeId === 1 ? `<span class="to-mqtt">direct to MQTT</span>` :
toNodeId === 4294967295 ? `<span class="to-mqtt">All</span>` :
`<a href="/new_node/${toNodeId}" style="text-decoration:underline; color:inherit;">${nodeMap[toNodeId] || toNodeId}</a>`;
// Inline link next to port tag
let inlineLinks = "";
let links = "";
if (pkt.portnum === 3 && pkt.payload) {
const latMatch = pkt.payload.match(/latitude_i:\s*(-?\d+)/);
const lonMatch = pkt.payload.match(/longitude_i:\s*(-?\d+)/);
if (latMatch && lonMatch) {
const lat = parseInt(latMatch[1]) / 1e7;
const lon = parseInt(lonMatch[1]) / 1e7;
links += `<a href="https://www.google.com/maps?q=${lat},${lon}" target="_blank" rel="noopener noreferrer" style="font-weight:bold; text-decoration:none;">Map</a>`;
inlineLinks += ` <a class="inline-link" href="https://www.google.com/maps?q=${lat},${lon}" target="_blank">📍</a>`;
}
}
if (pkt.portnum === 70) {
let traceId = pkt.id;
const idMatch = pkt.payload.match(/ID:\s*(\d+)/i);
if (idMatch) traceId = idMatch[1];
if (links) links += "&nbsp;|&nbsp;";
links += `<a href="/graph/traceroute/${traceId}" target="_blank" rel="noopener noreferrer" style="font-weight:bold; text-decoration:none;">Graph</a>`;
const match = pkt.payload.match(/ID:\s*(\d+)/i);
if (match) traceId = match[1];
inlineLinks += ` <a class="inline-link" href="/graph/traceroute/${traceId}" target="_blank">⮕</a>`;
}
const safePayload = (pkt.payload || "").replace(/</g, "&lt;").replace(/>/g, "&gt;");
@@ -251,11 +268,10 @@ async function fetchUpdates() {
<td><span class="toggle-btn">▶</span> <a href="/new_packet/${pkt.id}" style="text-decoration:underline; color:inherit;">${pkt.id}</a></td>
<td>${from}</td>
<td>${to}</td>
<td>${portLabel(pkt.portnum, pkt.payload)}</td>
<td>${links}</td>
<td>${portLabel(pkt.portnum, pkt.payload, inlineLinks)}</td>
</tr>
<tr class="payload-row">
<td colspan="6" class="payload-cell">${safePayload}</td>
<td colspan="5" class="payload-cell">${safePayload}</td>
</tr>`;
list.insertAdjacentHTML("afterbegin", html);
}

View File

@@ -130,6 +130,17 @@
background:#1b1e22; border-radius:8px;
width:90%; height:85%; padding:10px;
}
/* Link next to port tag */
.inline-link {
margin-left: 6px;
font-weight: bold;
text-decoration: none;
color: #9fd4ff;
}
.inline-link:hover {
color: #c7e6ff;
}
{% endblock %}
{% block body %}
@@ -537,13 +548,34 @@ async function loadPackets(){
drawNeighbors(pkt.from_node_id, nids);
}
let inlineLinks = "";
if (pkt.portnum === 3 && pkt.payload) {
const latMatch = pkt.payload.match(/latitude_i:\s*(-?\d+)/);
const lonMatch = pkt.payload.match(/longitude_i:\s*(-?\d+)/);
if (latMatch && lonMatch) {
const lat = parseFloat(latMatch[1]) / 1e7;
const lon = parseFloat(lonMatch[1]) / 1e7;
inlineLinks += ` <a class="inline-link" href="https://www.google.com/maps?q=${lat},${lon}" target="_blank">📍</a>`;
}
}
if (pkt.portnum === 70) {
let traceId = pkt.id;
const match = pkt.payload?.match(/ID:\s*(\d+)/i);
if (match) traceId = match[1];
inlineLinks += ` <a class="inline-link" href="/graph/traceroute/${traceId}" target="_blank">⮕</a>`;
}
list.insertAdjacentHTML("afterbegin", `
<tr class="packet-row">
<td>${localTime}</td>
<td><span class="toggle-btn">▶</span> <a href="/new_packet/${pkt.id}" style="text-decoration:underline; color:inherit;">${pkt.id}</a></td>
<td>${fromCell}</td>
<td>${toCell}</td>
<td>${portLabel(pkt.portnum)}</td>
<td>${portLabel(pkt.portnum)}${inlineLinks}</td>
</tr>
<tr class="payload-row">
<td colspan="5" class="payload-cell">${safePayload}</td>