🧑💻 Improve env variables type safety and management (#718)
Closes #679
This commit is contained in:
@@ -3,14 +3,14 @@ import { GiphyFetch } from '@giphy/js-fetch-api'
|
||||
import { Grid } from '@giphy/react-components'
|
||||
import { GiphyLogo } from '../logos/GiphyLogo'
|
||||
import React, { useState } from 'react'
|
||||
import { env, isEmpty } from '@typebot.io/lib'
|
||||
import { TextInput } from '../inputs'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
type GiphySearchFormProps = {
|
||||
onSubmit: (url: string) => void
|
||||
}
|
||||
|
||||
const giphyFetch = new GiphyFetch(env('GIPHY_API_KEY') as string)
|
||||
const giphyFetch = new GiphyFetch(env.NEXT_PUBLIC_GIPHY_API_KEY ?? '')
|
||||
|
||||
export const GiphyPicker = ({ onSubmit }: GiphySearchFormProps) => {
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
@@ -21,7 +21,7 @@ export const GiphyPicker = ({ onSubmit }: GiphySearchFormProps) => {
|
||||
const fetchGifsTrending = (offset: number) =>
|
||||
giphyFetch.trending({ offset, limit: 10 })
|
||||
|
||||
return isEmpty(env('GIPHY_API_KEY')) ? (
|
||||
return !env.NEXT_PUBLIC_GIPHY_API_KEY ? (
|
||||
<Text>NEXT_PUBLIC_GIPHY_API_KEY is missing in environment</Text>
|
||||
) : (
|
||||
<Stack spacing={4} pt="2">
|
||||
|
||||
@@ -14,16 +14,17 @@ import {
|
||||
Text,
|
||||
useColorModeValue,
|
||||
} from '@chakra-ui/react'
|
||||
import { env, isDefined, isEmpty } from '@typebot.io/lib'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { createApi } from 'unsplash-js'
|
||||
import { Basic as UnsplashImage } from 'unsplash-js/dist/methods/photos/types'
|
||||
import { TextInput } from '../inputs'
|
||||
import { UnsplashLogo } from '../logos/UnsplashLogo'
|
||||
import { TextLink } from '../TextLink'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const api = createApi({
|
||||
accessKey: env('UNSPLASH_ACCESS_KEY') ?? '',
|
||||
accessKey: env.NEXT_PUBLIC_UNSPLASH_ACCESS_KEY ?? '',
|
||||
})
|
||||
|
||||
type Props = {
|
||||
@@ -124,7 +125,7 @@ export const UnsplashPicker = ({ imageSize, onImageSelect }: Props) => {
|
||||
searchRandomImages()
|
||||
}, [])
|
||||
|
||||
if (isEmpty(env('UNSPLASH_ACCESS_KEY')))
|
||||
if (!env.NEXT_PUBLIC_UNSPLASH_ACCESS_KEY)
|
||||
return (
|
||||
<Text>NEXT_PUBLIC_UNSPLASH_ACCESS_KEY is missing in environment</Text>
|
||||
)
|
||||
@@ -143,9 +144,7 @@ export const UnsplashPicker = ({ imageSize, onImageSelect }: Props) => {
|
||||
/>
|
||||
<Link
|
||||
isExternal
|
||||
href={`https://unsplash.com/?utm_source=${env(
|
||||
'UNSPLASH_APP_NAME'
|
||||
)}&utm_medium=referral`}
|
||||
href={`https://unsplash.com/?utm_source=${env.NEXT_PUBLIC_UNSPLASH_APP_NAME}&utm_medium=referral`}
|
||||
>
|
||||
<UnsplashLogo width="80px" fill={unsplashLogoFillColor} />
|
||||
</Link>
|
||||
@@ -224,9 +223,7 @@ const UnsplashImage = ({ image, onClick }: UnsplashImageProps) => {
|
||||
<TextLink
|
||||
fontSize="xs"
|
||||
isExternal
|
||||
href={`https://unsplash.com/@${user.username}?utm_source=${env(
|
||||
'UNSPLASH_APP_NAME'
|
||||
)}&utm_medium=referral`}
|
||||
href={`https://unsplash.com/@${user.username}?utm_source=${env.NEXT_PUBLIC_UNSPLASH_APP_NAME}&utm_medium=referral`}
|
||||
noOfLines={1}
|
||||
color="white"
|
||||
>
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { useState, useRef, useEffect, ReactNode } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { env, isDefined } from '@typebot.io/lib'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { useOutsideClick } from '@/hooks/useOutsideClick'
|
||||
import { useParentModal } from '@/features/graph/providers/ParentModalProvider'
|
||||
import { VariablesButton } from '@/features/variables/components/VariablesButton'
|
||||
@@ -21,6 +21,7 @@ import { Variable } from '@typebot.io/schemas'
|
||||
import { injectVariableInText } from '@/features/variables/helpers/injectVariableInTextInput'
|
||||
import { focusInput } from '@/helpers/focusInput'
|
||||
import { MoreInfoTooltip } from '../MoreInfoTooltip'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
type Props = {
|
||||
items: string[]
|
||||
@@ -57,7 +58,7 @@ export const AutocompleteInput = ({
|
||||
|
||||
const onChange = useDebouncedCallback(
|
||||
_onChange,
|
||||
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||
env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useEffect, useRef, useState } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { VariablesButton } from '@/features/variables/components/VariablesButton'
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
import CodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror'
|
||||
import { tokyoNight } from '@uiw/codemirror-theme-tokyo-night'
|
||||
import { githubLight } from '@uiw/codemirror-theme-github'
|
||||
@@ -53,7 +53,7 @@ export const CodeEditor = ({
|
||||
_setValue(value)
|
||||
onChange && onChange(value)
|
||||
},
|
||||
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||
env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
)
|
||||
|
||||
const handleVariableSelected = (variable?: Pick<Variable, 'id' | 'name'>) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
import { Variable, VariableString } from '@typebot.io/schemas'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { MoreInfoTooltip } from '../MoreInfoTooltip'
|
||||
|
||||
type Value<HasVariable> = HasVariable extends true | undefined
|
||||
@@ -47,7 +47,7 @@ export const NumberInput = <HasVariable extends boolean>({
|
||||
|
||||
const onValueChangeDebounced = useDebouncedCallback(
|
||||
onValueChange,
|
||||
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||
env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
)
|
||||
|
||||
useEffect(
|
||||
|
||||
@@ -19,7 +19,7 @@ import React, {
|
||||
useState,
|
||||
} from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { MoreInfoTooltip } from '../MoreInfoTooltip'
|
||||
|
||||
export type TextInputProps = {
|
||||
@@ -69,7 +69,7 @@ export const TextInput = forwardRef(function TextInput(
|
||||
const onChange = useDebouncedCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
_onChange ?? (() => {}),
|
||||
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||
env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { Variable } from '@typebot.io/schemas'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { MoreInfoTooltip } from '../MoreInfoTooltip'
|
||||
|
||||
type Props = {
|
||||
@@ -46,7 +46,7 @@ export const Textarea = ({
|
||||
)
|
||||
const onChange = useDebouncedCallback(
|
||||
_onChange,
|
||||
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||
env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useSession } from 'next-auth/react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { createContext, ReactNode, useEffect, useState } from 'react'
|
||||
import { env, isDefined, isNotDefined } from '@typebot.io/lib'
|
||||
import { isDefined, isNotDefined } from '@typebot.io/lib'
|
||||
import { User } from '@typebot.io/prisma'
|
||||
import { setUser as setSentryUser } from '@sentry/nextjs'
|
||||
import { useToast } from '@/hooks/useToast'
|
||||
import { updateUserQuery } from './queries/updateUserQuery'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const userContext = createContext<{
|
||||
user?: User
|
||||
@@ -66,7 +67,7 @@ export const UserProvider = ({ children }: { children: ReactNode }) => {
|
||||
if (error) showToast({ title: error.name, description: error.message })
|
||||
await refreshUser()
|
||||
},
|
||||
env('E2E_TEST') === 'true' ? 0 : debounceTimeout
|
||||
env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getTestAsset } from '@/test/utils/playwright'
|
||||
import test, { expect } from '@playwright/test'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { userId } from '@typebot.io/lib/playwright/databaseSetup'
|
||||
|
||||
test.describe.configure({ mode: 'parallel' })
|
||||
@@ -15,9 +16,9 @@ test('should display user info properly', async ({ page }) => {
|
||||
await expect(page.locator('img >> nth=1')).toHaveAttribute(
|
||||
'src',
|
||||
new RegExp(
|
||||
`${process.env.S3_ENDPOINT}${
|
||||
process.env.S3_PORT ? `:${process.env.S3_PORT}` : ''
|
||||
}/${process.env.S3_BUCKET}/public/users/${userId}/avatar`,
|
||||
`${env.S3_ENDPOINT}${env.S3_PORT ? `:${env.S3_PORT}` : ''}/${
|
||||
env.S3_BUCKET
|
||||
}/public/users/${userId}/avatar`,
|
||||
'gm'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { fetcher } from '@/helpers/fetcher'
|
||||
import useSWR from 'swr'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { ApiTokenFromServer } from '../types'
|
||||
|
||||
type ServerResponse = {
|
||||
@@ -18,7 +18,7 @@ export const useApiTokens = ({
|
||||
userId ? `/api/users/${userId}/api-tokens` : null,
|
||||
fetcher,
|
||||
{
|
||||
dedupingInterval: env('E2E_TEST') === 'true' ? 0 : undefined,
|
||||
dedupingInterval: env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined,
|
||||
}
|
||||
)
|
||||
if (error) onError(error)
|
||||
|
||||
@@ -14,6 +14,7 @@ import { convertInvitationsToCollaborations } from '@/features/auth/helpers/conv
|
||||
import { getNewUserInvitations } from '@/features/auth/helpers/getNewUserInvitations'
|
||||
import { joinWorkspaces } from '@/features/auth/helpers/joinWorkspaces'
|
||||
import { parseWorkspaceDefaultPlan } from '@/features/workspace/helpers/parseWorkspaceDefaultPlan'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export function customAdapter(p: PrismaClient): Adapter {
|
||||
return {
|
||||
@@ -26,8 +27,8 @@ export function customAdapter(p: PrismaClient): Adapter {
|
||||
user.email
|
||||
)
|
||||
if (
|
||||
process.env.DISABLE_SIGNUP === 'true' &&
|
||||
process.env.ADMIN_EMAIL !== user.email &&
|
||||
env.DISABLE_SIGNUP &&
|
||||
env.ADMIN_EMAIL !== user.email &&
|
||||
invitations.length === 0 &&
|
||||
workspaceInvitations.length === 0
|
||||
)
|
||||
|
||||
@@ -13,8 +13,8 @@ import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import confetti from 'canvas-confetti'
|
||||
import { useUser } from '@/features/account/hooks/useUser'
|
||||
import { env, isEmpty } from '@typebot.io/lib'
|
||||
import { useI18n } from '@/locales'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const totalSteps = 5
|
||||
|
||||
@@ -37,7 +37,7 @@ export const OnboardingPage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (!user?.createdAt) return
|
||||
if (isNewUser === false || isEmpty(env('ONBOARDING_TYPEBOT_ID')))
|
||||
if (isNewUser === false || !env.NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID)
|
||||
replace('/typebots')
|
||||
}, [isNewUser, replace, user?.createdAt])
|
||||
|
||||
@@ -90,7 +90,7 @@ export const OnboardingPage = () => {
|
||||
<Dots currentStep={currentStep} pos="fixed" top="9" />
|
||||
<Flex w="full" maxW="800px" h="full" maxH="70vh" rounded="lg">
|
||||
<Standard
|
||||
typebot={env('ONBOARDING_TYPEBOT_ID')}
|
||||
typebot={env.NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID}
|
||||
style={{ borderRadius: '1rem' }}
|
||||
prefilledVariables={{ Name: user?.name, Email: user?.email }}
|
||||
onEnd={() => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { User } from '@typebot.io/prisma'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { getServerSession } from 'next-auth'
|
||||
import { mockedUser } from '../mockedUser'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const getAuthenticatedUser = async (
|
||||
req: NextApiRequest,
|
||||
@@ -13,12 +13,11 @@ export const getAuthenticatedUser = async (
|
||||
): Promise<User | undefined> => {
|
||||
const bearerToken = extractBearerToken(req)
|
||||
if (bearerToken) return authenticateByToken(bearerToken)
|
||||
const user =
|
||||
env('E2E_TEST') === 'true'
|
||||
? mockedUser
|
||||
: ((await getServerSession(req, res, authOptions))?.user as
|
||||
| User
|
||||
| undefined)
|
||||
const user = env.NEXT_PUBLIC_E2E_TEST
|
||||
? mockedUser
|
||||
: ((await getServerSession(req, res, authOptions))?.user as
|
||||
| User
|
||||
| undefined)
|
||||
if (!user || !('id' in user)) return
|
||||
setUser({ id: user.id })
|
||||
return user
|
||||
|
||||
@@ -6,6 +6,7 @@ import Stripe from 'stripe'
|
||||
import { z } from 'zod'
|
||||
import { parseSubscriptionItems } from '../helpers/parseSubscriptionItems'
|
||||
import { isAdminWriteWorkspaceForbidden } from '@/features/workspace/helpers/isAdminWriteWorkspaceForbidden'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const createCheckoutSession = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -57,7 +58,7 @@ export const createCheckoutSession = authenticatedProcedure
|
||||
},
|
||||
ctx: { user },
|
||||
}) => {
|
||||
if (!process.env.STRIPE_SECRET_KEY)
|
||||
if (!env.STRIPE_SECRET_KEY)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Stripe environment variables are missing',
|
||||
@@ -82,14 +83,13 @@ export const createCheckoutSession = authenticatedProcedure
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Workspace not found',
|
||||
})
|
||||
|
||||
if (workspace.stripeId)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Customer already exists, use updateSubscription endpoint.',
|
||||
})
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Plan } from '@typebot.io/prisma'
|
||||
import Stripe from 'stripe'
|
||||
import { z } from 'zod'
|
||||
import { isAdminWriteWorkspaceForbidden } from '@/features/workspace/helpers/isAdminWriteWorkspaceForbidden'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const createCustomCheckoutSession = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -31,7 +32,7 @@ export const createCustomCheckoutSession = authenticatedProcedure
|
||||
)
|
||||
.mutation(
|
||||
async ({ input: { email, workspaceId, returnUrl }, ctx: { user } }) => {
|
||||
if (!process.env.STRIPE_SECRET_KEY)
|
||||
if (!env.STRIPE_SECRET_KEY)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Stripe environment variables are missing',
|
||||
@@ -61,7 +62,7 @@ export const createCustomCheckoutSession = authenticatedProcedure
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Custom plan not found',
|
||||
})
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TRPCError } from '@trpc/server'
|
||||
import Stripe from 'stripe'
|
||||
import { z } from 'zod'
|
||||
import { isAdminWriteWorkspaceForbidden } from '@/features/workspace/helpers/isAdminWriteWorkspaceForbidden'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const getBillingPortalUrl = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -26,7 +27,7 @@ export const getBillingPortalUrl = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { workspaceId }, ctx: { user } }) => {
|
||||
if (!process.env.STRIPE_SECRET_KEY)
|
||||
if (!env.STRIPE_SECRET_KEY)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'STRIPE_SECRET_KEY var is missing',
|
||||
@@ -50,12 +51,12 @@ export const getBillingPortalUrl = authenticatedProcedure
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Workspace not found',
|
||||
})
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
const portalSession = await stripe.billingPortal.sessions.create({
|
||||
customer: workspace.stripeId,
|
||||
return_url: `${process.env.NEXTAUTH_URL}/typebots`,
|
||||
return_url: `${env.NEXTAUTH_URL}/typebots`,
|
||||
})
|
||||
return {
|
||||
billingPortalUrl: portalSession.url,
|
||||
|
||||
@@ -4,8 +4,9 @@ import { TRPCError } from '@trpc/server'
|
||||
import Stripe from 'stripe'
|
||||
import { z } from 'zod'
|
||||
import { subscriptionSchema } from '@typebot.io/schemas/features/billing/subscription'
|
||||
import { priceIds } from '@typebot.io/lib/pricing'
|
||||
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
||||
import { priceIds } from '@typebot.io/lib/api/pricing'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const getSubscription = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -28,7 +29,7 @@ export const getSubscription = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { workspaceId }, ctx: { user } }) => {
|
||||
if (!process.env.STRIPE_SECRET_KEY)
|
||||
if (!env.STRIPE_SECRET_KEY)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Stripe environment variables are missing',
|
||||
@@ -55,7 +56,7 @@ export const getSubscription = authenticatedProcedure
|
||||
return {
|
||||
subscription: null,
|
||||
}
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
const subscriptions = await stripe.subscriptions.list({
|
||||
|
||||
@@ -6,6 +6,7 @@ import { isDefined } from '@typebot.io/lib'
|
||||
import { z } from 'zod'
|
||||
import { invoiceSchema } from '@typebot.io/schemas/features/billing/invoice'
|
||||
import { isAdminWriteWorkspaceForbidden } from '@/features/workspace/helpers/isAdminWriteWorkspaceForbidden'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const listInvoices = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -28,7 +29,7 @@ export const listInvoices = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { workspaceId }, ctx: { user } }) => {
|
||||
if (!process.env.STRIPE_SECRET_KEY)
|
||||
if (!env.STRIPE_SECRET_KEY)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'STRIPE_SECRET_KEY var is missing',
|
||||
@@ -52,7 +53,7 @@ export const listInvoices = authenticatedProcedure
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Workspace not found',
|
||||
})
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
const invoices = await stripe.invoices.list({
|
||||
|
||||
@@ -7,15 +7,13 @@ import { workspaceSchema } from '@typebot.io/schemas'
|
||||
import Stripe from 'stripe'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
getChatsLimit,
|
||||
getStorageLimit,
|
||||
priceIds,
|
||||
} from '@typebot.io/lib/pricing'
|
||||
import { getChatsLimit, getStorageLimit } from '@typebot.io/lib/pricing'
|
||||
import { chatPriceIds, storagePriceIds } from './getSubscription'
|
||||
import { createCheckoutSessionUrl } from './createCheckoutSession'
|
||||
import { isAdminWriteWorkspaceForbidden } from '@/features/workspace/helpers/isAdminWriteWorkspaceForbidden'
|
||||
import { getUsage } from '@typebot.io/lib/api/getUsage'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { priceIds } from '@typebot.io/lib/api/pricing'
|
||||
|
||||
export const updateSubscription = authenticatedProcedure
|
||||
.meta({
|
||||
@@ -57,7 +55,7 @@ export const updateSubscription = authenticatedProcedure
|
||||
},
|
||||
ctx: { user },
|
||||
}) => {
|
||||
if (!process.env.STRIPE_SECRET_KEY)
|
||||
if (!env.STRIPE_SECRET_KEY)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: 'Stripe environment variables are missing',
|
||||
@@ -85,7 +83,7 @@ export const updateSubscription = authenticatedProcedure
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Workspace not found',
|
||||
})
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
const { data } = await stripe.subscriptions.list({
|
||||
@@ -95,10 +93,9 @@ export const updateSubscription = authenticatedProcedure
|
||||
})
|
||||
const subscription = data[0] as Stripe.Subscription | undefined
|
||||
const currentPlanItemId = subscription?.items.data.find((item) =>
|
||||
[
|
||||
process.env.STRIPE_STARTER_PRODUCT_ID,
|
||||
process.env.STRIPE_PRO_PRODUCT_ID,
|
||||
].includes(item.price.product.toString())
|
||||
[env.STRIPE_STARTER_PRODUCT_ID, env.STRIPE_PRO_PRODUCT_ID].includes(
|
||||
item.price.product.toString()
|
||||
)
|
||||
)?.id
|
||||
const currentAdditionalChatsItemId = subscription?.items.data.find(
|
||||
(item) => chatPriceIds.includes(item.price.id)
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
deleteWorkspaces,
|
||||
injectFakeResults,
|
||||
} from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const usageWorkspaceId = createId()
|
||||
const usageTypebotId = createId()
|
||||
@@ -147,7 +148,7 @@ test('plan changes should work', async ({ page }) => {
|
||||
planChangeWorkspaceId,
|
||||
[
|
||||
{
|
||||
price: process.env.STRIPE_STARTER_MONTHLY_PRICE_ID,
|
||||
price: env.STRIPE_STARTER_MONTHLY_PRICE_ID,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import {
|
||||
getChatsLimit,
|
||||
getStorageLimit,
|
||||
priceIds,
|
||||
} from '@typebot.io/lib/pricing'
|
||||
import { getChatsLimit, getStorageLimit } from '@typebot.io/lib/pricing'
|
||||
import { priceIds } from '@typebot.io/lib/api/pricing'
|
||||
|
||||
export const parseSubscriptionItems = (
|
||||
plan: 'STARTER' | 'PRO',
|
||||
|
||||
@@ -4,6 +4,7 @@ import { parseDefaultGroupWithBlock } from '@typebot.io/lib/playwright/databaseH
|
||||
import { defaultPaymentInputOptions, InputBlockType } from '@typebot.io/schemas'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { stripePaymentForm } from '@/test/utils/selectorUtils'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
test.describe('Payment input block', () => {
|
||||
test('Can configure Stripe account', async ({ page }) => {
|
||||
@@ -23,21 +24,15 @@ test.describe('Payment input block', () => {
|
||||
await page.getByRole('button', { name: 'Select an account' }).click()
|
||||
await page.click('text=Connect new')
|
||||
await page.fill('[placeholder="Typebot"]', 'My Stripe Account')
|
||||
await page.fill(
|
||||
'[placeholder="sk_test_..."]',
|
||||
process.env.STRIPE_SECRET_KEY ?? ''
|
||||
)
|
||||
await page.fill(
|
||||
'[placeholder="sk_live_..."]',
|
||||
process.env.STRIPE_SECRET_KEY ?? ''
|
||||
)
|
||||
await page.fill('[placeholder="sk_test_..."]', env.STRIPE_SECRET_KEY ?? '')
|
||||
await page.fill('[placeholder="sk_live_..."]', env.STRIPE_SECRET_KEY ?? '')
|
||||
await page.fill(
|
||||
'[placeholder="pk_test_..."]',
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY ?? ''
|
||||
env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY ?? ''
|
||||
)
|
||||
await page.fill(
|
||||
'[placeholder="pk_live_..."]',
|
||||
process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY ?? ''
|
||||
env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY ?? ''
|
||||
)
|
||||
await expect(page.locator('button >> text="Connect"')).toBeEnabled()
|
||||
await page.click('button >> text="Connect"')
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { CodeEditor } from '@/components/inputs/CodeEditor'
|
||||
import { SendEmailOptions, Variable } from '@typebot.io/schemas'
|
||||
import React from 'react'
|
||||
import { env, isNotEmpty } from '@typebot.io/lib'
|
||||
import { isNotEmpty } from '@typebot.io/lib'
|
||||
import { SmtpConfigModal } from './SmtpConfigModal'
|
||||
import { SwitchWithLabel } from '@/components/inputs/SwitchWithLabel'
|
||||
import { VariableSearchInput } from '@/components/inputs/VariableSearchInput'
|
||||
@@ -18,6 +18,7 @@ import { CredentialsDropdown } from '@/features/credentials/components/Credentia
|
||||
import { TextInput, Textarea } from '@/components/inputs'
|
||||
import { useWorkspace } from '@/features/workspace/WorkspaceProvider'
|
||||
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
type Props = {
|
||||
options: SendEmailOptions
|
||||
@@ -117,9 +118,9 @@ export const SendEmailSettings = ({ options, onOptionsChange }: Props) => {
|
||||
currentCredentialsId={options.credentialsId}
|
||||
onCredentialsSelect={handleCredentialsSelect}
|
||||
onCreateNewClick={onOpen}
|
||||
defaultCredentialLabel={env('SMTP_FROM')
|
||||
?.match(/<(.*)>/)
|
||||
?.pop()}
|
||||
defaultCredentialLabel={env.NEXT_PUBLIC_SMTP_FROM?.match(
|
||||
/<(.*)>/
|
||||
)?.pop()}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -2,17 +2,18 @@ import test, { expect } from '@playwright/test'
|
||||
import { importTypebotInDatabase } from '@typebot.io/lib/playwright/databaseActions'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { getTestAsset } from '@/test/utils/playwright'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const typebotId = createId()
|
||||
|
||||
test.describe('Send email block', () => {
|
||||
test('its configuration should work', async ({ page }) => {
|
||||
if (
|
||||
!process.env.SMTP_USERNAME ||
|
||||
!process.env.SMTP_PORT ||
|
||||
!process.env.SMTP_HOST ||
|
||||
!process.env.SMTP_PASSWORD ||
|
||||
!process.env.NEXT_PUBLIC_SMTP_FROM
|
||||
!env.SMTP_USERNAME ||
|
||||
!env.SMTP_PORT ||
|
||||
!env.SMTP_HOST ||
|
||||
!env.SMTP_PASSWORD ||
|
||||
!env.NEXT_PUBLIC_SMTP_FROM
|
||||
)
|
||||
throw new Error('SMTP_ env vars are missing')
|
||||
await importTypebotInDatabase(
|
||||
@@ -30,21 +31,18 @@ test.describe('Send email block', () => {
|
||||
await expect(createButton).toBeDisabled()
|
||||
await page.fill(
|
||||
'[placeholder="notifications@provider.com"]',
|
||||
process.env.SMTP_USERNAME
|
||||
env.SMTP_USERNAME
|
||||
)
|
||||
await page.fill('[placeholder="John Smith"]', 'John Smith')
|
||||
await page.fill('[placeholder="mail.provider.com"]', process.env.SMTP_HOST)
|
||||
await page.fill(
|
||||
'[placeholder="user@provider.com"]',
|
||||
process.env.SMTP_USERNAME
|
||||
)
|
||||
await page.fill('[type="password"]', process.env.SMTP_PASSWORD)
|
||||
await page.fill('input[role="spinbutton"]', process.env.SMTP_PORT)
|
||||
await page.fill('[placeholder="mail.provider.com"]', env.SMTP_HOST)
|
||||
await page.fill('[placeholder="user@provider.com"]', env.SMTP_USERNAME)
|
||||
await page.fill('[type="password"]', env.SMTP_PASSWORD)
|
||||
await page.fill('input[role="spinbutton"]', env.SMTP_PORT.toString())
|
||||
await expect(createButton).toBeEnabled()
|
||||
await createButton.click()
|
||||
|
||||
await expect(
|
||||
page.locator(`button >> text=${process.env.SMTP_USERNAME}`)
|
||||
page.locator(`button >> text=${env.SMTP_USERNAME}`)
|
||||
).toBeVisible()
|
||||
|
||||
await page.fill(
|
||||
|
||||
@@ -7,6 +7,7 @@ import { HttpMethod } from '@typebot.io/schemas/features/blocks/integrations/web
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
import { getTestAsset } from '@/test/utils/playwright'
|
||||
import { apiToken } from '@typebot.io/lib/playwright/databaseSetup'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
test.describe('Builder', () => {
|
||||
test('easy configuration should work', async ({ page }) => {
|
||||
@@ -22,7 +23,7 @@ test.describe('Builder', () => {
|
||||
await page.click('text=Configure...')
|
||||
await page.fill(
|
||||
'input[placeholder="Paste webhook URL..."]',
|
||||
`${process.env.NEXTAUTH_URL}/api/mock/webhook-easy-config`
|
||||
`${env.NEXTAUTH_URL}/api/mock/webhook-easy-config`
|
||||
)
|
||||
await page.click('text=Test the request')
|
||||
await expect(page.locator('div[role="textbox"] >> nth=-1')).toContainText(
|
||||
@@ -45,7 +46,7 @@ test.describe('Builder', () => {
|
||||
await page.click('text=Configure...')
|
||||
await page.fill(
|
||||
'input[placeholder="Paste webhook URL..."]',
|
||||
`${process.env.NEXTAUTH_URL}/api/mock/webhook`
|
||||
`${env.NEXTAUTH_URL}/api/mock/webhook`
|
||||
)
|
||||
await page.click('text=Advanced configuration')
|
||||
await page.getByRole('button', { name: 'GET' }).click()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { fetcher } from '@/helpers/fetcher'
|
||||
import { Invitation } from '@typebot.io/prisma'
|
||||
import useSWR from 'swr'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const useInvitations = ({
|
||||
typebotId,
|
||||
@@ -14,7 +14,7 @@ export const useInvitations = ({
|
||||
typebotId ? `/api/typebots/${typebotId}/invitations` : null,
|
||||
fetcher,
|
||||
{
|
||||
dedupingInterval: env('E2E_TEST') === 'true' ? 0 : undefined,
|
||||
dedupingInterval: env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined,
|
||||
}
|
||||
)
|
||||
if (error) onError(error)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { publicProcedure } from '@/helpers/server/trpc'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const getAppVersionProcedure = publicProcedure.query(async () => {
|
||||
return { commitSha: process.env.VERCEL_GIT_COMMIT_SHA }
|
||||
return { commitSha: env.VERCEL_GIT_COMMIT_SHA }
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import { fetcher } from '@/helpers/fetcher'
|
||||
import { DashboardFolder } from '@typebot.io/prisma'
|
||||
import { stringify } from 'qs'
|
||||
import useSWR from 'swr'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const useFolders = ({
|
||||
parentId,
|
||||
@@ -18,7 +18,7 @@ export const useFolders = ({
|
||||
workspaceId ? `/api/folders?${params}` : null,
|
||||
fetcher,
|
||||
{
|
||||
dedupingInterval: env('E2E_TEST') === 'true' ? 0 : undefined,
|
||||
dedupingInterval: env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined,
|
||||
}
|
||||
)
|
||||
if (error) onError(error)
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Text,
|
||||
} from '@chakra-ui/react'
|
||||
import { Plan } from '@typebot.io/prisma'
|
||||
import { isDefined, getViewerUrl, isNotDefined, env } from '@typebot.io/lib'
|
||||
import { isDefined, isNotDefined } from '@typebot.io/lib'
|
||||
import { isPublicDomainAvailableQuery } from '../queries/isPublicDomainAvailableQuery'
|
||||
import { EditableUrl } from './EditableUrl'
|
||||
import { integrationsList } from './embeds/EmbedButton'
|
||||
@@ -25,6 +25,7 @@ import { CustomDomainsDropdown } from '@/features/customDomains/components/Custo
|
||||
import { TypebotHeader } from '@/features/editor/components/TypebotHeader'
|
||||
import { parseDefaultPublicId } from '../helpers/parseDefaultPublicId'
|
||||
import { useI18n } from '@/locales'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const SharePage = () => {
|
||||
const t = useI18n()
|
||||
@@ -97,7 +98,7 @@ export const SharePage = () => {
|
||||
</Heading>
|
||||
{typebot && (
|
||||
<EditableUrl
|
||||
hostname={getViewerUrl() ?? 'https://typebot.io'}
|
||||
hostname={env.NEXT_PUBLIC_VIEWER_URL[0]}
|
||||
pathname={publicId}
|
||||
isValid={checkIfPublicIdIsValid}
|
||||
onPathnameChange={handlePublicIdChange}
|
||||
@@ -120,7 +121,7 @@ export const SharePage = () => {
|
||||
</HStack>
|
||||
)}
|
||||
{isNotDefined(typebot?.customDomain) &&
|
||||
env('VERCEL_VIEWER_PROJECT_NAME') ? (
|
||||
env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME ? (
|
||||
<>
|
||||
{isProPlan(workspace) ? (
|
||||
<CustomDomainsDropdown
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
Text,
|
||||
Stack,
|
||||
} from '@chakra-ui/react'
|
||||
import { env, getViewerUrl } from '@typebot.io/lib'
|
||||
import { getViewerUrl } from '@typebot.io/lib'
|
||||
import { ModalProps } from '../EmbedButton'
|
||||
|
||||
export const FlutterFlowModal = ({
|
||||
@@ -51,16 +51,12 @@ export const FlutterFlowModal = ({
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
type={'text'}
|
||||
defaultValue={`${
|
||||
env('VIEWER_INTERNAL_URL') ?? getViewerUrl()
|
||||
}/${publicId}`}
|
||||
defaultValue={`${getViewerUrl()}/${publicId}`}
|
||||
/>
|
||||
<InputRightElement width="60px">
|
||||
<CopyButton
|
||||
size="sm"
|
||||
textToCopy={`${
|
||||
env('VIEWER_INTERNAL_URL') ?? getViewerUrl()
|
||||
}/${publicId}`}
|
||||
textToCopy={`${getViewerUrl()}/${publicId}`}
|
||||
/>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FlexProps } from '@chakra-ui/react'
|
||||
import { useTypebot } from '@/features/editor/providers/TypebotProvider'
|
||||
import { env, getViewerUrl } from '@typebot.io/lib'
|
||||
import { getViewerUrl } from '@typebot.io/lib'
|
||||
import { CodeEditor } from '@/components/inputs/CodeEditor'
|
||||
import prettier from 'prettier/standalone'
|
||||
import parserHtml from 'prettier/parser-html'
|
||||
@@ -13,9 +13,7 @@ type Props = {
|
||||
|
||||
export const IframeSnippet = ({ widthLabel, heightLabel }: Props) => {
|
||||
const { typebot } = useTypebot()
|
||||
const src = `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${
|
||||
typebot?.publicId
|
||||
}`
|
||||
const src = `${getViewerUrl()}/${typebot?.publicId}`
|
||||
const code = prettier.format(
|
||||
`<iframe src="${src}" style="border: none; width='${widthLabel}'; height='${heightLabel}'"></iframe>`,
|
||||
{ parser: 'html', plugins: [parserHtml] }
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
Text,
|
||||
Stack,
|
||||
} from '@chakra-ui/react'
|
||||
import { env, getViewerUrl } from '@typebot.io/lib'
|
||||
import { getViewerUrl } from '@typebot.io/lib'
|
||||
import { ModalProps } from '../EmbedButton'
|
||||
|
||||
export const NotionModal = ({
|
||||
@@ -49,16 +49,12 @@ export const NotionModal = ({
|
||||
<InputGroup size="sm">
|
||||
<Input
|
||||
type={'text'}
|
||||
defaultValue={`${
|
||||
env('VIEWER_INTERNAL_URL') ?? getViewerUrl()
|
||||
}/${publicId}`}
|
||||
defaultValue={`${getViewerUrl()}/${publicId}`}
|
||||
/>
|
||||
<InputRightElement width="60px">
|
||||
<CopyButton
|
||||
size="sm"
|
||||
textToCopy={`${
|
||||
env('VIEWER_INTERNAL_URL') ?? getViewerUrl()
|
||||
}/${publicId}`}
|
||||
textToCopy={`${getViewerUrl()}/${publicId}`}
|
||||
/>
|
||||
</InputRightElement>
|
||||
</InputGroup>
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { useState } from 'react'
|
||||
import { StandardSettings } from '../../../settings/StandardSettings'
|
||||
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
||||
import { env, getViewerUrl } from '@typebot.io/lib'
|
||||
import { getViewerUrl } from '@typebot.io/lib'
|
||||
|
||||
type Props = {
|
||||
publicId: string
|
||||
@@ -76,9 +76,7 @@ const parseWordpressShortcode = ({
|
||||
publicId: string
|
||||
}) => {
|
||||
return `[typebot typebot="${publicId}"${
|
||||
isCloudProdInstance
|
||||
? ''
|
||||
: ` host="${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}"`
|
||||
isCloudProdInstance ? '' : ` host="${getViewerUrl()}"`
|
||||
}${width ? ` width="${width}"` : ''}${height ? ` height="${height}"` : ''}]
|
||||
`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BotProps } from '@typebot.io/nextjs'
|
||||
import parserBabel from 'prettier/parser-babel'
|
||||
import prettier from 'prettier/standalone'
|
||||
import { env, getViewerUrl, isDefined } from '@typebot.io/lib'
|
||||
import { getViewerUrl, isDefined } from '@typebot.io/lib'
|
||||
import { Typebot } from '@typebot.io/schemas'
|
||||
import { isCloudProdInstance } from '@/helpers/isCloudProdInstance'
|
||||
import packageJson from '../../../../../../../../packages/embeds/js/package.json'
|
||||
@@ -58,7 +58,7 @@ export const parseApiHost = (
|
||||
customDomain: Typebot['customDomain'] | undefined
|
||||
) => {
|
||||
if (customDomain) return new URL(`https://${customDomain}`).origin
|
||||
return env('VIEWER_INTERNAL_URL') ?? getViewerUrl()
|
||||
return getViewerUrl()
|
||||
}
|
||||
|
||||
export const parseApiHostValue = (
|
||||
|
||||
@@ -4,6 +4,7 @@ import { PostHog } from 'posthog-node'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import got from 'got'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
// Only used for the cloud version of Typebot. It's the way it processes telemetry events and inject it to thrid-party services.
|
||||
export const processTelemetryEvent = authenticatedProcedure
|
||||
@@ -26,17 +27,17 @@ export const processTelemetryEvent = authenticatedProcedure
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { events }, ctx: { user } }) => {
|
||||
if (user.email !== process.env.ADMIN_EMAIL)
|
||||
if (user.email !== env.ADMIN_EMAIL)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Only app admin can process telemetry events',
|
||||
})
|
||||
if (!process.env.POSTHOG_API_KEY)
|
||||
if (!env.POSTHOG_API_KEY)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'Server does not have POSTHOG_API_KEY configured',
|
||||
})
|
||||
const client = new PostHog(process.env.POSTHOG_API_KEY, {
|
||||
const client = new PostHog(env.POSTHOG_API_KEY, {
|
||||
host: 'https://eu.posthog.com',
|
||||
})
|
||||
|
||||
@@ -65,11 +66,8 @@ export const processTelemetryEvent = authenticatedProcedure
|
||||
groupKey: event.typebotId,
|
||||
properties: { name: event.data.name },
|
||||
})
|
||||
if (
|
||||
event.name === 'User created' &&
|
||||
process.env.USER_CREATED_WEBHOOK_URL
|
||||
) {
|
||||
await got.post(process.env.USER_CREATED_WEBHOOK_URL, {
|
||||
if (event.name === 'User created' && env.USER_CREATED_WEBHOOK_URL) {
|
||||
await got.post(env.USER_CREATED_WEBHOOK_URL, {
|
||||
json: {
|
||||
email: event.data.email,
|
||||
name: event.data.name ? event.data.name.split(' ')[0] : undefined,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Text, HStack } from '@chakra-ui/react'
|
||||
import { env, isEmpty } from '@typebot.io/lib'
|
||||
import { AutocompleteInput } from '@/components/inputs/AutocompleteInput'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
type FontSelectorProps = {
|
||||
activeFont?: string
|
||||
@@ -20,11 +20,9 @@ export const FontSelector = ({
|
||||
}, [])
|
||||
|
||||
const fetchPopularFonts = async () => {
|
||||
if (isEmpty(env('GOOGLE_API_KEY'))) return []
|
||||
if (!env.NEXT_PUBLIC_GOOGLE_API_KEY) return []
|
||||
const response = await fetch(
|
||||
`https://www.googleapis.com/webfonts/v1/webfonts?key=${env(
|
||||
'GOOGLE_API_KEY'
|
||||
)}&sort=popularity`
|
||||
`https://www.googleapis.com/webfonts/v1/webfonts?key=${env.NEXT_PUBLIC_GOOGLE_API_KEY}&sort=popularity`
|
||||
)
|
||||
return (await response.json()).items.map(
|
||||
(item: { family: string }) => item.family
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { CollaboratorsOnTypebots, User } from '@typebot.io/prisma'
|
||||
import { Typebot } from '@typebot.io/schemas'
|
||||
|
||||
@@ -9,7 +10,7 @@ export const isReadTypebotForbidden = async (
|
||||
user: Pick<User, 'email' | 'id'>
|
||||
) => {
|
||||
if (
|
||||
process.env.ADMIN_EMAIL === user.email ||
|
||||
env.ADMIN_EMAIL === user.email ||
|
||||
typebot.collaborators.find(
|
||||
(collaborator) => collaborator.userId === user.id
|
||||
)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { env } from '@typebot.io/env'
|
||||
import { Plan } from '@typebot.io/prisma'
|
||||
|
||||
export const parseWorkspaceDefaultPlan = (userEmail: string) => {
|
||||
if (process.env.ADMIN_EMAIL === userEmail) return Plan.UNLIMITED
|
||||
const defaultPlan = process.env.DEFAULT_WORKSPACE_PLAN as Plan | undefined
|
||||
if (env.ADMIN_EMAIL === userEmail) return Plan.UNLIMITED
|
||||
const defaultPlan = env.DEFAULT_WORKSPACE_PLAN as Plan | undefined
|
||||
if (defaultPlan && Object.values(Plan).includes(defaultPlan))
|
||||
return defaultPlan
|
||||
return Plan.FREE
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { WorkspaceInvitation } from '@typebot.io/prisma'
|
||||
import { fetcher } from '@/helpers/fetcher'
|
||||
import useSWR from 'swr'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
import { Member } from '../types'
|
||||
|
||||
export const useMembers = ({ workspaceId }: { workspaceId?: string }) => {
|
||||
@@ -9,7 +9,7 @@ export const useMembers = ({ workspaceId }: { workspaceId?: string }) => {
|
||||
{ members: Member[]; invitations: WorkspaceInvitation[] },
|
||||
Error
|
||||
>(workspaceId ? `/api/workspaces/${workspaceId}/members` : null, fetcher, {
|
||||
dedupingInterval: env('E2E_TEST') === 'true' ? 0 : undefined,
|
||||
dedupingInterval: env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined,
|
||||
})
|
||||
return {
|
||||
members: data?.members ?? [],
|
||||
|
||||
@@ -7,14 +7,14 @@ import {
|
||||
} from '@typebot.io/prisma'
|
||||
import prisma from '@/lib/prisma'
|
||||
import { NextApiResponse } from 'next'
|
||||
import { env, isNotEmpty } from '@typebot.io/lib'
|
||||
import { forbidden } from '@typebot.io/lib/api'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const canWriteTypebots = (
|
||||
typebotIds: string[] | string,
|
||||
user: Pick<User, 'email' | 'id'>
|
||||
): Prisma.TypebotWhereInput =>
|
||||
isNotEmpty(env('E2E_TEST'))
|
||||
env.NEXT_PUBLIC_E2E_TEST
|
||||
? { id: typeof typebotIds === 'string' ? typebotIds : { in: typebotIds } }
|
||||
: {
|
||||
id: typeof typebotIds === 'string' ? typebotIds : { in: typebotIds },
|
||||
@@ -40,7 +40,7 @@ export const canReadTypebots = (
|
||||
) => ({
|
||||
id: typeof typebotIds === 'string' ? typebotIds : { in: typebotIds },
|
||||
workspace:
|
||||
user.email === process.env.ADMIN_EMAIL || isNotEmpty(env('E2E_TEST'))
|
||||
user.email === env.ADMIN_EMAIL || env.NEXT_PUBLIC_E2E_TEST
|
||||
? undefined
|
||||
: {
|
||||
members: {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { env } from '@typebot.io/env'
|
||||
import { Client } from 'minio'
|
||||
|
||||
export const deleteFilesFromBucket = async ({
|
||||
@@ -5,32 +6,26 @@ export const deleteFilesFromBucket = async ({
|
||||
}: {
|
||||
urls: string[]
|
||||
}): Promise<void> => {
|
||||
if (
|
||||
!process.env.S3_ENDPOINT ||
|
||||
!process.env.S3_ACCESS_KEY ||
|
||||
!process.env.S3_SECRET_KEY
|
||||
)
|
||||
if (!env.S3_ENDPOINT || !env.S3_ACCESS_KEY || !env.S3_SECRET_KEY)
|
||||
throw new Error(
|
||||
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY'
|
||||
)
|
||||
|
||||
const useSSL =
|
||||
process.env.S3_SSL && process.env.S3_SSL === 'false' ? false : true
|
||||
const minioClient = new Client({
|
||||
endPoint: process.env.S3_ENDPOINT,
|
||||
port: process.env.S3_PORT ? parseInt(process.env.S3_PORT) : undefined,
|
||||
useSSL,
|
||||
accessKey: process.env.S3_ACCESS_KEY,
|
||||
secretKey: process.env.S3_SECRET_KEY,
|
||||
region: process.env.S3_REGION,
|
||||
endPoint: env.S3_ENDPOINT,
|
||||
port: env.S3_PORT,
|
||||
useSSL: env.S3_SSL,
|
||||
accessKey: env.S3_ACCESS_KEY,
|
||||
secretKey: env.S3_SECRET_KEY,
|
||||
region: env.S3_REGION,
|
||||
})
|
||||
|
||||
const bucket = process.env.S3_BUCKET ?? 'typebot'
|
||||
const bucket = env.S3_BUCKET ?? 'typebot'
|
||||
|
||||
return minioClient.removeObjects(
|
||||
bucket,
|
||||
urls
|
||||
.filter((url) => url.includes(process.env.S3_ENDPOINT as string))
|
||||
.filter((url) => url.includes(env.S3_ENDPOINT as string))
|
||||
.map((url) => url.split(`/${bucket}/`)[1])
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import { GoogleSheetsCredentials } from '@typebot.io/schemas'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
import { decrypt, encrypt } from '@typebot.io/lib/api'
|
||||
import prisma from './prisma'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export const oauth2Client = new OAuth2Client(
|
||||
process.env.GOOGLE_CLIENT_ID,
|
||||
process.env.GOOGLE_CLIENT_SECRET,
|
||||
`${process.env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
||||
env.GOOGLE_CLIENT_ID,
|
||||
env.GOOGLE_CLIENT_SECRET,
|
||||
`${env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
||||
)
|
||||
|
||||
export const getAuthenticatedGoogleClient = async (
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Instantiates a single instance PrismaClient and save it on the global object.
|
||||
* @link https://www.prisma.io/docs/support/help-articles/nextjs-prisma-client-dev-practices
|
||||
*/
|
||||
import { env } from '@typebot.io/env'
|
||||
import { PrismaClient } from '@typebot.io/prisma'
|
||||
|
||||
const prismaGlobal = global as typeof global & {
|
||||
@@ -11,10 +12,10 @@ const prismaGlobal = global as typeof global & {
|
||||
const prisma: PrismaClient =
|
||||
prismaGlobal.prisma ||
|
||||
new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
|
||||
log: env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (env.NODE_ENV !== 'production') {
|
||||
prismaGlobal.prisma = prisma
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@ import { createTRPCProxyClient, httpBatchLink, loggerLink } from '@trpc/client'
|
||||
import { createTRPCNext } from '@trpc/next'
|
||||
import type { AppRouter } from '../helpers/server/routers/v1/trpcRouter'
|
||||
import superjson from 'superjson'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const getBaseUrl = () =>
|
||||
typeof window !== 'undefined' ? '' : process.env.NEXTAUTH_URL
|
||||
const getBaseUrl = () => (typeof window !== 'undefined' ? '' : env.NEXTAUTH_URL)
|
||||
|
||||
export const trpc = createTRPCNext<AppRouter>({
|
||||
config() {
|
||||
@@ -36,5 +35,5 @@ export const trpcVanilla = createTRPCProxyClient<AppRouter>({
|
||||
})
|
||||
|
||||
export const defaultQueryOptions = {
|
||||
refetchOnMount: env('E2E_TEST') === 'true',
|
||||
refetchOnMount: env.NEXT_PUBLIC_E2E_TEST,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const Document = () => (
|
||||
/>
|
||||
<meta name="google" content="notranslate" />
|
||||
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
|
||||
<script src="/__env.js" />
|
||||
<script src="/__ENV.js" />
|
||||
</Head>
|
||||
<body>
|
||||
<ColorModeScript initialColorMode={customTheme.config.initialColorMode} />
|
||||
|
||||
@@ -10,139 +10,114 @@ import { Provider } from 'next-auth/providers'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { customAdapter } from '../../../features/auth/api/customAdapter'
|
||||
import { User } from '@typebot.io/prisma'
|
||||
import { env, getAtPath, isDefined, isNotEmpty } from '@typebot.io/lib'
|
||||
import { getAtPath, isDefined } from '@typebot.io/lib'
|
||||
import { mockedUser } from '@/features/auth/mockedUser'
|
||||
import { getNewUserInvitations } from '@/features/auth/helpers/getNewUserInvitations'
|
||||
import { sendVerificationRequest } from '@/features/auth/helpers/sendVerificationRequest'
|
||||
import { Ratelimit } from '@upstash/ratelimit'
|
||||
import { Redis } from '@upstash/redis/nodejs'
|
||||
import got from 'got'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const providers: Provider[] = []
|
||||
|
||||
let rateLimit: Ratelimit | undefined
|
||||
|
||||
if (
|
||||
process.env.UPSTASH_REDIS_REST_URL &&
|
||||
process.env.UPSTASH_REDIS_REST_TOKEN
|
||||
) {
|
||||
if (env.UPSTASH_REDIS_REST_URL && env.UPSTASH_REDIS_REST_TOKEN) {
|
||||
rateLimit = new Ratelimit({
|
||||
redis: Redis.fromEnv(),
|
||||
limiter: Ratelimit.slidingWindow(1, '60 s'),
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
isNotEmpty(process.env.GITHUB_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.GITHUB_CLIENT_SECRET)
|
||||
)
|
||||
if (env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET)
|
||||
providers.push(
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
clientId: env.GITHUB_CLIENT_ID,
|
||||
clientSecret: env.GITHUB_CLIENT_SECRET,
|
||||
})
|
||||
)
|
||||
|
||||
if (isNotEmpty(env('SMTP_FROM')) && process.env.SMTP_AUTH_DISABLED !== 'true')
|
||||
if (env.NEXT_PUBLIC_SMTP_FROM && env.SMTP_AUTH_DISABLED)
|
||||
providers.push(
|
||||
EmailProvider({
|
||||
server: {
|
||||
host: process.env.SMTP_HOST,
|
||||
port: process.env.SMTP_PORT ? Number(process.env.SMTP_PORT) : 25,
|
||||
secure: process.env.SMTP_SECURE
|
||||
? process.env.SMTP_SECURE === 'true'
|
||||
: false,
|
||||
host: env.SMTP_HOST,
|
||||
port: env.SMTP_PORT ? Number(env.SMTP_PORT) : 25,
|
||||
secure: env.SMTP_SECURE ? env.SMTP_SECURE : false,
|
||||
auth: {
|
||||
user: process.env.SMTP_USERNAME,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
user: env.SMTP_USERNAME,
|
||||
pass: env.SMTP_PASSWORD,
|
||||
},
|
||||
},
|
||||
from: env('SMTP_FROM'),
|
||||
from: env.NEXT_PUBLIC_SMTP_FROM,
|
||||
sendVerificationRequest,
|
||||
})
|
||||
)
|
||||
|
||||
if (
|
||||
isNotEmpty(process.env.GOOGLE_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.GOOGLE_CLIENT_SECRET)
|
||||
)
|
||||
if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET)
|
||||
providers.push(
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
clientId: env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
||||
})
|
||||
)
|
||||
|
||||
if (
|
||||
isNotEmpty(process.env.FACEBOOK_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.FACEBOOK_CLIENT_SECRET)
|
||||
)
|
||||
if (env.FACEBOOK_CLIENT_ID && env.FACEBOOK_CLIENT_SECRET)
|
||||
providers.push(
|
||||
FacebookProvider({
|
||||
clientId: process.env.FACEBOOK_CLIENT_ID,
|
||||
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
|
||||
clientId: env.FACEBOOK_CLIENT_ID,
|
||||
clientSecret: env.FACEBOOK_CLIENT_SECRET,
|
||||
})
|
||||
)
|
||||
|
||||
if (
|
||||
isNotEmpty(process.env.GITLAB_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.GITLAB_CLIENT_SECRET)
|
||||
) {
|
||||
const BASE_URL = process.env.GITLAB_BASE_URL || 'https://gitlab.com'
|
||||
if (env.GITLAB_CLIENT_ID && env.GITLAB_CLIENT_SECRET) {
|
||||
const BASE_URL = env.GITLAB_BASE_URL || 'https://gitlab.com'
|
||||
providers.push(
|
||||
GitlabProvider({
|
||||
clientId: process.env.GITLAB_CLIENT_ID,
|
||||
clientSecret: process.env.GITLAB_CLIENT_SECRET,
|
||||
clientId: env.GITLAB_CLIENT_ID,
|
||||
clientSecret: env.GITLAB_CLIENT_SECRET,
|
||||
authorization: `${BASE_URL}/oauth/authorize?scope=read_api`,
|
||||
token: `${BASE_URL}/oauth/token`,
|
||||
userinfo: `${BASE_URL}/api/v4/user`,
|
||||
name: process.env.GITLAB_NAME || 'GitLab',
|
||||
name: env.GITLAB_NAME || 'GitLab',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
isNotEmpty(process.env.AZURE_AD_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.AZURE_AD_CLIENT_SECRET) &&
|
||||
isNotEmpty(process.env.AZURE_AD_TENANT_ID)
|
||||
env.AZURE_AD_CLIENT_ID &&
|
||||
env.AZURE_AD_CLIENT_SECRET &&
|
||||
env.AZURE_AD_TENANT_ID
|
||||
) {
|
||||
providers.push(
|
||||
AzureADProvider({
|
||||
clientId: process.env.AZURE_AD_CLIENT_ID,
|
||||
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
|
||||
tenantId: process.env.AZURE_AD_TENANT_ID,
|
||||
clientId: env.AZURE_AD_CLIENT_ID,
|
||||
clientSecret: env.AZURE_AD_CLIENT_SECRET,
|
||||
tenantId: env.AZURE_AD_TENANT_ID,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (isNotEmpty(process.env.CUSTOM_OAUTH_WELL_KNOWN_URL)) {
|
||||
if (env.CUSTOM_OAUTH_WELL_KNOWN_URL) {
|
||||
providers.push({
|
||||
id: 'custom-oauth',
|
||||
name: process.env.CUSTOM_OAUTH_NAME ?? 'Custom OAuth',
|
||||
name: env.CUSTOM_OAUTH_NAME ?? 'Custom OAuth',
|
||||
type: 'oauth',
|
||||
authorization: {
|
||||
params: {
|
||||
scope: process.env.CUSTOM_OAUTH_SCOPE ?? 'openid profile email',
|
||||
scope: env.CUSTOM_OAUTH_SCOPE ?? 'openid profile email',
|
||||
},
|
||||
},
|
||||
clientId: process.env.CUSTOM_OAUTH_CLIENT_ID,
|
||||
clientSecret: process.env.CUSTOM_OAUTH_CLIENT_SECRET,
|
||||
wellKnown: process.env.CUSTOM_OAUTH_WELL_KNOWN_URL,
|
||||
clientId: env.CUSTOM_OAUTH_CLIENT_ID,
|
||||
clientSecret: env.CUSTOM_OAUTH_CLIENT_SECRET,
|
||||
wellKnown: env.CUSTOM_OAUTH_WELL_KNOWN_URL,
|
||||
profile(profile) {
|
||||
return {
|
||||
id: getAtPath(profile, process.env.CUSTOM_OAUTH_USER_ID_PATH ?? 'id'),
|
||||
name: getAtPath(
|
||||
profile,
|
||||
process.env.CUSTOM_OAUTH_USER_NAME_PATH ?? 'name'
|
||||
),
|
||||
email: getAtPath(
|
||||
profile,
|
||||
process.env.CUSTOM_OAUTH_USER_EMAIL_PATH ?? 'email'
|
||||
),
|
||||
image: getAtPath(
|
||||
profile,
|
||||
process.env.CUSTOM_OAUTH_USER_IMAGE_PATH ?? 'image'
|
||||
),
|
||||
id: getAtPath(profile, env.CUSTOM_OAUTH_USER_ID_PATH ?? 'id'),
|
||||
name: getAtPath(profile, env.CUSTOM_OAUTH_USER_NAME_PATH ?? 'name'),
|
||||
email: getAtPath(profile, env.CUSTOM_OAUTH_USER_EMAIL_PATH ?? 'email'),
|
||||
image: getAtPath(profile, env.CUSTOM_OAUTH_USER_IMAGE_PATH ?? 'image'),
|
||||
} as User
|
||||
},
|
||||
})
|
||||
@@ -150,16 +125,14 @@ if (isNotEmpty(process.env.CUSTOM_OAUTH_WELL_KNOWN_URL)) {
|
||||
|
||||
export const authOptions: AuthOptions = {
|
||||
adapter: customAdapter(prisma),
|
||||
secret: process.env.ENCRYPTION_SECRET,
|
||||
secret: env.ENCRYPTION_SECRET,
|
||||
providers,
|
||||
session: {
|
||||
strategy: 'database',
|
||||
},
|
||||
pages: {
|
||||
signIn: '/signin',
|
||||
newUser: process.env.NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID
|
||||
? '/onboarding'
|
||||
: undefined,
|
||||
newUser: env.NEXT_PUBLIC_ONBOARDING_TYPEBOT_ID ? '/onboarding' : undefined,
|
||||
},
|
||||
callbacks: {
|
||||
session: async ({ session, user }) => {
|
||||
@@ -181,7 +154,7 @@ export const authOptions: AuthOptions = {
|
||||
if (disposableEmailDomains.includes(user.email.split('@')[1]))
|
||||
return false
|
||||
}
|
||||
if (process.env.DISABLE_SIGNUP === 'true' && isNewUser && user.email) {
|
||||
if (env.DISABLE_SIGNUP && isNewUser && user.email) {
|
||||
const { invitations, workspaceInvitations } =
|
||||
await getNewUserInvitations(prisma, user.email)
|
||||
if (invitations.length === 0 && workspaceInvitations.length === 0)
|
||||
@@ -201,7 +174,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const isMockingSession =
|
||||
req.method === 'GET' &&
|
||||
req.url === '/api/auth/session' &&
|
||||
env('E2E_TEST') === 'true'
|
||||
env.NEXT_PUBLIC_E2E_TEST
|
||||
if (isMockingSession) return res.send({ user: mockedUser })
|
||||
const requestIsFromCompanyFirewall = req.method === 'HEAD'
|
||||
if (requestIsFromCompanyFirewall) return res.status(200).end()
|
||||
@@ -248,7 +221,7 @@ const getUserGroups = async (account: Account): Promise<string[]> => {
|
||||
): Promise<{ full_path: string }[]> => {
|
||||
const res = await fetch(
|
||||
`${
|
||||
process.env.GITLAB_BASE_URL || 'https://gitlab.com'
|
||||
env.GITLAB_BASE_URL || 'https://gitlab.com'
|
||||
}/api/v4/groups?per_page=100&page=${page}`,
|
||||
{ headers: { Authorization: `Bearer ${accessToken}` } }
|
||||
)
|
||||
@@ -269,7 +242,7 @@ const getUserGroups = async (account: Account): Promise<string[]> => {
|
||||
const getRequiredGroups = (provider: string): string[] => {
|
||||
switch (provider) {
|
||||
case 'gitlab':
|
||||
return process.env.GITLAB_REQUIRED_GROUPS?.split(',') || []
|
||||
return env.GITLAB_REQUIRED_GROUPS ?? []
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { stringify } from 'querystring'
|
||||
import { badRequest, encrypt, notAuthenticated } from '@typebot.io/lib/api'
|
||||
import { oauth2Client } from '@/lib/googleSheets'
|
||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getAuthenticatedUser(req, res)
|
||||
@@ -49,9 +50,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
data: credentials,
|
||||
})
|
||||
const queryParams = stringify({ blockId, credentialsId })
|
||||
res.redirect(
|
||||
`${redirectUrl}?${queryParams}` ?? `${process.env.NEXTAUTH_URL}`
|
||||
)
|
||||
res.redirect(`${redirectUrl}?${queryParams}` ?? `${env.NEXTAUTH_URL}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '@typebot.io/lib/api'
|
||||
import { got } from 'got'
|
||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getAuthenticatedUser(req, res)
|
||||
@@ -31,8 +32,8 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
|
||||
const deleteDomainOnVercel = (name: string) =>
|
||||
got.delete({
|
||||
url: `https://api.vercel.com/v8/projects/${process.env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains/${name}?teamId=${process.env.VERCEL_TEAM_ID}`,
|
||||
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
|
||||
url: `https://api.vercel.com/v8/projects/${env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains/${name}?teamId=${env.VERCEL_TEAM_ID}`,
|
||||
headers: { Authorization: `Bearer ${env.VERCEL_TOKEN}` },
|
||||
})
|
||||
|
||||
export default handler
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
methodNotAllowed,
|
||||
notAuthenticated,
|
||||
} from '@typebot.io/lib/api'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const handler = async (
|
||||
req: NextApiRequest,
|
||||
@@ -16,11 +17,7 @@ const handler = async (
|
||||
const user = await getAuthenticatedUser(req, res)
|
||||
if (!user) return notAuthenticated(res)
|
||||
|
||||
if (
|
||||
!process.env.S3_ENDPOINT ||
|
||||
!process.env.S3_ACCESS_KEY ||
|
||||
!process.env.S3_SECRET_KEY
|
||||
)
|
||||
if (!env.S3_ENDPOINT || !env.S3_ACCESS_KEY || !env.S3_SECRET_KEY)
|
||||
return badRequest(
|
||||
res,
|
||||
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY'
|
||||
|
||||
@@ -8,10 +8,11 @@ import { Plan, WorkspaceRole } from '@typebot.io/prisma'
|
||||
import { RequestHandler } from 'next/dist/server/next'
|
||||
import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEvent'
|
||||
import { Settings } from '@typebot.io/schemas'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
if (!process.env.STRIPE_SECRET_KEY || !process.env.STRIPE_WEBHOOK_SECRET)
|
||||
if (!env.STRIPE_SECRET_KEY || !env.STRIPE_WEBHOOK_SECRET)
|
||||
throw new Error('STRIPE_SECRET_KEY or STRIPE_WEBHOOK_SECRET missing')
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY, {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
|
||||
@@ -19,7 +20,7 @@ const cors = Cors({
|
||||
allowMethods: ['POST', 'HEAD'],
|
||||
})
|
||||
|
||||
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET as string
|
||||
const webhookSecret = env.STRIPE_WEBHOOK_SECRET as string
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
notAuthenticated,
|
||||
} from '@typebot.io/lib/api'
|
||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { sendGuestInvitationEmail } from '@typebot.io/emails'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getAuthenticatedUser(req, res)
|
||||
@@ -80,11 +80,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
await prisma.invitation.create({
|
||||
data: { email: email.toLowerCase().trim(), type, typebotId },
|
||||
})
|
||||
if (env('E2E_TEST') !== 'true')
|
||||
if (!env.NEXT_PUBLIC_E2E_TEST)
|
||||
await sendGuestInvitationEmail({
|
||||
to: email,
|
||||
hostEmail: user.email ?? '',
|
||||
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${typebot.workspaceId}`,
|
||||
url: `${env.NEXTAUTH_URL}/typebots?workspaceId=${typebot.workspaceId}`,
|
||||
guestEmail: email.toLowerCase(),
|
||||
typebotName: typebot.name,
|
||||
workspaceName: typebot.workspace?.name ?? '',
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
} from '@typebot.io/lib/api'
|
||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||
import { sendWorkspaceMemberInvitationEmail } from '@typebot.io/emails'
|
||||
import { env } from '@typebot.io/lib'
|
||||
import { isSeatsLimitReached } from '@typebot.io/lib/pricing'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getAuthenticatedUser(req, res)
|
||||
@@ -52,12 +52,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
userId: existingUser.id,
|
||||
},
|
||||
})
|
||||
if (env('E2E_TEST') !== 'true')
|
||||
if (!env.NEXT_PUBLIC_E2E_TEST)
|
||||
await sendWorkspaceMemberInvitationEmail({
|
||||
to: data.email,
|
||||
workspaceName: workspace.name,
|
||||
guestEmail: data.email,
|
||||
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspace.id}`,
|
||||
url: `${env.NEXTAUTH_URL}/typebots?workspaceId=${workspace.id}`,
|
||||
hostEmail: user.email ?? '',
|
||||
})
|
||||
return res.send({
|
||||
@@ -71,12 +71,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
})
|
||||
} else {
|
||||
const invitation = await prisma.workspaceInvitation.create({ data })
|
||||
if (env('E2E_TEST') !== 'true')
|
||||
if (!env.NEXT_PUBLIC_E2E_TEST)
|
||||
await sendWorkspaceMemberInvitationEmail({
|
||||
to: data.email,
|
||||
workspaceName: workspace.name,
|
||||
guestEmail: data.email,
|
||||
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspace.id}`,
|
||||
url: `${env.NEXTAUTH_URL}/typebots?workspaceId=${workspace.id}`,
|
||||
hostEmail: user.email ?? '',
|
||||
})
|
||||
return res.send({ invitation })
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isNotDefined } from '@typebot.io/lib'
|
||||
import { sign } from 'jsonwebtoken'
|
||||
import { getServerSession } from 'next-auth'
|
||||
import { authOptions } from './api/auth/[...nextauth]'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export default function Page() {
|
||||
return null
|
||||
@@ -28,7 +29,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
}
|
||||
|
||||
const createSSOToken = (user: User) => {
|
||||
if (!process.env.SLEEKPLAN_SSO_KEY) return
|
||||
if (!env.SLEEKPLAN_SSO_KEY) return
|
||||
const userData = {
|
||||
mail: user.email,
|
||||
id: user.id,
|
||||
@@ -36,5 +37,5 @@ const createSSOToken = (user: User) => {
|
||||
img: user.image,
|
||||
}
|
||||
|
||||
return sign(userData, process.env.SLEEKPLAN_SSO_KEY, { algorithm: 'HS256' })
|
||||
return sign(userData, env.SLEEKPLAN_SSO_KEY, { algorithm: 'HS256' })
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { isNotDefined } from '@typebot.io/lib'
|
||||
import { sign } from 'jsonwebtoken'
|
||||
import { authOptions } from '../api/auth/[...nextauth]'
|
||||
import { GetServerSidePropsContext } from 'next'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
export default function Page() {
|
||||
return null
|
||||
@@ -29,7 +30,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
}
|
||||
|
||||
const createSSOToken = (user: User) => {
|
||||
if (!process.env.SLEEKPLAN_SSO_KEY) return
|
||||
if (!env.SLEEKPLAN_SSO_KEY) return
|
||||
const userData = {
|
||||
mail: user.email,
|
||||
id: user.id,
|
||||
@@ -37,5 +38,5 @@ const createSSOToken = (user: User) => {
|
||||
img: user.image,
|
||||
}
|
||||
|
||||
return sign(userData, process.env.SLEEKPLAN_SSO_KEY, { algorithm: 'HS256' })
|
||||
return sign(userData, env.SLEEKPLAN_SSO_KEY, { algorithm: 'HS256' })
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ import {
|
||||
} from '@typebot.io/prisma'
|
||||
import Stripe from 'stripe'
|
||||
import { proWorkspaceId } from '@typebot.io/lib/playwright/databaseSetup'
|
||||
import { env } from '@typebot.io/env'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY ?? '', {
|
||||
const stripe = new Stripe(env.STRIPE_SECRET_KEY ?? '', {
|
||||
apiVersion: '2022-11-15',
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user