docs: 📝 Update env var and write Configuration doc
This commit is contained in:
@ -1,60 +1,14 @@
|
|||||||
DATABASE_URL=postgresql://postgres:@localhost:5432/typebot
|
DATABASE_URL=postgresql://postgres:@localhost:5432/typebot
|
||||||
|
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6 #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
||||||
ENCRYPTION_SECRET=q3t6v9y$B&E)H@McQfTjWnZr4u7x!z%C #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
|
||||||
NEXTAUTH_URL=http://localhost:3000
|
NEXTAUTH_URL=http://localhost:3000
|
||||||
|
NEXT_PUBLIC_VIEWER_URL=http://localhost:3001
|
||||||
|
|
||||||
# Used for email auth and email notifications
|
|
||||||
AUTH_EMAIL_SERVER_USERNAME=username
|
|
||||||
AUTH_EMAIL_SERVER_PASSWORD=password
|
|
||||||
AUTH_EMAIL_SERVER_HOST=smtp.example.com
|
|
||||||
AUTH_EMAIL_SERVER_PORT=587
|
|
||||||
AUTH_EMAIL_FROM_EMAIL=noreply@example.com
|
|
||||||
AUTH_EMAIL_FROM_NAME="John Smith"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_EMAIL=
|
|
||||||
|
|
||||||
# Storage
|
|
||||||
# Used for uploading images, videos, etc...
|
|
||||||
S3_ACCESS_KEY=minio
|
S3_ACCESS_KEY=minio
|
||||||
S3_SECRET_KEY=minio123
|
S3_SECRET_KEY=minio123
|
||||||
S3_BUCKET=typebot
|
S3_BUCKET=typebot
|
||||||
S3_PORT=9000
|
S3_PORT=9000
|
||||||
S3_ENDPOINT=localhost
|
S3_ENDPOINT=localhost
|
||||||
S3_SSL=false
|
S3_SSL=false
|
||||||
# S3_REGION=
|
|
||||||
|
|
||||||
# Auth
|
# For more configuration options check out:
|
||||||
# (Optional) Used to login using GitHub
|
https://docs.typebot.io/self-hosting/configuration
|
||||||
GITHUB_CLIENT_ID=
|
|
||||||
GITHUB_CLIENT_SECRET=
|
|
||||||
|
|
||||||
# (Optional) Used to login using Google AND Google Sheets integration
|
|
||||||
GOOGLE_CLIENT_ID=
|
|
||||||
GOOGLE_CLIENT_SECRET=
|
|
||||||
|
|
||||||
# (Optional) Used to login using Facebook
|
|
||||||
FACEBOOK_CLIENT_ID=
|
|
||||||
FACEBOOK_CLIENT_SECRET=
|
|
||||||
|
|
||||||
# (Optional) Subscription Payment
|
|
||||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=
|
|
||||||
STRIPE_SECRET_KEY=
|
|
||||||
STRIPE_PRICE_USD_ID=
|
|
||||||
STRIPE_PRICE_EUR_ID=
|
|
||||||
STRIPE_WEBHOOK_SECRET=
|
|
||||||
|
|
||||||
# (Optional) Used for GIF search
|
|
||||||
NEXT_PUBLIC_GIPHY_API_KEY=
|
|
||||||
|
|
||||||
NEXT_PUBLIC_VIEWER_HOST=http://localhost:3001
|
|
||||||
|
|
||||||
# (Optional) Error tracking with Sentry
|
|
||||||
NEXT_PUBLIC_SENTRY_DSN=
|
|
||||||
SENTRY_AUTH_TOKEN=
|
|
||||||
SENTRY_PROJECT=
|
|
||||||
SENTRY_ORG=
|
|
||||||
|
|
||||||
# Vercel
|
|
||||||
VERCEL_TOKEN=
|
|
||||||
VERCEL_VIEWER_PROJECT_NAME=
|
|
||||||
VERCEL_TEAM_ID=
|
|
@ -52,26 +52,30 @@ export const SignInForm = ({
|
|||||||
return (
|
return (
|
||||||
<Stack spacing="4">
|
<Stack spacing="4">
|
||||||
<SocialLoginButtons />
|
<SocialLoginButtons />
|
||||||
<DividerWithText mt="6">Or with your email</DividerWithText>
|
{process.env.NEXT_PUBLIC_SMTP_FROM && (
|
||||||
<HStack as="form" onSubmit={handleEmailSubmit}>
|
<>
|
||||||
<Input
|
<DividerWithText mt="6">Or with your email</DividerWithText>
|
||||||
name="email"
|
<HStack as="form" onSubmit={handleEmailSubmit}>
|
||||||
type="email"
|
<Input
|
||||||
autoComplete="email"
|
name="email"
|
||||||
placeholder="email@company.com"
|
type="email"
|
||||||
required
|
autoComplete="email"
|
||||||
value={emailValue}
|
placeholder="email@company.com"
|
||||||
onChange={handleEmailChange}
|
required
|
||||||
/>
|
value={emailValue}
|
||||||
<Button
|
onChange={handleEmailChange}
|
||||||
type="submit"
|
/>
|
||||||
isLoading={
|
<Button
|
||||||
['loading', 'authenticated'].includes(status) || authLoading
|
type="submit"
|
||||||
}
|
isLoading={
|
||||||
>
|
['loading', 'authenticated'].includes(status) || authLoading
|
||||||
Submit
|
}
|
||||||
</Button>
|
>
|
||||||
</HStack>
|
Submit
|
||||||
|
</Button>
|
||||||
|
</HStack>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -36,24 +36,28 @@ export const SocialLoginButtons = () => {
|
|||||||
>
|
>
|
||||||
Continue with GitHub
|
Continue with GitHub
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
{process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID && (
|
||||||
leftIcon={<GoogleLogo />}
|
<Button
|
||||||
onClick={handleGoogleClick}
|
leftIcon={<GoogleLogo />}
|
||||||
data-testid="google"
|
onClick={handleGoogleClick}
|
||||||
isLoading={['loading', 'authenticated'].includes(status)}
|
data-testid="google"
|
||||||
variant="outline"
|
isLoading={['loading', 'authenticated'].includes(status)}
|
||||||
>
|
variant="outline"
|
||||||
Continue with Google
|
>
|
||||||
</Button>
|
Continue with Google
|
||||||
<Button
|
</Button>
|
||||||
leftIcon={<FacebookLogo />}
|
)}
|
||||||
onClick={handleFacebookClick}
|
{process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID && (
|
||||||
data-testid="facebook"
|
<Button
|
||||||
isLoading={['loading', 'authenticated'].includes(status)}
|
leftIcon={<FacebookLogo />}
|
||||||
variant="outline"
|
onClick={handleFacebookClick}
|
||||||
>
|
data-testid="facebook"
|
||||||
Continue with Facebook
|
isLoading={['loading', 'authenticated'].includes(status)}
|
||||||
</Button>
|
variant="outline"
|
||||||
|
>
|
||||||
|
Continue with Facebook
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ export const ShareContent = () => {
|
|||||||
{typebot && (
|
{typebot && (
|
||||||
<EditableUrl
|
<EditableUrl
|
||||||
hostname={
|
hostname={
|
||||||
process.env.NEXT_PUBLIC_VIEWER_HOST ?? 'https://typebot.io'
|
process.env.NEXT_PUBLIC_VIEWER_URL ?? 'https://typebot.io'
|
||||||
}
|
}
|
||||||
pathname={publicId}
|
pathname={publicId}
|
||||||
onPathnameChange={handlePublicIdChange}
|
onPathnameChange={handlePublicIdChange}
|
||||||
|
@ -20,7 +20,9 @@ export const SpreadsheetsDropdown = ({
|
|||||||
})
|
})
|
||||||
const { spreadsheets, isLoading } = useSpreadsheets({
|
const { spreadsheets, isLoading } = useSpreadsheets({
|
||||||
credentialsId,
|
credentialsId,
|
||||||
onError: (e) => toast({ title: e.name, description: e.message }),
|
onError: (e) =>
|
||||||
|
!toast.isActive('spreadsheets') &&
|
||||||
|
toast({ id: 'spreadsheets', title: e.name, description: e.message }),
|
||||||
})
|
})
|
||||||
const currentSpreadsheet = useMemo(
|
const currentSpreadsheet = useMemo(
|
||||||
() => spreadsheets?.find((s) => s.id === spreadsheetId),
|
() => spreadsheets?.find((s) => s.id === spreadsheetId),
|
||||||
|
@ -69,9 +69,9 @@ export const SendEmailSettings = ({ options, onOptionsChange }: Props) => {
|
|||||||
currentCredentialsId={options.credentialsId}
|
currentCredentialsId={options.credentialsId}
|
||||||
onCredentialsSelect={handleCredentialsSelect}
|
onCredentialsSelect={handleCredentialsSelect}
|
||||||
onCreateNewClick={onOpen}
|
onCreateNewClick={onOpen}
|
||||||
defaultCredentialLabel={
|
defaultCredentialLabel={process.env.NEXT_PUBLIC_SMTP_FROM?.match(
|
||||||
process.env.NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_EMAIL
|
/\<(.*)\>/
|
||||||
}
|
)?.pop()}
|
||||||
refreshDropdownKey={refreshCredentialsKey}
|
refreshDropdownKey={refreshCredentialsKey}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -78,7 +78,6 @@ export const SmtpConfigModal = ({
|
|||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button
|
<Button
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
mr={3}
|
|
||||||
onClick={handleCreateClick}
|
onClick={handleCreateClick}
|
||||||
isDisabled={
|
isDisabled={
|
||||||
isNotDefined(smtpConfig.from.email) ||
|
isNotDefined(smtpConfig.from.email) ||
|
||||||
@ -91,7 +90,6 @@ export const SmtpConfigModal = ({
|
|||||||
>
|
>
|
||||||
Create
|
Create
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost">Close</Button>
|
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -2,6 +2,7 @@ import { useTypebot } from 'contexts/TypebotContext'
|
|||||||
import { useUser } from 'contexts/UserContext'
|
import { useUser } from 'contexts/UserContext'
|
||||||
import { Plan } from 'db'
|
import { Plan } from 'db'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
|
import { isCloudProdInstance } from 'services/utils'
|
||||||
import { initBubble } from 'typebot-js'
|
import { initBubble } from 'typebot-js'
|
||||||
|
|
||||||
export const SupportBubble = () => {
|
export const SupportBubble = () => {
|
||||||
@ -9,20 +10,21 @@ export const SupportBubble = () => {
|
|||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initBubble({
|
if (isCloudProdInstance())
|
||||||
publishId: 'typebot-support',
|
initBubble({
|
||||||
viewerHost: process.env.NEXT_PUBLIC_VIEWER_HOST,
|
publishId: 'typebot-support',
|
||||||
backgroundColor: '#ffffff',
|
viewerHost: process.env.NEXT_PUBLIC_VIEWER_URL,
|
||||||
button: { color: '#0042DA' },
|
backgroundColor: '#ffffff',
|
||||||
hiddenVariables: {
|
button: { color: '#0042DA' },
|
||||||
'User ID': user?.id,
|
hiddenVariables: {
|
||||||
Name: user?.name ?? undefined,
|
'User ID': user?.id,
|
||||||
Email: user?.email ?? undefined,
|
Name: user?.name ?? undefined,
|
||||||
'Typebot ID': typebot?.id,
|
Email: user?.email ?? undefined,
|
||||||
'Avatar URL': user?.image ?? undefined,
|
'Typebot ID': typebot?.id,
|
||||||
Plan: planToReadable(user?.plan),
|
'Avatar URL': user?.image ?? undefined,
|
||||||
},
|
Plan: planToReadable(user?.plan),
|
||||||
})
|
},
|
||||||
|
})
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [user, typebot])
|
}, [user, typebot])
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { decrypt, encrypt } from 'utils'
|
|||||||
import prisma from './prisma'
|
import prisma from './prisma'
|
||||||
|
|
||||||
export const oauth2Client = new OAuth2Client(
|
export const oauth2Client = new OAuth2Client(
|
||||||
process.env.GOOGLE_CLIENT_ID,
|
process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
|
||||||
process.env.GOOGLE_CLIENT_SECRET,
|
process.env.GOOGLE_CLIENT_SECRET,
|
||||||
`${process.env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
`${process.env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
||||||
)
|
)
|
||||||
|
@ -23,11 +23,6 @@ class MyDocument extends Document {
|
|||||||
href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&family=Open+Sans:wght@400;500;600;700&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&family=Open+Sans:wght@400;500;600;700&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<script
|
|
||||||
defer
|
|
||||||
data-domain="app.typebot.io"
|
|
||||||
src="https://plausible.baptistearno.com/js/plausible.js"
|
|
||||||
/>
|
|
||||||
</Head>
|
</Head>
|
||||||
<body>
|
<body>
|
||||||
<Main />
|
<Main />
|
||||||
|
@ -10,40 +10,51 @@ import { withSentry } from '@sentry/nextjs'
|
|||||||
import { CustomAdapter } from './adapter'
|
import { CustomAdapter } from './adapter'
|
||||||
import { User } from 'db'
|
import { User } from 'db'
|
||||||
|
|
||||||
const providers: Provider[] = [
|
const providers: Provider[] = []
|
||||||
EmailProvider({
|
|
||||||
server: {
|
|
||||||
host: process.env.AUTH_EMAIL_SERVER_HOST,
|
|
||||||
port: Number(process.env.AUTH_EMAIL_SERVER_PORT),
|
|
||||||
auth: {
|
|
||||||
user: process.env.AUTH_EMAIL_SERVER_USER,
|
|
||||||
pass: process.env.AUTH_EMAIL_SERVER_PASSWORD,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
from: `"${process.env.AUTH_EMAIL_FROM_NAME}" <${process.env.AUTH_EMAIL_FROM_EMAIL}>`,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET)
|
providers.push(
|
||||||
|
GitHubProvider({
|
||||||
|
clientId:
|
||||||
|
process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID ?? '534b549dd17709a743a2',
|
||||||
|
clientSecret:
|
||||||
|
process.env.GITHUB_CLIENT_SECRET ??
|
||||||
|
'7adb03507504fb1a54422f6c3c697277cfd000a9',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
if (process.env.NEXT_PUBLIC_SMTP_FROM)
|
||||||
providers.push(
|
providers.push(
|
||||||
GitHubProvider({
|
EmailProvider({
|
||||||
clientId: process.env.GITHUB_CLIENT_ID,
|
server: {
|
||||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
host: process.env.SMTP_HOST,
|
||||||
|
port: process.env.SMTP_PORT ? Number(process.env.SMTP_PORT) : 25,
|
||||||
|
auth: {
|
||||||
|
user: process.env.SMTP_USERNAME,
|
||||||
|
pass: process.env.SMTP_PASSWORD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
from: process.env.NEXT_PUBLIC_SMTP_FROM,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET)
|
if (
|
||||||
|
process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID &&
|
||||||
|
process.env.GOOGLE_CLIENT_SECRET
|
||||||
|
)
|
||||||
providers.push(
|
providers.push(
|
||||||
GoogleProvider({
|
GoogleProvider({
|
||||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
|
||||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET)
|
if (
|
||||||
|
process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID &&
|
||||||
|
process.env.FACEBOOK_CLIENT_SECRET
|
||||||
|
)
|
||||||
providers.push(
|
providers.push(
|
||||||
FacebookProvider({
|
FacebookProvider({
|
||||||
clientId: process.env.FACEBOOK_CLIENT_ID,
|
clientId: process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID,
|
||||||
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
|
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -19,12 +19,11 @@ const handler = async (
|
|||||||
if (
|
if (
|
||||||
!process.env.S3_ENDPOINT ||
|
!process.env.S3_ENDPOINT ||
|
||||||
!process.env.S3_ACCESS_KEY ||
|
!process.env.S3_ACCESS_KEY ||
|
||||||
!process.env.S3_SECRET_KEY ||
|
!process.env.S3_SECRET_KEY
|
||||||
!process.env.S3_BUCKET
|
|
||||||
)
|
)
|
||||||
return res.send({
|
return res.send({
|
||||||
message:
|
message:
|
||||||
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_ACCESS_KEY, S3_BUCKET',
|
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY',
|
||||||
})
|
})
|
||||||
|
|
||||||
const s3 = new Client({
|
const s3 = new Client({
|
||||||
@ -40,7 +39,7 @@ const handler = async (
|
|||||||
const filePath = req.query.filePath as string | undefined
|
const filePath = req.query.filePath as string | undefined
|
||||||
if (!filePath) return badRequest(res)
|
if (!filePath) return badRequest(res)
|
||||||
const presignedUrl = await s3.presignedPutObject(
|
const presignedUrl = await s3.presignedPutObject(
|
||||||
process.env.S3_BUCKET as string,
|
process.env.S3_BUCKET ?? 'typebot',
|
||||||
filePath
|
filePath
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import { NextPageContext } from 'next/types'
|
|||||||
|
|
||||||
const DashboardPage = () => {
|
const DashboardPage = () => {
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [showBanner, setShowBanner] = useState(false)
|
||||||
const { query, isReady } = useRouter()
|
const { query, isReady } = useRouter()
|
||||||
const { user } = useUser()
|
const { user } = useUser()
|
||||||
const toast = useToast({
|
const toast = useToast({
|
||||||
@ -37,6 +38,9 @@ const DashboardPage = () => {
|
|||||||
const couponCode = query.coupon?.toString()
|
const couponCode = query.coupon?.toString()
|
||||||
const stripeStatus = query.stripe?.toString()
|
const stripeStatus = query.stripe?.toString()
|
||||||
|
|
||||||
|
if (document.location.hostname.includes('app.typebot.io'))
|
||||||
|
setShowBanner(true)
|
||||||
|
|
||||||
if (stripeStatus === 'success')
|
if (stripeStatus === 'success')
|
||||||
toast({
|
toast({
|
||||||
title: 'Typebot Pro',
|
title: 'Typebot Pro',
|
||||||
@ -53,14 +57,16 @@ const DashboardPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack minH="100vh">
|
<Stack minH="100vh">
|
||||||
<Banner id={'v1-navigation'}>
|
{showBanner && (
|
||||||
<Text>
|
<Banner id={'v1-navigation'}>
|
||||||
You are on Typebot 2.0. To access the old version, navigate to
|
<Text>
|
||||||
</Text>
|
You are on Typebot 2.0. To access the old version, navigate to
|
||||||
<Link href="https://old.typebot.io" isExternal textDecor="underline">
|
</Text>
|
||||||
https://old.typebot.io
|
<Link href="https://old.typebot.io" isExternal textDecor="underline">
|
||||||
</Link>
|
https://old.typebot.io
|
||||||
</Banner>
|
</Link>
|
||||||
|
</Banner>
|
||||||
|
)}
|
||||||
<Seo title="My typebots" />
|
<Seo title="My typebots" />
|
||||||
<DashboardHeader />
|
<DashboardHeader />
|
||||||
<TypebotDndContext>
|
<TypebotDndContext>
|
||||||
|
@ -29,7 +29,7 @@ test.describe('Send email step', () => {
|
|||||||
await page.goto(`/typebots/${typebotId}/edit`)
|
await page.goto(`/typebots/${typebotId}/edit`)
|
||||||
await page.click('text=Configure...')
|
await page.click('text=Configure...')
|
||||||
await page.click(
|
await page.click(
|
||||||
`text=${process.env.NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_EMAIL}`
|
`text=${process.env.NEXT_PUBLIC_SMTP_FROM?.match(/\<(.*)\>/)?.pop()}`
|
||||||
)
|
)
|
||||||
await page.click('text=Connect new')
|
await page.click('text=Connect new')
|
||||||
const createButton = page.locator('button >> text=Create')
|
const createButton = page.locator('button >> text=Create')
|
||||||
|
@ -10,16 +10,16 @@ export const sendEmailNotification = ({
|
|||||||
content: string
|
content: string
|
||||||
}) => {
|
}) => {
|
||||||
const transporter = createTransport({
|
const transporter = createTransport({
|
||||||
host: process.env.AUTH_EMAIL_SERVER_HOST,
|
host: process.env.SMTP_HOST,
|
||||||
port: Number(process.env.AUTH_EMAIL_SERVER_PORT),
|
port: Number(process.env.SMTP_PORT),
|
||||||
auth: {
|
auth: {
|
||||||
user: process.env.AUTH_EMAIL_SERVER_USER,
|
user: process.env.SMTP_USERNAME,
|
||||||
pass: process.env.AUTH_EMAIL_SERVER_PASSWORD,
|
pass: process.env.SMTP_PASSWORD,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return transporter.sendMail({
|
return transporter.sendMail({
|
||||||
from: `"${process.env.AUTH_EMAIL_FROM_NAME}" <${process.env.AUTH_EMAIL_FROM_EMAIL}>`,
|
from: process.env.NEXT_PUBLIC_SMTP_FROM,
|
||||||
to,
|
to,
|
||||||
subject,
|
subject,
|
||||||
html: content,
|
html: content,
|
||||||
|
@ -77,7 +77,7 @@ export const executeWebhook = (
|
|||||||
{ blockId, stepId }: { blockId: string; stepId: string }
|
{ blockId, stepId }: { blockId: string; stepId: string }
|
||||||
) =>
|
) =>
|
||||||
sendRequest<WebhookResponse>({
|
sendRequest<WebhookResponse>({
|
||||||
url: `${process.env.NEXT_PUBLIC_VIEWER_HOST}/api/typebots/${typebotId}/blocks/${blockId}/steps/${stepId}/executeWebhook`,
|
url: `${process.env.NEXT_PUBLIC_VIEWER_URL}/api/typebots/${typebotId}/blocks/${blockId}/steps/${stepId}/executeWebhook`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
variables,
|
variables,
|
||||||
|
@ -135,3 +135,6 @@ export const timeSince = (date: string) => {
|
|||||||
}
|
}
|
||||||
return Math.floor(seconds) + ' seconds'
|
return Math.floor(seconds) + ' seconds'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isCloudProdInstance = () =>
|
||||||
|
window.location.hostname === 'app.typebot.io'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"label": "Self hosting",
|
"label": "Self-Hosting",
|
||||||
"position": 6
|
"position": 6
|
||||||
}
|
}
|
||||||
|
177
apps/docs/docs/self-hosting/configuration.md
Normal file
177
apps/docs/docs/self-hosting/configuration.md
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
:::note
|
||||||
|
The easiest way to get started with Typebot is with [the official managed service in the Cloud](https://app.typebot.io). It takes 1 minute to try out the tool for free. You'll have high availability, backups, security, and maintenance all managed for you by me, Baptiste, Typebot's founder.
|
||||||
|
|
||||||
|
That's also the best way to support my work, open-source software, and you'll get great service!
|
||||||
|
:::
|
||||||
|
|
||||||
|
When running a Typebot on your machine, the following configuration parameters can be supplied as environment variables.
|
||||||
|
|
||||||
|
## Builder & Viewer
|
||||||
|
|
||||||
|
These variables are shared between builder and viewer. If you host them in a different environments then it needs to be set in both environments.
|
||||||
|
|
||||||
|
### Global
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ---------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| DATABASE_URL | -- | The database URL, i.e. for external db server `postgres://user:password@ip.or.domain.to.server/database_name` |
|
||||||
|
| ENCRYPTION_SECRET | SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6 | A 256-bit key used to encrypt sensitive data. It is strongly recommended to [generate](https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx) a new one. |
|
||||||
|
| ADMIN_EMAIL | -- | The email that will get a "Pro" plan on user creation |
|
||||||
|
| NEXTAUTH_URL | http://localhost:3000 | The builder base URL |
|
||||||
|
| NEXT_PUBLIC_VIEWER_URL | http://localhost:3001 | The viewer base URL |
|
||||||
|
|
||||||
|
### SMTP (optional)
|
||||||
|
|
||||||
|
Used for sending email notifications and authentication
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| --------------------- | ------- | ------------------------------------------------------------------------------- |
|
||||||
|
| SMTP_USERNAME | -- | SMTP username |
|
||||||
|
| SMTP_PASSWORD | -- | SMTP password |
|
||||||
|
| SMTP_HOST | -- | SMTP host. (i.e. `smtp.host.com`) |
|
||||||
|
| SMTP_PORT | 25 | SMTP port |
|
||||||
|
| NEXT_PUBLIC_SMTP_FROM | - | From name and email (i.e. `"Typebot Notifications" <notifications@typebot.io>`) |
|
||||||
|
|
||||||
|
### Google (optional)
|
||||||
|
|
||||||
|
Used authentication in the builder and for the Google Sheets integration step. Make sure to set the required scopes (`userinfo.email`, `spreadsheets`, `drive.readonly`) in your console
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ---------------------------- | ------- | --------------------------------------------- |
|
||||||
|
| NEXT_PUBLIC_GOOGLE_CLIENT_ID | -- | The Client ID from the Google API Console |
|
||||||
|
| GOOGLE_CLIENT_SECRET | -- | The Client secret from the Google API Console |
|
||||||
|
|
||||||
|
## Builder
|
||||||
|
|
||||||
|
The following variables are only used for the builder.
|
||||||
|
|
||||||
|
### GitHub (optional)
|
||||||
|
|
||||||
|
Used for authenticating with GitHub. By default, it uses the credentials of a Typebot-dev app.
|
||||||
|
|
||||||
|
You can create your own GitHub OAuth app [here](https://github.com/settings/developers).
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ---------------------------- | ---------------------------------------- | --------------------------------------------------------------------------- |
|
||||||
|
| NEXT_PUBLIC_GITHUB_CLIENT_ID | 534b549dd17709a743a2 | Application client ID. Also used to check if it is enabled in the front-end |
|
||||||
|
| GITHUB_CLIENT_SECRET | 7adb03507504fb1a54422f6c3c697277cfd000a9 | Application secret |
|
||||||
|
|
||||||
|
### Facebook (optional)
|
||||||
|
|
||||||
|
You can create your own Facebook OAuth app [here](https://developers.facebook.com/apps/create/).
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ------------------------------ | ------- | --------------------------------------------------------------------------- |
|
||||||
|
| NEXT_PUBLIC_FACEBOOK_CLIENT_ID | -- | Application client ID. Also used to check if it is enabled in the front-end |
|
||||||
|
| FACEBOOK_CLIENT_SECRET | -- | Application secret |
|
||||||
|
|
||||||
|
### S3 Storage (optional)
|
||||||
|
|
||||||
|
Used for uploading images, videos, etc... It can be any S3 compatible object storage service (Minio, Digital Oceans Space, AWS S3...)
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ------------- | ------- | -------------------------------------------------------------- |
|
||||||
|
| S3_ACCESS_KEY | -- | S3 access key. Also used to check if upload feature is enabled |
|
||||||
|
| S3_SECRET_KEY | -- | S3 secret key. |
|
||||||
|
| S3_BUCKET | typebot | Name of the bucket where assets will be uploaded in. |
|
||||||
|
| S3_PORT | -- | S3 Host port number |
|
||||||
|
| S3_ENDPOINT | -- | S3 secret key. |
|
||||||
|
| S3_SSL | true | Use SSL when establishing the connection. |
|
||||||
|
| S3_REGION | -- | S3 region. |
|
||||||
|
|
||||||
|
Your bucket must have the following policy that tells S3 to allow public read when an object is located under the public folder:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid": "PublicRead",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": "*",
|
||||||
|
"Action": "s3:GetObject",
|
||||||
|
"Resource": "arn:aws:s3:::<BUCKET_NAME>/public/*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You also need to configure CORS so that an object can be uploaded from the browser:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"AllowedHeaders": ["*"],
|
||||||
|
"AllowedMethods": ["PUT", "POST"],
|
||||||
|
"AllowedOrigins": ["*"],
|
||||||
|
"ExposeHeaders": ["ETag"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Giphy (optional)
|
||||||
|
|
||||||
|
Used to search for GIF. You can create a Giphy app [here](https://developers.giphy.com/dashboard/)
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ------------------------- | ------- | ------------- |
|
||||||
|
| NEXT_PUBLIC_GIPHY_API_KEY | -- | Giphy API key |
|
||||||
|
|
||||||
|
### Others (optional)
|
||||||
|
|
||||||
|
The [official Typebot managed service](https://app.typebot.io/) uses other services such as [Stripe](https://stripe.com/) for processing payments, [Sentry](https://sentry.io/) for tracking bugs and [Sleekplan](https://sleekplan.com/) for user feedbacks.
|
||||||
|
|
||||||
|
The related environment variables are listed here but you are probably not interested in these if you self-host Typebot.
|
||||||
|
|
||||||
|
<details><summary>Stripe</summary>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ----------------------------- | ------- | --------------------- |
|
||||||
|
| NEXT_PUBLIC_STRIPE_PUBLIC_KEY | -- | Stripe public key |
|
||||||
|
| STRIPE_SECRET_KEY | -- | Stripe secret key |
|
||||||
|
| STRIPE_PRICE_USD_ID | -- | Pro plan USD price id |
|
||||||
|
| STRIPE_PRICE_EUR_ID | -- | Pro plan EUR price id |
|
||||||
|
| STRIPE_WEBHOOK_SECRET | -- | Stripe Webhook secret |
|
||||||
|
|
||||||
|
</p></details>
|
||||||
|
|
||||||
|
<details><summary>Sentry</summary>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ---------------------- | ------- | -------------------------------------- |
|
||||||
|
| NEXT_PUBLIC_SENTRY_DSN | -- | Sentry DSN |
|
||||||
|
| SENTRY_AUTH_TOKEN | -- | Used to upload sourcemaps on app build |
|
||||||
|
| SENTRY_PROJECT | -- | Sentry project name |
|
||||||
|
| SENTRY_ORG | -- | Sentry organization name |
|
||||||
|
|
||||||
|
These can also be added to the `viewer` environment
|
||||||
|
|
||||||
|
</p></details>
|
||||||
|
|
||||||
|
<details><summary>Vercel (custom domains)</summary>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| -------------------------- | ------- | ----------------------------------------------- |
|
||||||
|
| VERCEL_TOKEN | -- | Vercel API token |
|
||||||
|
| VERCEL_VIEWER_PROJECT_NAME | -- | The name of the viewer project in Vercel |
|
||||||
|
| VERCEL_TEAM_ID | -- | Vercel team ID that contains the viewer project |
|
||||||
|
|
||||||
|
</p></details>
|
||||||
|
|
||||||
|
<details><summary>Sleekplan</summary>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
| Parameter | Default | Description |
|
||||||
|
| ----------------- | ------- | ------------------------------------------------------------------------ |
|
||||||
|
| SLEEKPLAN_SSO_KEY | -- | Sleekplan SSO key used to automatically authenticate a user in Sleekplan |
|
||||||
|
|
||||||
|
</p></details>
|
44
apps/docs/docs/self-hosting/docker.md
Normal file
44
apps/docs/docs/self-hosting/docker.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
|
||||||
|
:::note
|
||||||
|
The easiest way to get started with Typebot is with [the official managed service in the Cloud](https://app.typebot.io). It takes 1 minute to try out the tool for free. You'll have high availability, backups, security, and maintenance all managed for you by me, Baptiste, Typebot's founder.
|
||||||
|
|
||||||
|
That's also the best way to support my work, open-source software, and you'll get great service!
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
You need a server with Docker installed. If your server doesn't come with Docker pre-installed, you can follow [their docs](https://docs.docker.com/get-docker/) to install it.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
1. Fork the repository
|
||||||
|
|
||||||
|
On your server:
|
||||||
|
|
||||||
|
2. Clone the forked repo:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/<your-fork>/typebot.io.git
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Edit the `typebot-config.env` file. ([Check out the configuration guide](/self-hosting/configuration))
|
||||||
|
|
||||||
|
4. Start the applications:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
It does the following:
|
||||||
|
|
||||||
|
- Create a database
|
||||||
|
- Run the migrations
|
||||||
|
- Start the builder on port 8080
|
||||||
|
- Start the viewer on port 8081
|
||||||
|
|
||||||
|
You should see the login screen if you navigate to `http://{hostname}:8080`. Login with the `${ADMIN_EMAIL}` in order to have access to a Pro account automatically.
|
22
apps/docs/docs/self-hosting/get-started.md
Normal file
22
apps/docs/docs/self-hosting/get-started.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
slug: /self-hosting
|
||||||
|
---
|
||||||
|
|
||||||
|
# Getting started
|
||||||
|
|
||||||
|
:::note
|
||||||
|
The easiest way to get started with Typebot is with [the official managed service in the Cloud](https://app.typebot.io). It takes 1 minute to try out the tool for free. You'll have high availability, backups, security, and maintenance all managed for you by me, Baptiste, Typebot's founder.
|
||||||
|
|
||||||
|
That's also the best way to support my work, open-source software, and you'll get great service!
|
||||||
|
:::
|
||||||
|
|
||||||
|
Typebot is composed of 2 Next.js applications you need to deploy:
|
||||||
|
|
||||||
|
- the builder, where you build your typebots
|
||||||
|
- the viewer, where your user answer the typebot
|
||||||
|
|
||||||
|
I've written guides on how to deploy Typebot using:
|
||||||
|
|
||||||
|
- [Docker](/self-hosting/docker)
|
||||||
|
- [Vercel](/self-hosting/vercel)
|
@ -1,53 +0,0 @@
|
|||||||
# Self hosting
|
|
||||||
|
|
||||||
:::note
|
|
||||||
The easiest way to get started with Typebot is with [our official managed service in the Cloud](https://app.typebot.io). It takes 1 minute to create your free account. You'll have high availability, backups, security, and maintenance all managed for you by Typebot. The section below is for self-hosting Typebot on your server and managing your infrastructure.
|
|
||||||
:::
|
|
||||||
|
|
||||||
Typebot is composed of 2 Next.js applications you need to deploy:
|
|
||||||
|
|
||||||
- the builder, where you build your typebots
|
|
||||||
- the viewer, where your user answer the typebot
|
|
||||||
|
|
||||||
They are connected to a Database and an S3 bucket
|
|
||||||
|
|
||||||
## S3
|
|
||||||
|
|
||||||
Paste this bucket policy after replacing `<BUCKET_NAME>` with your bucket name:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"Version": "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Sid": "PublicRead",
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": "*",
|
|
||||||
"Action": "s3:GetObject",
|
|
||||||
"Resource": "arn:aws:s3:::<BUCKET_NAME>/public/*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
CORS config:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"AllowedHeaders": ["*"],
|
|
||||||
"AllowedMethods": ["PUT", "POST"],
|
|
||||||
"AllowedOrigins": ["*"],
|
|
||||||
"ExposeHeaders": ["ETag"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
S3_ACCESS_KEY=minio
|
|
||||||
S3_SECRET_KEY=minio123
|
|
||||||
S3_BUCKET=typebot
|
|
||||||
S3_PORT=9000
|
|
||||||
S3_ENDPOINT=localhost
|
|
||||||
S3_SSL=false
|
|
||||||
```
|
|
49
apps/docs/docs/self-hosting/vercel.md
Normal file
49
apps/docs/docs/self-hosting/vercel.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Vercel
|
||||||
|
|
||||||
|
:::note
|
||||||
|
The easiest way to get started with Typebot is with [the official managed service in the Cloud](https://app.typebot.io). It takes 1 minute to try out the tool for free. You'll have high availability, backups, security, and maintenance all managed for you by me, Baptiste, Typebot's founder.
|
||||||
|
|
||||||
|
That's also the best way to support my work, open-source software, and you'll get great service!
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
You need a PostgresDB database hosted somewhere. [Supabase](https://supabase.com/) and [Heroku](https://www.heroku.com/) offer great free options.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
Fork the repository
|
||||||
|
|
||||||
|
### Deploy the builder
|
||||||
|
|
||||||
|
1. Create a new Vercel project and import the forked repo
|
||||||
|
2. Change the project name to: `typebot-builder` (or anything else)
|
||||||
|
3. Choose Next.js framework
|
||||||
|
4. Change the root directory to: `apps/builder`
|
||||||
|
5. Change the build command to:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd ../.. && yarn db:migrate && yarn turbo run build --scope=builder --include-dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Add the required environment variables ([Check out the configuration guide](/self-hosting/configuration))
|
||||||
|
7. Hit "Deploy"
|
||||||
|
|
||||||
|
### Deploy the viewer
|
||||||
|
|
||||||
|
1. Create a new Vercel project and import the forked repo
|
||||||
|
2. Change the project name to: `typebot-viewer` (or anything else)
|
||||||
|
3. Choose Next.js framework
|
||||||
|
4. Change the root directory to: `apps/viewer`
|
||||||
|
5. Change the build command to:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd ../.. && yarn db:migrate && yarn turbo run build --scope=viewer --include-dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Add the required environment variables ([Check out the configuration guide](/self-hosting/configuration))
|
||||||
|
7. Hit "Deploy"
|
@ -80,7 +80,8 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
colorMode: {
|
colorMode: {
|
||||||
disableSwitch: true,
|
disableSwitch: false,
|
||||||
|
respectPrefersColorScheme: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
presets: [
|
presets: [
|
||||||
|
@ -21,17 +21,6 @@ h4 {
|
|||||||
img {
|
img {
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
/* You can override the default Infima variables here. */
|
|
||||||
:root {
|
|
||||||
--ifm-color-primary: #0042da;
|
|
||||||
--ifm-color-primary-dark: #003bc4;
|
|
||||||
--ifm-color-primary-darker: #0038b9;
|
|
||||||
--ifm-color-primary-darkest: #002e99;
|
|
||||||
--ifm-color-primary-light: #0049f0;
|
|
||||||
--ifm-color-primary-lighter: #004cfb;
|
|
||||||
--ifm-color-primary-lightest: #1c61ff;
|
|
||||||
--ifm-code-font-size: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.docusaurus-highlight-code-line {
|
.docusaurus-highlight-code-line {
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
NOTION_API_TOKEN=
|
NOTION_API_TOKEN=
|
||||||
NOTION_DATABASE_ID=
|
NOTION_DATABASE_ID=
|
||||||
NEXT_PUBLIC_VIEWER_HOST=http://localhost:3001
|
NEXT_PUBLIC_VIEWER_URL=http://localhost:3001
|
||||||
LANDING_PAGE_HOST=http://localhost:3002
|
LANDING_PAGE_HOST=http://localhost:3002
|
@ -35,7 +35,7 @@ module.exports = withBundleAnalyzer({
|
|||||||
{
|
{
|
||||||
source: '/_next/static/:static*',
|
source: '/_next/static/:static*',
|
||||||
destination:
|
destination:
|
||||||
process.env.NEXT_PUBLIC_VIEWER_HOST + '/_next/static/:static*',
|
process.env.NEXT_PUBLIC_VIEWER_URL + '/_next/static/:static*',
|
||||||
has: [
|
has: [
|
||||||
{
|
{
|
||||||
type: 'header',
|
type: 'header',
|
||||||
@ -52,11 +52,11 @@ module.exports = withBundleAnalyzer({
|
|||||||
fallback: [
|
fallback: [
|
||||||
{
|
{
|
||||||
source: '/:typebotId*',
|
source: '/:typebotId*',
|
||||||
destination: process.env.NEXT_PUBLIC_VIEWER_HOST + '/:typebotId*',
|
destination: process.env.NEXT_PUBLIC_VIEWER_URL + '/:typebotId*',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
source: '/api/:path*',
|
source: '/api/:path*',
|
||||||
destination: process.env.NEXT_PUBLIC_VIEWER_HOST + '/api/:path*',
|
destination: process.env.NEXT_PUBLIC_VIEWER_URL + '/api/:path*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,6 @@
|
|||||||
ENCRYPTION_SECRET=
|
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6 #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
||||||
NEXT_PUBLIC_VIEWER_HOST=http://localhost:3001
|
NEXT_PUBLIC_VIEWER_URL=http://localhost:3001
|
||||||
DATABASE_URL=postgresql://postgres:@localhost:5432/typebot
|
DATABASE_URL=postgresql://postgres:@localhost:5432/typebot
|
||||||
|
|
||||||
GOOGLE_CLIENT_ID=
|
# For more configuration options check out:
|
||||||
GOOGLE_CLIENT_SECRET=
|
https://docs.typebot.io/self-hosting/configuration
|
||||||
|
|
||||||
# (Optional) Used for email notifications
|
|
||||||
EMAIL_NOTIFICATIONS_SERVER_USER=username
|
|
||||||
EMAIL_NOTIFICATIONS_SERVER_PASSWORD=password
|
|
||||||
EMAIL_NOTIFICATIONS_SERVER_HOST=smtp.example.com
|
|
||||||
EMAIL_NOTIFICATIONS_SERVER_PORT=587
|
|
||||||
NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_EMAIL=noreply@example.com
|
|
||||||
NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_NAME="John Smith"
|
|
||||||
|
|
||||||
NEXT_PUBLIC_SENTRY_DSN=
|
|
@ -17,7 +17,7 @@ export const getAuthenticatedGoogleClient = async (
|
|||||||
) as GoogleSheetsCredentialsData
|
) as GoogleSheetsCredentialsData
|
||||||
|
|
||||||
const oauth2Client = new OAuth2Client(
|
const oauth2Client = new OAuth2Client(
|
||||||
process.env.GOOGLE_CLIENT_ID,
|
process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
|
||||||
process.env.GOOGLE_CLIENT_SECRET,
|
process.env.GOOGLE_CLIENT_SECRET,
|
||||||
`${process.env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
`${process.env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ export const getServerSideProps: GetServerSideProps = async (
|
|||||||
try {
|
try {
|
||||||
if (!context.req.headers.host) return { props: {} }
|
if (!context.req.headers.host) return { props: {} }
|
||||||
typebot = context.req.headers.host.includes(
|
typebot = context.req.headers.host.includes(
|
||||||
(process.env.NEXT_PUBLIC_VIEWER_HOST ?? '').split('//')[1]
|
(process.env.NEXT_PUBLIC_VIEWER_URL ?? '').split('//')[1]
|
||||||
)
|
)
|
||||||
? await getTypebotFromPublicId(context.query.publicId?.toString())
|
? await getTypebotFromPublicId(context.query.publicId?.toString())
|
||||||
: await getTypebotFromCustomDomain(
|
: await getTypebotFromCustomDomain(
|
||||||
|
@ -10,18 +10,18 @@ import { withSentry } from '@sentry/nextjs'
|
|||||||
const cors = initMiddleware(Cors())
|
const cors = initMiddleware(Cors())
|
||||||
|
|
||||||
const defaultTransportOptions = {
|
const defaultTransportOptions = {
|
||||||
host: process.env.EMAIL_NOTIFICATIONS_SERVER_HOST,
|
host: process.env.SMTP_HOST,
|
||||||
port: Number(process.env.EMAIL_NOTIFICATIONS_SERVER_PORT),
|
port: Number(process.env.SMTP_PORT),
|
||||||
secure: false,
|
secure: false,
|
||||||
auth: {
|
auth: {
|
||||||
user: process.env.EMAIL_NOTIFICATIONS_SERVER_USER,
|
user: process.env.SMTP_USERNAME,
|
||||||
pass: process.env.EMAIL_NOTIFICATIONS_SERVER_PASSWORD,
|
pass: process.env.SMTP_PASSWORD,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultFrom = {
|
const defaultFrom = {
|
||||||
name: process.env.NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_NAME,
|
name: process.env.NEXT_PUBLIC_SMTP_FROM?.split(' <')[0].replace(/"/g, ''),
|
||||||
email: process.env.NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_EMAIL,
|
email: process.env.NEXT_PUBLIC_SMTP_FROM?.match(/\<(.*)\>/)?.pop(),
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
@ -17,7 +17,7 @@ const config: PlaywrightTestConfig = {
|
|||||||
maxFailures: process.env.CI ? 10 : undefined,
|
maxFailures: process.env.CI ? 10 : undefined,
|
||||||
use: {
|
use: {
|
||||||
actionTimeout: 0,
|
actionTimeout: 0,
|
||||||
baseURL: process.env.NEXT_PUBLIC_VIEWER_HOST,
|
baseURL: process.env.NEXT_PUBLIC_VIEWER_URL,
|
||||||
storageState: path.join(__dirname, 'playwright/proUser.json'),
|
storageState: path.join(__dirname, 'playwright/proUser.json'),
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
video: 'retain-on-failure',
|
video: 'retain-on-failure',
|
||||||
|
@ -33,7 +33,7 @@ export type TypebotViewerProps = {
|
|||||||
}
|
}
|
||||||
export const TypebotViewer = ({
|
export const TypebotViewer = ({
|
||||||
typebot,
|
typebot,
|
||||||
apiHost = process.env.NEXT_PUBLIC_VIEWER_HOST,
|
apiHost = process.env.NEXT_PUBLIC_VIEWER_URL,
|
||||||
isPreview = false,
|
isPreview = false,
|
||||||
onNewLog,
|
onNewLog,
|
||||||
onNewBlockVisible,
|
onNewBlockVisible,
|
||||||
@ -59,7 +59,7 @@ export const TypebotViewer = ({
|
|||||||
const handleCompleted = () => onCompleted && onCompleted()
|
const handleCompleted = () => onCompleted && onCompleted()
|
||||||
|
|
||||||
if (!apiHost)
|
if (!apiHost)
|
||||||
return <p>process.env.NEXT_PUBLIC_VIEWER_HOST is missing in env</p>
|
return <p>process.env.NEXT_PUBLIC_VIEWER_URL is missing in env</p>
|
||||||
return (
|
return (
|
||||||
<Frame
|
<Frame
|
||||||
id="typebot-iframe"
|
id="typebot-iframe"
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
# Typebot JS library
|
# Typebot JS library
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/typebot-js) [](https://bundlephobia.com/result?p=typebot-js@latest) [](https://travis-ci.com/plausible/typebot-js)
|
|
||||||
|
|
||||||
Frontend library to embed typebots from [Typebot](https://www.typebot.io/).
|
Frontend library to embed typebots from [Typebot](https://www.typebot.io/).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@ -19,9 +17,9 @@ yarn add typebot-js
|
|||||||
It exposes 3 functions:
|
It exposes 3 functions:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
initContainer();
|
initContainer()
|
||||||
initPopup();
|
initPopup()
|
||||||
initBubble();
|
initBubble()
|
||||||
```
|
```
|
||||||
|
|
||||||
You can configure them directly in the "Share" tab of your typebot.
|
You can configure them directly in the "Share" tab of your typebot.
|
||||||
@ -29,9 +27,9 @@ You can configure them directly in the "Share" tab of your typebot.
|
|||||||
Example:
|
Example:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import { initContainer } from "typebot-js";
|
import { initContainer } from 'typebot-js'
|
||||||
|
|
||||||
const plausible = initContainer("container-id", {
|
const plausible = initContainer('container-id', {
|
||||||
publishId: "my-app.com",
|
publishId: 'my-app.com',
|
||||||
});
|
})
|
||||||
```
|
```
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Shared
|
# Shared
|
||||||
# MIGRATION_DATABASE_URL=
|
# MIGRATION_DATABASE_URL=
|
||||||
DATABASE_URL=postgresql://typebot:typebot@postgres:5432/typebot
|
DATABASE_URL=postgresql://typebot:typebot@postgres:5432/typebot
|
||||||
NEXT_PUBLIC_VIEWER_HOST=http://localhost:8081
|
NEXT_PUBLIC_VIEWER_URL=http://localhost:8081
|
||||||
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
||||||
|
|
||||||
# Builder
|
# Builder
|
||||||
|
Reference in New Issue
Block a user