From 925d9fd6dcd814807e5986c462d3e373ddd412a8 Mon Sep 17 00:00:00 2001 From: Mitchell R Date: Tue, 26 May 2026 01:32:28 +0200 Subject: [PATCH] feat: pass request obs into claimPairing for traced logging claimPairing now receives the request Observable and logs the specific reason for pending (not_found/expired/not_consumed/ missing_key). Success logged at info level with kiosk_id. All logs correlated via request trace. Co-Authored-By: Claude Opus 4.6 (1M context) --- server/src/plugins/service-api-http/index.ts | 7 ++++++- server/src/shared/pairing.ts | 10 ++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/server/src/plugins/service-api-http/index.ts b/server/src/plugins/service-api-http/index.ts index b1dd3c3..1a4263f 100644 --- a/server/src/plugins/service-api-http/index.ts +++ b/server/src/plugins/service-api-http/index.ts @@ -327,7 +327,8 @@ function registerPairingRoutes( const code = (body?.code ?? "").trim().toUpperCase(); if (!code) throw createError({ statusCode: 400, statusMessage: "code required" }); - const result = await claimPairing(repo, code); + const reqObs = event.context.obs!; + const result = await claimPairing(repo, code, reqObs); if (result.status === "pending") { return new Response(JSON.stringify({ status: "pending" }), { status: 202, @@ -335,6 +336,10 @@ function registerPairingRoutes( }); } + reqObs.log.info("pair/claim success for code {code} kiosk {kioskId}", { + code, + kioskId: String(result.kioskId), + }); return { status: "claimed", kiosk_id: result.kioskId, diff --git a/server/src/shared/pairing.ts b/server/src/shared/pairing.ts index 6f2cb71..4126b95 100644 --- a/server/src/shared/pairing.ts +++ b/server/src/shared/pairing.ts @@ -7,6 +7,7 @@ * 3. Admin enters code in UI → confirmPairing creates kiosk + kiosk_key */ import { randomBytes } from "node:crypto"; +import type { Observable } from "@bsb/base"; import type { Repository } from "./db/repository.js"; import type { AuthApi } from "./auth.js"; import type { SecretsApi } from "./secrets.js"; @@ -76,16 +77,17 @@ export interface PairingClaimResult { export async function claimPairing( repo: Repository, code: string, + obs?: Observable, ): Promise { const pc = await repo.getPairingCode(code); - if (!pc) return { status: "pending" }; - if (new Date(pc.expires_at) < new Date()) return { status: "pending" }; - if (!pc.consumed_at) return { status: "pending" }; + if (!pc) { obs?.log.debug("claim {code}: code not found", { code }); return { status: "pending" }; } + if (new Date(pc.expires_at) < new Date()) { obs?.log.debug("claim {code}: expired", { code }); return { status: "pending" }; } + if (!pc.consumed_at) { obs?.log.debug("claim {code}: not yet consumed", { code }); return { status: "pending" }; } const extras = pc.extras as Record; const kioskKey = extras["kiosk_key_plaintext"] as string | undefined; - if (!kioskKey || !pc.consumed_by_kiosk_id) return { status: "pending" }; + if (!kioskKey || !pc.consumed_by_kiosk_id) { obs?.log.warn("claim {code}: consumed but missing key/id", { code }); return { status: "pending" }; } const kiosk = await repo.getKioskById(pc.consumed_by_kiosk_id); const clusterKey = extras["cluster_key"] as string | undefined;