2023-04-20 04:20:00 -06:00
|
|
|
import { z } from "zod";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify your server-side environment variables schema here. This way you can ensure the app isn't
|
|
|
|
* built with invalid env vars.
|
|
|
|
*/
|
|
|
|
const server = z.object({
|
|
|
|
DATABASE_URL: z.string().url(),
|
2023-06-18 23:45:04 -06:00
|
|
|
UPSTASH_REDIS_REST_URL: z.string().url(),
|
|
|
|
UPSTASH_REDIS_REST_TOKEN: z.string(),
|
2023-06-28 12:18:15 -06:00
|
|
|
UPSTASH_REDIS_EXPIRY_SECONDS: z.string(),
|
2023-04-20 04:20:00 -06:00
|
|
|
NODE_ENV: z.enum(["development", "test", "production"]),
|
|
|
|
NEXTAUTH_SECRET:
|
|
|
|
process.env.NODE_ENV === "production"
|
|
|
|
? z.string().min(1)
|
|
|
|
: z.string().min(1).optional(),
|
|
|
|
NEXTAUTH_URL: z.preprocess(
|
|
|
|
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
|
|
|
|
// Since NextAuth.js automatically uses the VERCEL_URL if present.
|
|
|
|
(str) => process.env.VERCEL_URL ?? str,
|
|
|
|
// VERCEL_URL doesn't include `https` so it cant be validated as a URL
|
|
|
|
process.env.VERCEL ? z.string().min(1) : z.string().url()
|
|
|
|
),
|
2023-06-25 16:50:52 -06:00
|
|
|
GITHUB_CLIENT_ID: z.string(),
|
|
|
|
GITHUB_CLIENT_SECRET: z.string(),
|
2023-04-20 04:20:00 -06:00
|
|
|
GOOGLE_CLIENT_ID: z.string(),
|
|
|
|
GOOGLE_CLIENT_SECRET: z.string(),
|
|
|
|
ABLY_PRIVATE_KEY: z.string(),
|
|
|
|
APP_ENV: z.string(),
|
2023-06-07 22:21:02 -06:00
|
|
|
RESEND_API_KEY: z.string(),
|
2023-04-20 04:20:00 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Specify your client-side environment variables schema here. This way you can ensure the app isn't
|
|
|
|
* built with invalid env vars. To expose them to the client, prefix them with `NEXT_PUBLIC_`.
|
|
|
|
*/
|
|
|
|
const client = z.object({
|
|
|
|
NEXT_PUBLIC_ABLY_PUBLIC_KEY: z.string(),
|
|
|
|
NEXT_PUBLIC_APP_ENV: z.string(),
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
|
|
|
|
* middlewares) or client-side so we need to destruct manually.
|
|
|
|
*
|
|
|
|
* @type {Record<keyof z.infer<typeof server> | keyof z.infer<typeof client>, string | undefined>}
|
|
|
|
*/
|
|
|
|
const processEnv = {
|
|
|
|
DATABASE_URL: process.env.DATABASE_URL,
|
2023-06-18 23:45:04 -06:00
|
|
|
UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
|
|
|
|
UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
|
2023-06-28 12:18:15 -06:00
|
|
|
UPSTASH_REDIS_EXPIRY_SECONDS: process.env.UPSTASH_REDIS_EXPIRY_SECONDS,
|
2023-04-20 04:20:00 -06:00
|
|
|
NODE_ENV: process.env.NODE_ENV,
|
|
|
|
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
|
|
|
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
|
|
|
|
GITHUB_CLIENT_ID: process.env.GITHUB_CLIENT_ID,
|
|
|
|
GITHUB_CLIENT_SECRET: process.env.GITHUB_CLIENT_SECRET,
|
|
|
|
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
|
|
|
|
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
|
|
|
ABLY_PRIVATE_KEY: process.env.ABLY_PRIVATE_KEY,
|
|
|
|
NEXT_PUBLIC_ABLY_PUBLIC_KEY: process.env.NEXT_PUBLIC_ABLY_PUBLIC_KEY,
|
|
|
|
APP_ENV: process.env.APP_ENV,
|
|
|
|
NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV,
|
2023-06-07 22:21:02 -06:00
|
|
|
RESEND_API_KEY: process.env.RESEND_API_KEY,
|
2023-04-20 04:20:00 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// Don't touch the part below
|
|
|
|
// --------------------------
|
|
|
|
|
|
|
|
const merged = server.merge(client);
|
|
|
|
|
|
|
|
/** @typedef {z.input<typeof merged>} MergedInput */
|
|
|
|
/** @typedef {z.infer<typeof merged>} MergedOutput */
|
|
|
|
/** @typedef {z.SafeParseReturnType<MergedInput, MergedOutput>} MergedSafeParseReturn */
|
|
|
|
|
|
|
|
let env = /** @type {MergedOutput} */ (process.env);
|
|
|
|
|
|
|
|
if (!!process.env.SKIP_ENV_VALIDATION == false) {
|
|
|
|
const isServer = typeof window === "undefined";
|
|
|
|
|
|
|
|
const parsed = /** @type {MergedSafeParseReturn} */ (
|
|
|
|
isServer
|
|
|
|
? merged.safeParse(processEnv) // on server we can validate all env vars
|
|
|
|
: client.safeParse(processEnv) // on client we can only validate the ones that are exposed
|
|
|
|
);
|
|
|
|
|
|
|
|
if (parsed.success === false) {
|
|
|
|
console.error(
|
|
|
|
"❌ Invalid environment variables:",
|
|
|
|
parsed.error.flatten().fieldErrors
|
|
|
|
);
|
|
|
|
throw new Error("Invalid environment variables");
|
|
|
|
}
|
|
|
|
|
|
|
|
env = new Proxy(parsed.data, {
|
|
|
|
get(target, prop) {
|
|
|
|
if (typeof prop !== "string") return undefined;
|
|
|
|
// Throw a descriptive error if a server-side env var is accessed on the client
|
|
|
|
// Otherwise it would just be returning `undefined` and be annoying to debug
|
|
|
|
if (!isServer && !prop.startsWith("NEXT_PUBLIC_"))
|
|
|
|
throw new Error(
|
|
|
|
process.env.NODE_ENV === "production"
|
|
|
|
? "❌ Attempted to access a server-side environment variable on the client"
|
|
|
|
: `❌ Attempted to access server-side environment variable '${prop}' on the client`
|
|
|
|
);
|
|
|
|
return target[/** @type {keyof typeof target} */ (prop)];
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export { env };
|