fix(broker): show mesh slugs in /meshes + /status, remove all-meshes fallback
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

- /meshes and /status now show mesh slug names instead of truncated IDs
- meshSlug cached on connect and loaded from DB join on boot
- Remove dangerous fallback that connected to ALL meshes in email flow
- BridgeRow now includes optional meshSlug field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-10 02:24:55 +01:00
parent 5df2664bae
commit a022da1998
2 changed files with 15 additions and 11 deletions

View File

@@ -4124,14 +4124,17 @@ function main(): void {
const rows = await db.select({ const rows = await db.select({
chatId: telegramBridge.chatId, chatId: telegramBridge.chatId,
meshId: telegramBridge.meshId, meshId: telegramBridge.meshId,
meshSlug: mesh.slug,
memberId: telegramBridge.memberId, memberId: telegramBridge.memberId,
pubkey: telegramBridge.pubkey, pubkey: telegramBridge.pubkey,
secretKey: telegramBridge.secretKey, secretKey: telegramBridge.secretKey,
displayName: telegramBridge.displayName, displayName: telegramBridge.displayName,
chatType: telegramBridge.chatType, chatType: telegramBridge.chatType,
chatTitle: telegramBridge.chatTitle, chatTitle: telegramBridge.chatTitle,
}).from(telegramBridge).where(eq(telegramBridge.active, true)); }).from(telegramBridge)
return rows.map(r => ({ ...r, chatId: Number(r.chatId) })); .leftJoin(mesh, eq(telegramBridge.meshId, mesh.id))
.where(eq(telegramBridge.active, true));
return rows.map(r => ({ ...r, meshSlug: r.meshSlug ?? undefined, chatId: Number(r.chatId) }));
}, },
async (row) => { async (row) => {
await db.insert(telegramBridge).values({ await db.insert(telegramBridge).values({
@@ -4170,12 +4173,7 @@ function main(): void {
const byUserId = await db.select({ meshId: meshMember.meshId }) const byUserId = await db.select({ meshId: meshMember.meshId })
.from(meshMember).where(and(eq(meshMember.userId, userId), isNull(meshMember.revokedAt))); .from(meshMember).where(and(eq(meshMember.userId, userId), isNull(meshMember.revokedAt)));
for (const m of byUserId) meshIds.add(m.meshId); for (const m of byUserId) meshIds.add(m.meshId);
// Fallback: if user has no members, check all meshes (owner bootstraps) // No fallback user must be an explicit member of a mesh
if (meshIds.size === 0) {
const allMeshes = await db.select({ id: mesh.id }).from(mesh);
for (const m of allMeshes) meshIds.add(m.id);
log.info("tg-email-connect: no member found, trying all meshes", { email, userId, meshCount: meshIds.size });
}
if (meshIds.size === 0) return []; if (meshIds.size === 0) return [];
const existingMembers = Array.from(meshIds).map(meshId => ({ meshId })); const existingMembers = Array.from(meshIds).map(meshId => ({ meshId }));

View File

@@ -21,6 +21,7 @@ import { validateTelegramConnectToken } from "./telegram-token";
export interface BridgeRow { export interface BridgeRow {
chatId: number; chatId: number;
meshId: string; meshId: string;
meshSlug?: string;
memberId: string; memberId: string;
pubkey: string; pubkey: string;
secretKey: string; secretKey: string;
@@ -510,6 +511,9 @@ const meshChats = new Map<string, Set<number>>();
/** meshId → shared WS connection */ /** meshId → shared WS connection */
const meshConnections = new Map<string, MeshConnection>(); const meshConnections = new Map<string, MeshConnection>();
/** meshId → slug (human-readable name) */
const meshSlugs = new Map<string, string>();
// Pending DM picker state: chatId → { message, matches, meshId } // Pending DM picker state: chatId → { message, matches, meshId }
const pendingDMs = new Map< const pendingDMs = new Map<
number, number,
@@ -809,6 +813,7 @@ function setupBotCommands(
); );
linkChatMesh(chatId, meshId); linkChatMesh(chatId, meshId);
if (meshSlug) meshSlugs.set(meshId, meshSlug);
await ctx.reply( await ctx.reply(
`✅ Connected to mesh *${escapeMarkdown(meshSlug ?? meshId.slice(0, 8))}*\\!`, `✅ Connected to mesh *${escapeMarkdown(meshSlug ?? meshId.slice(0, 8))}*\\!`,
@@ -909,7 +914,7 @@ function setupBotCommands(
const lines = meshIds.map((id) => { const lines = meshIds.map((id) => {
const conn = meshConnections.get(id); const conn = meshConnections.get(id);
const status = conn?.isConnected() ? "🟢" : "🔴"; const status = conn?.isConnected() ? "🟢" : "🔴";
return `${status} \`${id.slice(0, 16)}\``; return `${status} \`${meshSlugs.get(id) ?? id.slice(0, 12)}\``;
}); });
await ctx.reply(`*Connected meshes:*\n${lines.join("\n")}`, { await ctx.reply(`*Connected meshes:*\n${lines.join("\n")}`, {
parse_mode: "Markdown", parse_mode: "Markdown",
@@ -1104,7 +1109,7 @@ function setupBotCommands(
const lines = meshIds.map((id) => { const lines = meshIds.map((id) => {
const conn = meshConnections.get(id); const conn = meshConnections.get(id);
const icon = conn?.isConnected() ? "🟢" : "🔴"; const icon = conn?.isConnected() ? "🟢" : "🔴";
return `${icon} \`${id.slice(0, 16)}\``; return `${icon} \`${meshSlugs.get(id) ?? id.slice(0, 12)}\``;
}); });
await ctx.reply( await ctx.reply(
`*Claudemesh Telegram Bridge*\n${lines.join("\n")}`, `*Claudemesh Telegram Bridge*\n${lines.join("\n")}`,
@@ -1624,9 +1629,10 @@ export async function bootTelegramBridge(
); );
} }
// Populate routing maps for all chats in this mesh // Populate routing maps and slug cache for all chats in this mesh
for (const row of meshRows) { for (const row of meshRows) {
linkChatMesh(row.chatId, meshId); linkChatMesh(row.chatId, meshId);
if (row.meshSlug) meshSlugs.set(meshId, row.meshSlug);
} }
} }