From c6d688c931519e8bfd4d3e08ce18f62f039a75ad Mon Sep 17 00:00:00 2001 From: Alex Vanderpot Date: Sun, 14 Jun 2026 17:44:21 -0400 Subject: [PATCH] Coerce ClickHouse UInt8 booleans for proto bool fields is_recent (and is_uplinked/has_packets) come back from ClickHouse as JSON numbers (1/0); protobuf-es rejected them for the MqttTopic.is_recent bool field ("expected boolean, got 1"). Add a bool() helper alongside num() and apply it in the node handler. Co-Authored-By: Claude Opus 4.8 (1M context) --- meshexplorer/src/server/connect/mappers.ts | 6 ++++++ meshexplorer/src/server/connect/node.ts | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/meshexplorer/src/server/connect/mappers.ts b/meshexplorer/src/server/connect/mappers.ts index 1a4d3f5..b823fe2 100644 --- a/meshexplorer/src/server/connect/mappers.ts +++ b/meshexplorer/src/server/connect/mappers.ts @@ -24,6 +24,12 @@ export function num(v: unknown): number { return Number(v ?? 0); } +// ClickHouse returns UInt8 boolean-ish columns (e.g. is_recent) as JSON numbers +// (1/0). Coerce before feeding a proto `bool` field. +export function bool(v: unknown): boolean { + return v === true || v === 1 || v === "1"; +} + /** Maps a ClickHouse neighbor-edge row to the NeighborEdge proto init shape. */ export function toNeighborEdge(row: NeighborEdgeRow): MessageInitShape { return { diff --git a/meshexplorer/src/server/connect/node.ts b/meshexplorer/src/server/connect/node.ts index 7c112c4..8ae3d36 100644 --- a/meshexplorer/src/server/connect/node.ts +++ b/meshexplorer/src/server/connect/node.ts @@ -7,7 +7,7 @@ import { getMeshcoreNodeNeighbors, searchMeshcoreNodes, } from "@/lib/clickhouse/actions"; -import { num } from "./mappers"; +import { bool, num } from "./mappers"; interface SearchResultRow { public_key: string; @@ -112,13 +112,13 @@ export const nodeServiceImpl: ServiceImpl = { longitude: l.longitude, })), mqtt: { - isUplinked: nodeInfo.mqtt.is_uplinked, - hasPackets: nodeInfo.mqtt.has_packets, + isUplinked: bool(nodeInfo.mqtt.is_uplinked), + hasPackets: bool(nodeInfo.mqtt.has_packets), topics: nodeInfo.mqtt.topics.map((t) => ({ topic: t.topic, broker: t.broker, lastPacketTime: t.last_packet_time, - isRecent: t.is_recent, + isRecent: bool(t.is_recent), })), }, region: nodeInfo.region ?? undefined,