-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Leverage t3-oss/env-core for better env vars
- Loading branch information
Showing
11 changed files
with
174 additions
and
159 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,107 +1,59 @@ | ||
import {makeId, zEnvName} from '@usevenice/cdk-core' | ||
import {R, z, zEnvVars, zFlattenForEnv} from '@usevenice/util' | ||
|
||
import type {PROVIDERS} from './providers' | ||
import {DOCUMENTED_PROVIDERS} from './providers' | ||
|
||
// MARK: - Env vars | ||
|
||
export const zCommonEnv = zEnvVars({ | ||
NEXT_PUBLIC_SUPABASE_URL: z.string(), | ||
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(), | ||
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(), | ||
NEXT_PUBLIC_POSTHOG_WRITEKEY: z.string().optional(), | ||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string(), | ||
NEXT_PUBLIC_CLERK_SUPABASE_JWT_TEMPLATE_NAME: z.string().default('supabase'), | ||
|
||
// Deprecated | ||
// TODO: Deprecate me? prefix with NEXT_PUBLIC please | ||
DEFAULT_CONNECT_ENV: zEnvName.default('sandbox'), | ||
|
||
// TODO: Make use of me... prefix with NEXT_PUBLIC please | ||
NODE_ENV: z | ||
.string() | ||
.optional() | ||
.default(process.env['NEXT_PUBLIC_NODE_ENV'] ?? process.env.NODE_ENV), | ||
}) | ||
|
||
export const zBackendEnv = zEnvVars({ | ||
POSTGRES_OR_WEBHOOK_URL: z.string().describe(` | ||
Pass a valid postgres(ql):// url for stateful mode. Will be used Primary database used for metadata and user data storage | ||
Pass a valid http(s):// url for stateless mode. Sync data and metadata be sent to provided URL and you are responsible for your own persistence`), | ||
JWT_SECRET_OR_PUBLIC_KEY: z | ||
.string() | ||
.trim() | ||
.describe('Used for validating authenticity of accessToken'), | ||
|
||
CLERK_SECRET_KEY: z.string(), | ||
|
||
SENTRY_CRON_MONITOR_ID: z | ||
.string() | ||
.optional() | ||
.describe('Used to monitor the schedule syncs cron job'), | ||
}) | ||
|
||
/** We would prefer to use `.` but vercel env var name can only be number, letter and underscore... */ | ||
const separator = '__' | ||
const getPrefix = (name: string) => makeId('int', name, '') | ||
|
||
// Should this be all providers or only dcoumented ones? | ||
|
||
export const zFlatConfigByProvider = R.mapToObj(DOCUMENTED_PROVIDERS, (p) => [ | ||
p.name, | ||
zFlattenForEnv(p.def.integrationConfig ?? z.unknown(), { | ||
prefix: getPrefix(p.name), | ||
separator, | ||
}), | ||
]) | ||
|
||
export const zIntegrationEnv = zEnvVars( | ||
R.pipe( | ||
zFlatConfigByProvider, | ||
R.values, | ||
R.map((schema) => schema.innerType().shape), | ||
R.mergeAll, | ||
) as {}, | ||
) | ||
|
||
export const zAllEnv = zCommonEnv.merge(zBackendEnv).merge(zIntegrationEnv) | ||
|
||
// MARK: - Parsing integration configs | ||
|
||
/** | ||
* Input env must be raw, so means most likely we are parsing the flatConfig input twice | ||
* for the moment unfortunately... But we need this to support transforms in flatConfig | ||
*/ | ||
export function parseIntConfigsFromRawEnv( | ||
env: Record<string, string | undefined> = process.env, | ||
) { | ||
return R.pipe( | ||
R.mapValues(zFlatConfigByProvider, (zFlatConfig, name) => { | ||
const subEnv = R.pipe( | ||
R.pickBy(env, (_v, k) => k.startsWith(getPrefix(name))), | ||
(e) => (R.keys(e).length ? e : undefined), // To get .optional() to work | ||
) | ||
try { | ||
return zFlatConfig.optional().parse(subEnv) | ||
} catch (err) { | ||
if (err instanceof z.ZodError && err.issues[0]) { | ||
const issue = err.issues[0] | ||
// const msg = issue.code === 'invalid_type' && issue.message === 'Required' ? `` | ||
// console.log('subEnv', subEnv, issue) | ||
throw new Error( | ||
`Failed to configure "${name}" provider due to invalid env var "${issue.path.join( | ||
separator, | ||
)}": ${issue.message} [${issue.code}]`, | ||
) | ||
} | ||
} | ||
}), | ||
(configMap) => R.pickBy(configMap, (val) => val !== undefined), | ||
) as { | ||
[k in (typeof PROVIDERS)[number]['name']]?: Extract< | ||
(typeof PROVIDERS)[number], | ||
{name: k} | ||
>['def']['_types']['integrationConfig'] | ||
} | ||
} | ||
import {createEnv} from '@t3-oss/env-nextjs' | ||
import {z} from 'zod' | ||
|
||
export const envConfig = { | ||
server: { | ||
POSTGRES_OR_WEBHOOK_URL: z.string().describe(` | ||
Pass a valid postgres(ql):// url for stateful mode. Will be used Primary database used for metadata and user data storage | ||
Pass a valid http(s):// url for stateless mode. Sync data and metadata be sent to provided URL and you are responsible for your own persistence`), | ||
JWT_SECRET_OR_PUBLIC_KEY: z | ||
.string() | ||
.trim() | ||
.describe('Used for validating authenticity of accessToken'), | ||
|
||
CLERK_SECRET_KEY: z.string(), | ||
SENTRY_CRON_MONITOR_ID: z | ||
.string() | ||
.optional() | ||
.describe('Used to monitor the schedule syncs cron job'), | ||
|
||
INNGEST_EVENT_KEY: z.string(), | ||
INNGEST_SIGNING_KEY: z.string(), | ||
}, | ||
client: { | ||
NEXT_PUBLIC_SUPABASE_URL: z.string(), | ||
NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string(), | ||
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(), | ||
NEXT_PUBLIC_SENTRY_ORG: z.string().optional(), | ||
NEXT_PUBLIC_POSTHOG_WRITEKEY: z.string().optional(), | ||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: z.string(), | ||
NEXT_PUBLIC_CLERK_SUPABASE_JWT_TEMPLATE_NAME: z | ||
.string() | ||
.default('supabase'), | ||
}, | ||
runtimeEnv: { | ||
CLERK_SECRET_KEY: process.env['CLERK_SECRET_KEY'], | ||
JWT_SECRET_OR_PUBLIC_KEY: process.env['JWT_SECRET_OR_PUBLIC_KEY'], | ||
POSTGRES_OR_WEBHOOK_URL: process.env['POSTGRES_OR_WEBHOOK_URL'], | ||
SENTRY_CRON_MONITOR_ID: process.env['SENTRY_CRON_MONITOR_ID'], | ||
NEXT_PUBLIC_SENTRY_ORG: process.env['NEXT_PUBLIC_SENTRY_ORG'], | ||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: | ||
process.env['NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY'], | ||
NEXT_PUBLIC_CLERK_SUPABASE_JWT_TEMPLATE_NAME: | ||
process.env['NEXT_PUBLIC_CLERK_SUPABASE_JWT_TEMPLATE_NAME'], | ||
NEXT_PUBLIC_POSTHOG_WRITEKEY: process.env['NEXT_PUBLIC_POSTHOG_WRITEKEY'], | ||
NEXT_PUBLIC_SENTRY_DSN: process.env['NEXT_PUBLIC_SENTRY_DSN'], | ||
NEXT_PUBLIC_SUPABASE_ANON_KEY: process.env['NEXT_PUBLIC_SUPABASE_ANON_KEY'], | ||
NEXT_PUBLIC_SUPABASE_URL: process.env['NEXT_PUBLIC_SUPABASE_URL'], | ||
INNGEST_EVENT_KEY: process.env['INNGEST_EVENT_KEY'], | ||
INNGEST_SIGNING_KEY: process.env['INNGEST_SIGNING_KEY'], | ||
}, | ||
onInvalidAccess: (variable: string) => { | ||
throw new Error( | ||
`❌ Attempted to access server-side environment variable ${variable} on the client`, | ||
) | ||
}, | ||
skipValidation: !!process.env['SKIP_ENV_VALIDATION'], | ||
} satisfies Parameters<typeof createEnv>[0] | ||
|
||
export const env = createEnv(envConfig) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/** @deprecated. We no longer initialize integration from ENVs, but maybe in clis still? */ | ||
import {makeId} from '@usevenice/cdk-core' | ||
import {R, z, zEnvVars, zFlattenForEnv} from '@usevenice/util' | ||
|
||
import type {PROVIDERS} from './providers' | ||
import {DOCUMENTED_PROVIDERS} from './providers' | ||
|
||
/** We would prefer to use `.` but vercel env var name can only be number, letter and underscore... */ | ||
const separator = '__' | ||
const getPrefix = (name: string) => makeId('int', name, '') | ||
|
||
// Should this be all providers or only dcoumented ones? | ||
|
||
export const zFlatConfigByProvider = R.mapToObj(DOCUMENTED_PROVIDERS, (p) => [ | ||
p.name, | ||
zFlattenForEnv(p.def.integrationConfig ?? z.unknown(), { | ||
prefix: getPrefix(p.name), | ||
separator, | ||
}), | ||
]) | ||
|
||
export const zIntegrationEnv = zEnvVars( | ||
R.pipe( | ||
zFlatConfigByProvider, | ||
R.values, | ||
R.map((schema) => schema.innerType().shape), | ||
R.mergeAll, | ||
) as {}, | ||
) | ||
|
||
// MARK: - Parsing integration configs | ||
|
||
/** | ||
* Input env must be raw, so means most likely we are parsing the flatConfig input twice | ||
* for the moment unfortunately... But we need this to support transforms in flatConfig | ||
*/ | ||
export function parseIntConfigsFromRawEnv( | ||
env: Record<string, string | undefined> = process.env, | ||
) { | ||
return R.pipe( | ||
R.mapValues(zFlatConfigByProvider, (zFlatConfig, name) => { | ||
const subEnv = R.pipe( | ||
R.pickBy(env, (_v, k) => k.startsWith(getPrefix(name))), | ||
(e) => (R.keys(e).length ? e : undefined), // To get .optional() to work | ||
) | ||
try { | ||
return zFlatConfig.optional().parse(subEnv) | ||
} catch (err) { | ||
if (err instanceof z.ZodError && err.issues[0]) { | ||
const issue = err.issues[0] | ||
// const msg = issue.code === 'invalid_type' && issue.message === 'Required' ? `` | ||
// console.log('subEnv', subEnv, issue) | ||
throw new Error( | ||
`Failed to configure "${name}" provider due to invalid env var "${issue.path.join( | ||
separator, | ||
)}": ${issue.message} [${issue.code}]`, | ||
) | ||
} | ||
} | ||
}), | ||
(configMap) => R.pickBy(configMap, (val) => val !== undefined), | ||
) as { | ||
[k in (typeof PROVIDERS)[number]['name']]?: Extract< | ||
(typeof PROVIDERS)[number], | ||
{name: k} | ||
>['def']['_types']['integrationConfig'] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.