🧑‍💻 Improve env variables type safety and management (#718)

Closes #679
This commit is contained in:
Baptiste Arnaud
2023-08-28 09:13:53 +02:00
committed by GitHub
parent a23a8c4456
commit 786e5cb582
148 changed files with 1550 additions and 1293 deletions

View File

@@ -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(() => {

View File

@@ -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'
)
)

View File

@@ -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)

View File

@@ -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
)

View File

@@ -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={() => {

View File

@@ -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

View File

@@ -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',
})

View File

@@ -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',
})

View File

@@ -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,

View File

@@ -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({

View File

@@ -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({

View File

@@ -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)

View File

@@ -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,
},
],

View File

@@ -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',

View File

@@ -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"')

View File

@@ -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>

View File

@@ -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(

View File

@@ -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()

View File

@@ -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)

View File

@@ -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 }
})

View File

@@ -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)

View File

@@ -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

View File

@@ -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>

View File

@@ -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] }

View File

@@ -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>

View File

@@ -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}"` : ''}]
`
}

View File

@@ -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 = (

View File

@@ -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,

View File

@@ -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

View File

@@ -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
)

View File

@@ -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

View File

@@ -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 ?? [],