- /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>
55 lines
1.9 KiB
TypeScript
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 };
|
|
}
|