expria-backend/src/lib/deepgram.ts
Hermann_Kitio 14880fe94c fix(deepgram): revert to /v1/auth/grant for temporary JWT tokens
- /v1/projects/{id}/keys creates permanent API keys, not WebSocket-compatible JWT tokens
- /v1/auth/grant requires Member-scoped API key (now configured)
- Remove DEEPGRAM_PROJECT_ID dependency
- Update tests

Typecheck: OK · Tests: 241/241 

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 05:49:45 +03:00

55 lines
1.9 KiB
TypeScript

/**
* Client Deepgram — Sprint 4b.
*
* Génère un token éphémère que le frontend utilise pour ouvrir une connexion
* WebSocket directe à Deepgram (transcription live). Le token est passé en
* query string `?token=...` lors de l'init de la WS — c'est le seul mécanisme
* de tokens éphémères WebSocket-compatible côté Deepgram. Les clés API créées
* via `/v1/projects/{id}/keys` sont permanentes et ne fonctionnent pas en
* query string sur la WS.
*
* Endpoint : POST https://api.deepgram.com/v1/auth/grant
* Doc : https://developers.deepgram.com/docs/create-temporary-api-key
*
* Pré-requis : la clé `DEEPGRAM_API_KEY` doit avoir le scope « Member » du
* projet. Sans ce scope, l'endpoint renvoie 403.
*/
const DEEPGRAM_API_KEY = process.env.DEEPGRAM_API_KEY ?? "";
const DEEPGRAM_BASE_URL = "https://api.deepgram.com";
const DEEPGRAM_TIMEOUT_MS = 10_000;
export interface DeepgramToken {
token: string;
expires_in: number;
}
export async function createTemporaryToken(
ttlSeconds: number,
): Promise<DeepgramToken> {
const response = await fetch(`${DEEPGRAM_BASE_URL}/v1/auth/grant`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Token ${DEEPGRAM_API_KEY}`,
},
body: JSON.stringify({ ttl_seconds: ttlSeconds }),
signal: AbortSignal.timeout(DEEPGRAM_TIMEOUT_MS),
});
if (!response.ok) {
throw new Error(
`Deepgram API error: ${response.status} ${response.statusText}`,
);
}
const data = (await response.json()) as { access_token?: string };
if (typeof data.access_token !== "string" || data.access_token.length === 0) {
throw new Error("Deepgram API: access_token manquant dans la réponse");
}
// L'API retourne le TTL effectif dans le payload (champ `expires_in`),
// mais on retourne la valeur demandée pour cohérence avec le frontend.
return { token: data.access_token, expires_in: ttlSeconds };
}