mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-28 23:57:17 +09:00
170 lines
5.3 KiB
TypeScript
170 lines
5.3 KiB
TypeScript
import { z } from "zod";
|
|
|
|
const environment = z.object({
|
|
// Authentication
|
|
DEV_LOGIN: z.string().optional(),
|
|
GH_CLIENT_ID: z.string().optional(),
|
|
GH_CLIENT_SECRET: z.string().optional(),
|
|
GITHUB_ISSUE_REPORT_APP_ID: z.string().optional(),
|
|
GITHUB_ISSUE_REPORT_PRIVATE_KEY: z.string().optional(),
|
|
GOOGLE_CLIENT_ID: z.string().optional(),
|
|
GOOGLE_CLIENT_SECRET: z.string().optional(),
|
|
|
|
// Secret session key, context encode
|
|
AUTH_SECRET: z.string().optional(),
|
|
|
|
// DEPLOYMENT_ENVIRONMENT development | preview | production
|
|
DEPLOYMENT_ENVIRONMENT: z
|
|
.enum(["development", "preview", "production"])
|
|
.optional(),
|
|
DEPLOYMENT_URL: z.string().url().optional(),
|
|
|
|
// Trpc on SaaS
|
|
TRPC_SERVER_URL: z.string().url().optional(),
|
|
TRPC_SERVER_API_TOKEN: z.string().optional(),
|
|
|
|
PORT: z
|
|
.string()
|
|
.optional()
|
|
.transform((val) => (val ? parseInt(val, 10) : undefined)),
|
|
|
|
// Assets
|
|
MAX_UPLOAD_SIZE: z.string().optional(),
|
|
|
|
// Remote assets
|
|
ASSET_CDN_URL: z.string().url().optional(),
|
|
S3_ENDPOINT: z.string().optional(),
|
|
S3_REGION: z.string().optional(),
|
|
S3_ACCESS_KEY_ID: z.string().optional(),
|
|
S3_SECRET_ACCESS_KEY: z.string().optional(),
|
|
S3_BUCKET: z.string().optional(),
|
|
S3_ACL: z.string().optional(),
|
|
|
|
// Origin for /cdn-cgi/image/ cloudflare endpoint (without ending slash)
|
|
RESIZE_ORIGIN: z.string().url().optional(),
|
|
|
|
// Entri API credentials
|
|
ENTRI_APPLICATION_ID: z.string().default("webstudio"),
|
|
ENTRI_SECRET: z.string().optional(),
|
|
|
|
PUBLISHER_HOST: z.string().default("wstd.work"),
|
|
|
|
STAGING_USERNAME: z.string().default("admin"),
|
|
STAGING_PASSWORD: z.string().default("webstudio"),
|
|
|
|
FEATURE_FLAGS: z.string().default(""),
|
|
|
|
// Plan definitions for local dev/staging (JSON array of {name, features})
|
|
PLANS: z
|
|
.string()
|
|
.default("[]")
|
|
.refine((val) => {
|
|
try {
|
|
JSON.parse(val);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}, "PLANS must be valid JSON"),
|
|
|
|
POSTGREST_URL: z.string().url().default("http://localhost:3000"),
|
|
POSTGREST_API_KEY: z.string().default(""),
|
|
|
|
// Payment worker configuration
|
|
PAYMENT_WORKER_URL: z.string().optional(),
|
|
PAYMENT_WORKER_TOKEN: z.string().optional(),
|
|
|
|
// Used for project oauth login flow
|
|
AUTH_WS_CLIENT_ID: z.string().default("12345"),
|
|
AUTH_WS_CLIENT_SECRET: z.string().default("12345678"),
|
|
|
|
// Vercel system variables
|
|
VERCEL: z.string().optional(),
|
|
VERCEL_ENV: z.enum(["development", "preview", "production"]).optional(),
|
|
VERCEL_URL: z.string().optional(),
|
|
});
|
|
|
|
const rawEnv = {
|
|
DEV_LOGIN: process.env.DEV_LOGIN,
|
|
GH_CLIENT_ID: process.env.GH_CLIENT_ID,
|
|
GH_CLIENT_SECRET: process.env.GH_CLIENT_SECRET,
|
|
GITHUB_ISSUE_REPORT_APP_ID: process.env.GITHUB_ISSUE_REPORT_APP_ID,
|
|
GITHUB_ISSUE_REPORT_PRIVATE_KEY: process.env.GITHUB_ISSUE_REPORT_PRIVATE_KEY,
|
|
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
|
|
GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
|
|
AUTH_SECRET: process.env.AUTH_SECRET,
|
|
DEPLOYMENT_ENVIRONMENT: process.env.DEPLOYMENT_ENVIRONMENT,
|
|
DEPLOYMENT_URL: process.env.DEPLOYMENT_URL,
|
|
TRPC_SERVER_URL: process.env.TRPC_SERVER_URL,
|
|
TRPC_SERVER_API_TOKEN: process.env.TRPC_SERVER_API_TOKEN,
|
|
PORT: process.env.PORT,
|
|
MAX_UPLOAD_SIZE: process.env.MAX_UPLOAD_SIZE,
|
|
ASSET_CDN_URL: process.env.ASSET_CDN_URL,
|
|
S3_ENDPOINT: process.env.S3_ENDPOINT,
|
|
S3_REGION: process.env.S3_REGION,
|
|
S3_ACCESS_KEY_ID: process.env.S3_ACCESS_KEY_ID,
|
|
S3_SECRET_ACCESS_KEY: process.env.S3_SECRET_ACCESS_KEY,
|
|
S3_BUCKET: process.env.S3_BUCKET,
|
|
S3_ACL: process.env.S3_ACL,
|
|
RESIZE_ORIGIN: process.env.RESIZE_ORIGIN,
|
|
ENTRI_APPLICATION_ID: process.env.ENTRI_APPLICATION_ID,
|
|
ENTRI_SECRET: process.env.ENTRI_SECRET,
|
|
PUBLISHER_HOST: process.env.PUBLISHER_HOST,
|
|
STAGING_USERNAME: process.env.STAGING_USERNAME,
|
|
STAGING_PASSWORD: process.env.STAGING_PASSWORD,
|
|
FEATURE_FLAGS: process.env.FEATURE_FLAGS,
|
|
PLANS: process.env.PLANS,
|
|
POSTGREST_URL: process.env.POSTGREST_URL,
|
|
POSTGREST_API_KEY: process.env.POSTGREST_API_KEY,
|
|
PAYMENT_WORKER_URL: process.env.PAYMENT_WORKER_URL,
|
|
PAYMENT_WORKER_TOKEN: process.env.PAYMENT_WORKER_TOKEN,
|
|
AUTH_WS_CLIENT_ID: process.env.AUTH_WS_CLIENT_ID,
|
|
AUTH_WS_CLIENT_SECRET: process.env.AUTH_WS_CLIENT_SECRET,
|
|
VERCEL: process.env.VERCEL,
|
|
VERCEL_ENV: process.env.VERCEL_ENV,
|
|
VERCEL_URL: process.env.VERCEL_URL,
|
|
};
|
|
|
|
const parseResult = environment.safeParse(rawEnv);
|
|
|
|
if (!parseResult.success) {
|
|
console.error("Invalid environment variables:", parseResult.error.format());
|
|
throw new Error(
|
|
`Environment validation failed: ${parseResult.error.message}`
|
|
);
|
|
}
|
|
|
|
const env = {
|
|
...parseResult.data,
|
|
SECURE_COOKIE: true,
|
|
};
|
|
|
|
// Reject default OAuth secrets in production.
|
|
if (
|
|
env.DEPLOYMENT_ENVIRONMENT === "production" &&
|
|
(env.AUTH_WS_CLIENT_ID === "12345" ||
|
|
env.AUTH_WS_CLIENT_SECRET === "12345678")
|
|
) {
|
|
throw new Error(
|
|
"AUTH_WS_CLIENT_ID and AUTH_WS_CLIENT_SECRET must be set in production"
|
|
);
|
|
}
|
|
|
|
export type ServerEnv = typeof env;
|
|
|
|
// https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables
|
|
if (process.env.VERCEL !== undefined) {
|
|
if (env.DEPLOYMENT_ENVIRONMENT === undefined) {
|
|
env.DEPLOYMENT_ENVIRONMENT = process.env.VERCEL_ENV as
|
|
| "development"
|
|
| "preview"
|
|
| "production"
|
|
| undefined;
|
|
}
|
|
if (env.DEPLOYMENT_URL === undefined) {
|
|
env.DEPLOYMENT_URL = process.env.VERCEL_URL;
|
|
}
|
|
}
|
|
|
|
export default env;
|