-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenv.ts
More file actions
180 lines (149 loc) · 5.71 KB
/
env.ts
File metadata and controls
180 lines (149 loc) · 5.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import {z} from 'zod'
/**
* Schema for environment variables validation using Zod.
*/
const envSchema = z.object({
// Optional environment variable for Vercel environment
VERCEL_ENV: z.enum(['development', 'production', 'preview'], {
errorMap: () => ({message: 'Environment must be either "development", "preview" or "production"'})
}).optional().default('development'),
// Optional main URL, must be a valid URL
MAIN_URL: z.string().url({
message: 'MAIN_URL must be a valid URL (e.g., http://localhost:3000)'
}).optional().default('http://localhost:3000'),
// Required backend API URL, must be a valid URL
API_URL: z.string().url({
message: 'API_URL must be a valid URL'
}),
// Required self URL, must be a valid URL
NEXT_PUBLIC_SELF_URL: z.string().url({
message: 'SELF_URL must be a valid URL'
}),
// Required Clerk publishable key, must start with "pk_"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string().startsWith('pk_', {
message: 'Clerk publishable key must start with "pk_"'
}),
// Required Clerk secret key, must start with "sk_"
CLERK_SECRET_KEY: z.string().startsWith('sk_', {
message: 'Clerk secret key must start with "sk_"'
}),
// Required Clerk sign-in URL, must start with "/"
NEXT_PUBLIC_CLERK_SIGN_IN_URL: z.string().startsWith('/', {
message: 'Sign in URL must start with "/" (e.g., /signin)'
}),
// Required Clerk sign-up URL, must start with "/"
NEXT_PUBLIC_CLERK_SIGN_UP_URL: z.string().startsWith('/', {
message: 'Sign up URL must start with "/" (e.g., /signup)'
}),
// Required Resend API key, must start with "re_"
RESEND_API_KEY: z.string().startsWith('re_', {
message: 'Resend API key must start with "re_"'
}),
// Required Ghost CMS API key
GHOST_API_KEY: z.string({
message: 'Ghost API key must be a string'
}),
// Required PostHog key, must start with "phc_"
NEXT_PUBLIC_POSTHOG_KEY: z.string().startsWith('phc_', {
message: 'PostHog key must start with "phc_"'
}),
// Required PostHog host URL, must be a valid URL
NEXT_PUBLIC_POSTHOG_HOST: z.string().url({
message: 'PostHog host must be a valid URL (e.g., https://us.i.posthog.com)'
}),
// Required Discord bot token
DISCORD_BOT_TOKEN: z.string({
required_error: 'Discord bot token is required',
invalid_type_error: 'Discord bot token must be a string'
}),
// Optional Sentry auth token, must start with "sntrys_"
SENTRY_AUTH_TOKEN: z.string()
.regex(/^sntrys_.*/, {
message: 'Sentry auth token must start with "sntrys_"'
})
.optional(),
// Required Sentry DSN, must be a valid URL and match specific format
SENTRY_DSN: z.string()
.url({message: 'SENTRY_DSN must be a valid URL'})
.regex(/^https:\/\/[a-zA-Z0-9]+@[a-zA-Z0-9]+\.ingest\.(us|uk)\.sentry\.io\/[0-9]+$/, {
message: 'Invalid Sentry DSN format'
})
.transform(url => url.toString()),
// Required Knock public API key for client-side usage
NEXT_PUBLIC_KNOCK_PUBLIC_API_KEY: z.string({
required_error: 'Knock public API key is required',
invalid_type_error: 'Knock public API key must be a string'
}),
// Required Knock feed channel ID
NEXT_PUBLIC_KNOCK_FEED_CHANNEL_ID: z.string({
required_error: 'Knock feed channel ID is required',
invalid_type_error: 'Knock feed channel ID must be a string'
}),
// Required backend admin API key for accessing admin endpoints
BACKEND_ADMIN_API_KEY: z.string({
required_error: 'Backend admin API key is required',
invalid_type_error: 'Backend admin API key must be a string'
}),
// Required self secret key for API route authentication
SELF_SECRET_KEY: z.string({
required_error: 'Self secret key is required',
invalid_type_error: 'Self secret key must be a string'
}),
// Required Gemini API key for Vercel AI SDK
GOOGLE_GENERATIVE_AI_API_KEY: z.string({
required_error: 'Gemini API key is required',
invalid_type_error: 'Gemini API key must be a string'
}),
// Required BrightData credentials for LinkedIn scraping
BRIGHTDATA_TOKEN: z.string({
required_error: 'BrightData token is required',
invalid_type_error: 'BrightData token must be a string'
}),
BRIGHTDATA_DATASET_ID: z.string({
required_error: 'BrightData dataset id is required',
invalid_type_error: 'BrightData dataset id must be a string'
}),
// Rize admin API key for server-to-server calls
RIZE_ADMIN_API_KEY: z.string({
required_error: 'Rize admin API key is required',
invalid_type_error: 'Rize admin API key must be a string'
}),
// Optional feature flag for Resume Editor tabs new design
NEXT_PUBLIC_RESUME_EDITOR_TABS_NEW_DESIGN_ENABLED: z.string()
.optional()
.default('true'),
// Optional Algolia search configuration for client-side search
NEXT_PUBLIC_ALGOLIA_APPLICATION_ID: z.string(),
NEXT_PUBLIC_ALGOLIA_SEARCH_ONLY_API_KEY: z.string(),
NEXT_PUBLIC_ALGOLIA_INDEX_NAME: z.string(),
// Required BaseHub token for CMS integration
BASEHUB_TOKEN: z.string({
required_error: 'BaseHub token is required for documentation',
invalid_type_error: 'BaseHub token must be a string'
})
})
// Type representing the environment variables schema.
type Env = z.infer<typeof envSchema>
/**
* Validates the environment variables against the schema.
* @returns {Env} - The validated environment variables.
* @throws {Error} - Throws an error if validation fails.
*/
const validateEnv = (): Env => {
try {
return envSchema.parse(process.env)
} catch (error) {
if (error instanceof z.ZodError) {
const errorMessages = error.errors.map(err => `${err.path.join('.')}: ${err.message}`).join('\n')
throw new Error(`❌ Invalid environment variables:\n${errorMessages}`)
}
throw error
}
}
declare global {
namespace NodeJS {
// Extends the Node.js ProcessEnv interface with the environment variables schema.
interface ProcessEnv extends z.infer<typeof envSchema> {}
}
}
export {validateEnv, envSchema}