project-env-cache.ts40 lines · main
| 1 | import { env } from './env.js'; |
| 2 | |
| 3 | /** |
| 4 | * Per-project env var cache for the Deno isolate executor. The control |
| 5 | * plane decrypts and serves env vars over `/v1/internal/projects/:id/env` |
| 6 | * (shared-secret-protected); we cache the response for 60s so a hot |
| 7 | * project doesn't pay a fetch on every cold-start. |
| 8 | * |
| 9 | * Invalidation strategy: pure TTL. When users update an env var via the |
| 10 | * dashboard, the next cold-start within 60s still uses the old value. |
| 11 | * That's the correct trade-off for Phase 1 — env edits are rare and the |
| 12 | * worst case is a one-minute delay, not a security hole. |
| 13 | */ |
| 14 | const cache = new Map<string, { values: Record<string, string>; expiresAt: number }>(); |
| 15 | const TTL_MS = 60_000; |
| 16 | |
| 17 | export async function fetchProjectEnv(projectId: string): Promise<Record<string, string>> { |
| 18 | const cached = cache.get(projectId); |
| 19 | if (cached && cached.expiresAt > Date.now()) return cached.values; |
| 20 | |
| 21 | const headers: Record<string, string> = { accept: 'application/json' }; |
| 22 | if (env.BRIVEN_RUNTIME_SHARED_SECRET) { |
| 23 | headers['authorization'] = `Bearer ${env.BRIVEN_RUNTIME_SHARED_SECRET}`; |
| 24 | } |
| 25 | const url = `${env.BRIVEN_API_INTERNAL_URL}/v1/internal/projects/${projectId}/env`; |
| 26 | const res = await fetch(url, { headers }); |
| 27 | if (res.status === 404) { |
| 28 | cache.set(projectId, { values: {}, expiresAt: Date.now() + TTL_MS }); |
| 29 | return {}; |
| 30 | } |
| 31 | if (!res.ok) throw new Error(`fetchProjectEnv ${projectId}: ${res.status}`); |
| 32 | const values = (await res.json()) as Record<string, string>; |
| 33 | cache.set(projectId, { values, expiresAt: Date.now() + TTL_MS }); |
| 34 | return values; |
| 35 | } |
| 36 | |
| 37 | /** Test hook — clears the in-memory cache so unit tests don't see staleness. */ |
| 38 | export function clearProjectEnvCache(): void { |
| 39 | cache.clear(); |
| 40 | } |