fix(api): dedupe /v1/peers by member (one row per active session)
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

This commit is contained in:
Alejandro Gutiérrez
2026-05-02 02:27:50 +01:00
parent 8b5708a604
commit 9418d0ee30

View File

@@ -240,21 +240,46 @@ export const v1Router = new Hono<Env>()
) )
// GET /v1/peers — connected peers in the key's mesh // GET /v1/peers — connected peers in the key's mesh
// Dedupe by memberId — a member can have multiple active presence
// rows (one per session). Status reflects the most recent presence;
// summary/groups come from the latest row.
.get("/peers", async (c) => { .get("/peers", async (c) => {
const key = c.var.apiKey; const key = c.var.apiKey;
requireCapability(key, "read"); requireCapability(key, "read");
const rows = await db const rows = await db
.select({ .select({
memberId: meshMember.id,
pubkey: meshMember.peerPubkey, pubkey: meshMember.peerPubkey,
displayName: meshMember.displayName, displayName: meshMember.displayName,
status: presence.status, status: presence.status,
summary: presence.summary, summary: presence.summary,
groups: presence.groups, groups: presence.groups,
connectedAt: presence.connectedAt,
}) })
.from(presence) .from(presence)
.innerJoin(meshMember, eq(presence.memberId, meshMember.id)) .innerJoin(meshMember, eq(presence.memberId, meshMember.id))
.where( .where(
and(eq(meshMember.meshId, key.meshId), isNull(presence.disconnectedAt)), and(eq(meshMember.meshId, key.meshId), isNull(presence.disconnectedAt)),
); )
return c.json({ peers: rows }); .orderBy(desc(presence.connectedAt));
const seen = new Set<string>();
const peers: Array<{
pubkey: string;
displayName: string;
status: string;
summary: string | null;
groups: unknown;
}> = [];
for (const r of rows) {
if (seen.has(r.memberId)) continue;
seen.add(r.memberId);
peers.push({
pubkey: r.pubkey,
displayName: r.displayName,
status: r.status,
summary: r.summary,
groups: r.groups,
});
}
return c.json({ peers });
}); });