From b5e1749b741ff8a3a5622b282700143ed29efb22 Mon Sep 17 00:00:00 2001 From: Nicolas Cantu Date: Fri, 10 Apr 2026 00:07:23 +0200 Subject: [PATCH] feat(ai_working_help): expose folder_uid and user_id on GET /v1/response Allows LeCoffre backend to validate polls after process restart. --- ai_working_help/docs/notary-ai-api.md | 2 +- ai_working_help/server.js | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ai_working_help/docs/notary-ai-api.md b/ai_working_help/docs/notary-ai-api.md index 018642c..df99ac5 100644 --- a/ai_working_help/docs/notary-ai-api.md +++ b/ai_working_help/docs/notary-ai-api.md @@ -35,7 +35,7 @@ Écrit dans `projects//data/notary-ai/pending/.json`. **Authentification** : header `Authorization: Bearer ` obligatoire (le token identifie le projet) ; 401 si absent ou inconnu. - **GET /v1/response/:request_uid** - Réponse **200** : `{ status: "pending" }` ou `{ status: "responded", response: { answer, nextActionsTable, membersInfoSheet, synthesisRecommendation } }`. + Réponse **200** : `{ status: "pending" }` ou `{ status: "responded", response: { answer, nextActionsTable, membersInfoSheet, synthesisRecommendation } }`. Lorsque le fichier pending ou responded est trouvé, le corps inclut en plus **`folder_uid`** et **`user_id`** (issus du JSON spooler) pour que le backend LeCoffre autorise le poll même si sa table mémoire `request_uid → meta` a été perdue (redémarrage, autre instance). Si le fichier responded contient `anon_mapping`, la réponse est recontextualisée avant envoi (voir `docs/business-qa-api.md`). Lit d’abord `projects//data/notary-ai/responded/`, sinon `pending/`. **Authentification** : idem (token obligatoire). diff --git a/ai_working_help/server.js b/ai_working_help/server.js index dc0b3d4..421faff 100644 --- a/ai_working_help/server.js +++ b/ai_working_help/server.js @@ -97,6 +97,17 @@ function findFileByRequestUid(dir, requestUid) { return null; } +/** Expose folder_uid + user_id so backends without in-memory meta (restart, multi-instance) can authorize GET /response. */ +function ownershipFromPayload(data) { + if (!data || typeof data !== "object") return {}; + const folder_uid = data.folder_uid; + const user_id = data.user_id; + if (typeof folder_uid === "string" && typeof user_id === "string" && folder_uid.length > 0 && user_id.length > 0) { + return { folder_uid, user_id }; + } + return {}; +} + app.get("/health", (req, res) => { res.status(200).json({ status: "ok" }); }); @@ -165,12 +176,13 @@ app.get("/v1/response/:request_uid", requireApiTokenAndResolveProject, (req, res return res.status(200).json({ status: "responded", response, + ...ownershipFromPayload(foundResponded.data), }); } const pendingDir = path.join(dir, "pending"); const foundPending = findFileByRequestUid(pendingDir, requestUid); if (foundPending) { - return res.status(200).json({ status: "pending" }); + return res.status(200).json({ status: "pending", ...ownershipFromPayload(foundPending.data) }); } res.status(200).json({ status: "pending" }); });