feat(ai_working_help): expose folder_uid and user_id on GET /v1/response

Allows LeCoffre backend to validate polls after process restart.
This commit is contained in:
Nicolas Cantu 2026-04-10 00:07:23 +02:00
parent 7eec9d15ee
commit b5e1749b74
2 changed files with 14 additions and 2 deletions

View File

@ -35,7 +35,7 @@
Écrit dans `projects/<id>/data/notary-ai/pending/<safe_uid>.json`. **Authentification** : header `Authorization: Bearer <token>` obligatoire (le token identifie le projet) ; 401 si absent ou inconnu. Écrit dans `projects/<id>/data/notary-ai/pending/<safe_uid>.json`. **Authentification** : header `Authorization: Bearer <token>` obligatoire (le token identifie le projet) ; 401 si absent ou inconnu.
- **GET /v1/response/:request_uid** - **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`). Si le fichier responded contient `anon_mapping`, la réponse est recontextualisée avant envoi (voir `docs/business-qa-api.md`).
Lit dabord `projects/<id>/data/notary-ai/responded/`, sinon `pending/`. **Authentification** : idem (token obligatoire). Lit dabord `projects/<id>/data/notary-ai/responded/`, sinon `pending/`. **Authentification** : idem (token obligatoire).

View File

@ -97,6 +97,17 @@ function findFileByRequestUid(dir, requestUid) {
return null; 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) => { app.get("/health", (req, res) => {
res.status(200).json({ status: "ok" }); res.status(200).json({ status: "ok" });
}); });
@ -165,12 +176,13 @@ app.get("/v1/response/:request_uid", requireApiTokenAndResolveProject, (req, res
return res.status(200).json({ return res.status(200).json({
status: "responded", status: "responded",
response, response,
...ownershipFromPayload(foundResponded.data),
}); });
} }
const pendingDir = path.join(dir, "pending"); const pendingDir = path.join(dir, "pending");
const foundPending = findFileByRequestUid(pendingDir, requestUid); const foundPending = findFileByRequestUid(pendingDir, requestUid);
if (foundPending) { 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" }); res.status(200).json({ status: "pending" });
}); });