`validateAuthEnv()` was running at module-load time, which fired during `astro build` while constructing the route manifest — before any actual request needed the secrets. CI build failed even though build-time code doesn't use JWT_SECRET / OAuth secrets. - src/lib/auth/env.ts: wrap authEnv in a Proxy that runs validation on first property read; cache the validated object after. - src/lib/auth/jwt.ts: defer `new TextEncoder().encode(...)` of the secret behind a memoised getSecret() helper. - src/lib/auth/oauth.ts: same for the JWT secret; vkOAuthConfig and yandexOAuthConfig switched to getter-based properties so credential access is also lazy. - src/lib/auth/auth.test.ts: 3 tests previously asserted "throws at module load"; updated to assert "throws on first access" — semantic guarantee (invalid env throws) is preserved. Runtime fail-fast is intact: any auth route reading authEnv.X will throw with the same descriptive Zod error if a var is missing. Build just no longer crashes when secrets aren't in env (e.g. CI deploy pipeline running tsc/lint/build without secrets). Verified: npm run build succeeds with no auth env vars set; tsc 0, lint 0, vitest 339/339. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
const envSchema = z.object({
|
|
JWT_SECRET: z.string().min(1, 'JWT_SECRET is required'),
|
|
VK_CLIENT_ID: z.string().min(1, 'VK_CLIENT_ID is required'),
|
|
VK_CLIENT_SECRET: z.string().min(1, 'VK_CLIENT_SECRET is required'),
|
|
YANDEX_CLIENT_ID: z.string().min(1, 'YANDEX_CLIENT_ID is required'),
|
|
YANDEX_CLIENT_SECRET: z.string().min(1, 'YANDEX_CLIENT_SECRET is required'),
|
|
PUBLIC_APP_URL: z.string().url('PUBLIC_APP_URL must be a valid URL'),
|
|
});
|
|
|
|
type AuthEnv = z.infer<typeof envSchema>;
|
|
|
|
let cached: AuthEnv | null = null;
|
|
|
|
function loadAuthEnv(): AuthEnv {
|
|
if (cached) return cached;
|
|
cached = envSchema.parse({
|
|
JWT_SECRET: process.env.JWT_SECRET,
|
|
VK_CLIENT_ID: process.env.VK_CLIENT_ID,
|
|
VK_CLIENT_SECRET: process.env.VK_CLIENT_SECRET,
|
|
YANDEX_CLIENT_ID: process.env.YANDEX_CLIENT_ID,
|
|
YANDEX_CLIENT_SECRET: process.env.YANDEX_CLIENT_SECRET,
|
|
PUBLIC_APP_URL: process.env.PUBLIC_APP_URL,
|
|
});
|
|
return cached;
|
|
}
|
|
|
|
// Lazy proxy: validation fires only when a key is actually read at runtime,
|
|
// not when the module is first imported (which happens during `astro build`).
|
|
export const authEnv = new Proxy({} as AuthEnv, {
|
|
get(_target, key: string) {
|
|
return loadAuthEnv()[key as keyof AuthEnv];
|
|
},
|
|
}) as AuthEnv;
|
|
|
|
export function requireEnv(name: keyof AuthEnv): string {
|
|
return loadAuthEnv()[name];
|
|
}
|