mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-03-30 12:05:38 +00:00
Enhance path handling in BotDataViewer and ModernContactsManager
- Added support for bytes per hop in the BotDataViewer, allowing for better path data representation. - Updated the contacts template to display bytes per hop and adjusted path formatting based on this value. - Improved the decode path functionality to utilize the correct bytes per hop for decoding paths, enhancing overall path handling and user experience.
This commit is contained in:
@@ -3569,6 +3569,7 @@ class BotDataViewer:
|
||||
MAX(c.last_advert_timestamp) as last_message,
|
||||
GROUP_CONCAT(op.path_hex, '|||') as all_paths_hex,
|
||||
GROUP_CONCAT(op.path_length, '|||') as all_paths_length,
|
||||
GROUP_CONCAT(COALESCE(op.bytes_per_hop, 1), '|||') as all_paths_bytes_per_hop,
|
||||
GROUP_CONCAT(op.observation_count, '|||') as all_paths_observations,
|
||||
GROUP_CONCAT(op.last_seen, '|||') as all_paths_last_seen
|
||||
FROM complete_contact_tracking c
|
||||
@@ -3606,14 +3607,24 @@ class BotDataViewer:
|
||||
if row['all_paths_hex']:
|
||||
paths_hex = row['all_paths_hex'].split('|||')
|
||||
paths_length = row['all_paths_length'].split('|||') if row['all_paths_length'] else []
|
||||
paths_bph = row['all_paths_bytes_per_hop'].split('|||') if row.get('all_paths_bytes_per_hop') else []
|
||||
paths_observations = row['all_paths_observations'].split('|||') if row['all_paths_observations'] else []
|
||||
paths_last_seen = row['all_paths_last_seen'].split('|||') if row['all_paths_last_seen'] else []
|
||||
|
||||
for i, path_hex in enumerate(paths_hex):
|
||||
if path_hex: # Skip empty strings
|
||||
bph = None
|
||||
if i < len(paths_bph) and paths_bph[i]:
|
||||
try:
|
||||
bph = int(paths_bph[i])
|
||||
if bph not in (1, 2, 3):
|
||||
bph = 1
|
||||
except (TypeError, ValueError):
|
||||
bph = 1
|
||||
all_paths.append({
|
||||
'path_hex': path_hex,
|
||||
'path_length': int(paths_length[i]) if i < len(paths_length) and paths_length[i] else 0,
|
||||
'bytes_per_hop': bph,
|
||||
'observation_count': int(paths_observations[i]) if i < len(paths_observations) and paths_observations[i] else 1,
|
||||
'last_seen': paths_last_seen[i] if i < len(paths_last_seen) and paths_last_seen[i] else None
|
||||
})
|
||||
|
||||
@@ -982,10 +982,12 @@ class ModernContactsManager {
|
||||
// Create a unique ID for this tooltip
|
||||
const tooltipId = `hops-tooltip-${contact.user_id.substring(0, 16).replace(/[^a-zA-Z0-9]/g, '_')}`;
|
||||
const pathCountText = hasMultiplePaths ? ` <small>(${allPaths.length} paths)</small>` : '';
|
||||
const bytesPerHop = contact.out_bytes_per_hop != null && [1, 2, 3].includes(Number(contact.out_bytes_per_hop)) ? Number(contact.out_bytes_per_hop) : '';
|
||||
return `<span class="badge bg-primary path-badge"
|
||||
id="${tooltipId}"
|
||||
data-path-hex="${escapedPath}"
|
||||
data-path-len="${outPathLen}"
|
||||
data-bytes-per-hop="${bytesPerHop}"
|
||||
data-user-id="${escapedUserId}"
|
||||
onclick="contactsManager.showPathsModal('${escapedUserId}')"
|
||||
style="cursor: pointer; position: relative;"
|
||||
@@ -1073,16 +1075,18 @@ class ModernContactsManager {
|
||||
|
||||
try {
|
||||
badge.dataset.loading = 'true';
|
||||
|
||||
const body = { path_hex: pathHex };
|
||||
const bytesPerHop = badge.dataset.bytesPerHop;
|
||||
if (bytesPerHop && ['1', '2', '3'].includes(bytesPerHop)) {
|
||||
body.bytes_per_hop = parseInt(bytesPerHop, 10);
|
||||
}
|
||||
// Fetch decoded path from API
|
||||
const response = await fetch('/api/decode-path', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
path_hex: pathHex
|
||||
})
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
@@ -1139,11 +1143,15 @@ class ModernContactsManager {
|
||||
});
|
||||
}
|
||||
|
||||
formatPathHex(pathHex) {
|
||||
// Break hex string into two-character chunks with spaces for readability
|
||||
formatPathHex(pathHex, bytesPerHop) {
|
||||
// Break hex string into chunks for readability. bytesPerHop 1,2,3 → 2,4,6 hex chars per node.
|
||||
if (!pathHex) return '';
|
||||
// Match every 1-2 characters (handles odd-length strings gracefully)
|
||||
return pathHex.match(/.{1,2}/g)?.join(' ') || pathHex;
|
||||
const n = (bytesPerHop && [1, 2, 3].includes(Number(bytesPerHop))) ? (Number(bytesPerHop) * 2) : 2;
|
||||
const chunks = [];
|
||||
for (let i = 0; i < pathHex.length; i += n) {
|
||||
chunks.push(pathHex.slice(i, i + n));
|
||||
}
|
||||
return chunks.join(' ') || pathHex;
|
||||
}
|
||||
|
||||
sortPaths(paths, sortOrder) {
|
||||
@@ -1197,20 +1205,23 @@ class ModernContactsManager {
|
||||
const path = paths[i];
|
||||
const isPrimary = path.path_hex === primaryPath;
|
||||
const primaryBadge = isPrimary ? '<span class="badge bg-success ms-2">Primary</span>' : '';
|
||||
const formattedPathHex = this.formatPathHex(path.path_hex);
|
||||
const bph = path.bytes_per_hop != null && [1, 2, 3].includes(Number(path.bytes_per_hop)) ? Number(path.bytes_per_hop) : null;
|
||||
const formattedPathHex = this.formatPathHex(path.path_hex, bph);
|
||||
const hopLabel = bph && path.path_length > 0 ? `${Math.floor(path.path_length / bph)} hops, ` : '';
|
||||
const bytesPerHopAttr = bph != null ? ` data-path-bph="${bph}"` : '';
|
||||
|
||||
pathsHtml += `
|
||||
<div class="list-group-item path-list-item ${isPrimary ? 'border-success' : ''}" id="path-item-${i}">
|
||||
<div class="list-group-item path-list-item ${isPrimary ? 'border-success' : ''}" id="path-item-${i}"${bytesPerHopAttr}>
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1">
|
||||
Path ${i + 1} ${primaryBadge}
|
||||
<small class="text-muted">(${path.path_length} bytes, ${path.observation_count} observations)</small>
|
||||
<small class="text-muted">(${hopLabel}${path.path_length} bytes, ${path.observation_count} observations)</small>
|
||||
</h6>
|
||||
<p class="mb-1"><code class="path-hex-code">${this.escapeHtml(formattedPathHex)}</code></p>
|
||||
<small class="text-muted">Last seen: ${this.formatTimeAgo(path.last_seen)}</small>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="contactsManager.decodePathInModal('${path.path_hex}', ${i})">
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="contactsManager.decodePathInModal('${String(path.path_hex).replace(/\\/g, '\\\\').replace(/'/g, "\\'")}', ${i}, ${bph != null ? bph : 'null'})">
|
||||
<i class="fas fa-code"></i> Decode
|
||||
</button>
|
||||
</div>
|
||||
@@ -1321,9 +1332,10 @@ class ModernContactsManager {
|
||||
});
|
||||
}
|
||||
|
||||
async decodePathInModal(pathHex, pathIndex) {
|
||||
async decodePathInModal(pathHex, pathIndex, pathBytesPerHop) {
|
||||
const decodedDiv = document.getElementById(`decoded-path-${pathIndex}`);
|
||||
const button = document.querySelector(`#path-item-${pathIndex} button`);
|
||||
const pathItem = document.getElementById(`path-item-${pathIndex}`);
|
||||
const button = pathItem ? pathItem.querySelector('button') : null;
|
||||
|
||||
if (!decodedDiv || !button) return;
|
||||
|
||||
@@ -1335,11 +1347,13 @@ class ModernContactsManager {
|
||||
decodedDiv.innerHTML = '<div class="text-muted"><i class="fas fa-spinner fa-spin"></i> Decoding path...</div>';
|
||||
|
||||
try {
|
||||
// Use contact's out_bytes_per_hop when path came from a packet with multi-byte hops
|
||||
const bytesPerHop = this.currentModalContact?.out_bytes_per_hop || null;
|
||||
// Prefer this path's bytes_per_hop; fall back to contact's out_bytes_per_hop
|
||||
const bytesPerHop = (pathBytesPerHop != null && [1, 2, 3].includes(Number(pathBytesPerHop)))
|
||||
? Number(pathBytesPerHop)
|
||||
: (this.currentModalContact?.out_bytes_per_hop != null && [1, 2, 3].includes(Number(this.currentModalContact.out_bytes_per_hop)) ? Number(this.currentModalContact.out_bytes_per_hop) : null);
|
||||
const body = { path_hex: pathHex };
|
||||
if (bytesPerHop != null && [1, 2, 3].includes(Number(bytesPerHop))) {
|
||||
body.bytes_per_hop = Number(bytesPerHop);
|
||||
if (bytesPerHop != null) {
|
||||
body.bytes_per_hop = bytesPerHop;
|
||||
}
|
||||
const response = await fetch('/api/decode-path', {
|
||||
method: 'POST',
|
||||
|
||||
Reference in New Issue
Block a user