docs: 📝 Update env var and write Configuration doc
This commit is contained in:
@ -1,60 +1,14 @@
|
||||
DATABASE_URL=postgresql://postgres:@localhost:5432/typebot
|
||||
|
||||
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)
|
||||
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6 #256-bits secret (can be generated here: https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)
|
||||
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_SECRET_KEY=minio123
|
||||
S3_BUCKET=typebot
|
||||
S3_PORT=9000
|
||||
S3_ENDPOINT=localhost
|
||||
S3_SSL=false
|
||||
# S3_REGION=
|
||||
|
||||
# Auth
|
||||
# (Optional) Used to login using GitHub
|
||||
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=
|
||||
# For more configuration options check out:
|
||||
https://docs.typebot.io/self-hosting/configuration
|
@ -52,26 +52,30 @@ export const SignInForm = ({
|
||||
return (
|
||||
<Stack spacing="4">
|
||||
<SocialLoginButtons />
|
||||
<DividerWithText mt="6">Or with your email</DividerWithText>
|
||||
<HStack as="form" onSubmit={handleEmailSubmit}>
|
||||
<Input
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
placeholder="email@company.com"
|
||||
required
|
||||
value={emailValue}
|
||||
onChange={handleEmailChange}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
isLoading={
|
||||
['loading', 'authenticated'].includes(status) || authLoading
|
||||
}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</HStack>
|
||||
{process.env.NEXT_PUBLIC_SMTP_FROM && (
|
||||
<>
|
||||
<DividerWithText mt="6">Or with your email</DividerWithText>
|
||||
<HStack as="form" onSubmit={handleEmailSubmit}>
|
||||
<Input
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
placeholder="email@company.com"
|
||||
required
|
||||
value={emailValue}
|
||||
onChange={handleEmailChange}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
isLoading={
|
||||
['loading', 'authenticated'].includes(status) || authLoading
|
||||
}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</HStack>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
@ -36,24 +36,28 @@ export const SocialLoginButtons = () => {
|
||||
>
|
||||
Continue with GitHub
|
||||
</Button>
|
||||
<Button
|
||||
leftIcon={<GoogleLogo />}
|
||||
onClick={handleGoogleClick}
|
||||
data-testid="google"
|
||||
isLoading={['loading', 'authenticated'].includes(status)}
|
||||
variant="outline"
|
||||
>
|
||||
Continue with Google
|
||||
</Button>
|
||||
<Button
|
||||
leftIcon={<FacebookLogo />}
|
||||
onClick={handleFacebookClick}
|
||||
data-testid="facebook"
|
||||
isLoading={['loading', 'authenticated'].includes(status)}
|
||||
variant="outline"
|
||||
>
|
||||
Continue with Facebook
|
||||
</Button>
|
||||
{process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID && (
|
||||
<Button
|
||||
leftIcon={<GoogleLogo />}
|
||||
onClick={handleGoogleClick}
|
||||
data-testid="google"
|
||||
isLoading={['loading', 'authenticated'].includes(status)}
|
||||
variant="outline"
|
||||
>
|
||||
Continue with Google
|
||||
</Button>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID && (
|
||||
<Button
|
||||
leftIcon={<FacebookLogo />}
|
||||
onClick={handleFacebookClick}
|
||||
data-testid="facebook"
|
||||
isLoading={['loading', 'authenticated'].includes(status)}
|
||||
variant="outline"
|
||||
>
|
||||
Continue with Facebook
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ export const ShareContent = () => {
|
||||
{typebot && (
|
||||
<EditableUrl
|
||||
hostname={
|
||||
process.env.NEXT_PUBLIC_VIEWER_HOST ?? 'https://typebot.io'
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL ?? 'https://typebot.io'
|
||||
}
|
||||
pathname={publicId}
|
||||
onPathnameChange={handlePublicIdChange}
|
||||
|
@ -20,7 +20,9 @@ export const SpreadsheetsDropdown = ({
|
||||
})
|
||||
const { spreadsheets, isLoading } = useSpreadsheets({
|
||||
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(
|
||||
() => spreadsheets?.find((s) => s.id === spreadsheetId),
|
||||
|
@ -69,9 +69,9 @@ export const SendEmailSettings = ({ options, onOptionsChange }: Props) => {
|
||||
currentCredentialsId={options.credentialsId}
|
||||
onCredentialsSelect={handleCredentialsSelect}
|
||||
onCreateNewClick={onOpen}
|
||||
defaultCredentialLabel={
|
||||
process.env.NEXT_PUBLIC_EMAIL_NOTIFICATIONS_FROM_EMAIL
|
||||
}
|
||||
defaultCredentialLabel={process.env.NEXT_PUBLIC_SMTP_FROM?.match(
|
||||
/\<(.*)\>/
|
||||
)?.pop()}
|
||||
refreshDropdownKey={refreshCredentialsKey}
|
||||
/>
|
||||
</Stack>
|
||||
|
@ -78,7 +78,6 @@ export const SmtpConfigModal = ({
|
||||
<ModalFooter>
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
mr={3}
|
||||
onClick={handleCreateClick}
|
||||
isDisabled={
|
||||
isNotDefined(smtpConfig.from.email) ||
|
||||
@ -91,7 +90,6 @@ export const SmtpConfigModal = ({
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
<Button variant="ghost">Close</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
@ -2,6 +2,7 @@ import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { useUser } from 'contexts/UserContext'
|
||||
import { Plan } from 'db'
|
||||
import React, { useEffect } from 'react'
|
||||
import { isCloudProdInstance } from 'services/utils'
|
||||
import { initBubble } from 'typebot-js'
|
||||
|
||||
export const SupportBubble = () => {
|
||||
@ -9,20 +10,21 @@ export const SupportBubble = () => {
|
||||
const { user } = useUser()
|
||||
|
||||
useEffect(() => {
|
||||
initBubble({
|
||||
publishId: 'typebot-support',
|
||||
viewerHost: process.env.NEXT_PUBLIC_VIEWER_HOST,
|
||||
backgroundColor: '#ffffff',
|
||||
button: { color: '#0042DA' },
|
||||
hiddenVariables: {
|
||||
'User ID': user?.id,
|
||||
Name: user?.name ?? undefined,
|
||||
Email: user?.email ?? undefined,
|
||||
'Typebot ID': typebot?.id,
|
||||
'Avatar URL': user?.image ?? undefined,
|
||||
Plan: planToReadable(user?.plan),
|
||||
},
|
||||
})
|
||||
if (isCloudProdInstance())
|
||||
initBubble({
|
||||
publishId: 'typebot-support',
|
||||
viewerHost: process.env.NEXT_PUBLIC_VIEWER_URL,
|
||||
backgroundColor: '#ffffff',
|
||||
button: { color: '#0042DA' },
|
||||
hiddenVariables: {
|
||||
'User ID': user?.id,
|
||||
Name: user?.name ?? undefined,
|
||||
Email: user?.email ?? undefined,
|
||||
'Typebot ID': typebot?.id,
|
||||
'Avatar URL': user?.image ?? undefined,
|
||||
Plan: planToReadable(user?.plan),
|
||||
},
|
||||
})
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [user, typebot])
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { decrypt, encrypt } from 'utils'
|
||||
import prisma from './prisma'
|
||||
|
||||
export const oauth2Client = new OAuth2Client(
|
||||
process.env.GOOGLE_CLIENT_ID,
|
||||
process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
|
||||
process.env.GOOGLE_CLIENT_SECRET,
|
||||
`${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"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script
|
||||
defer
|
||||
data-domain="app.typebot.io"
|
||||
src="https://plausible.baptistearno.com/js/plausible.js"
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
|
@ -10,40 +10,51 @@ import { withSentry } from '@sentry/nextjs'
|
||||
import { CustomAdapter } from './adapter'
|
||||
import { User } from 'db'
|
||||
|
||||
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}>`,
|
||||
}),
|
||||
]
|
||||
const providers: Provider[] = []
|
||||
|
||||
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(
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
EmailProvider({
|
||||
server: {
|
||||
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(
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID,
|
||||
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(
|
||||
FacebookProvider({
|
||||
clientId: process.env.FACEBOOK_CLIENT_ID,
|
||||
clientId: process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID,
|
||||
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
|
||||
})
|
||||
)
|
||||
|
@ -19,12 +19,11 @@ const handler = async (
|
||||
if (
|
||||
!process.env.S3_ENDPOINT ||
|
||||
!process.env.S3_ACCESS_KEY ||
|
||||
!process.env.S3_SECRET_KEY ||
|
||||
!process.env.S3_BUCKET
|
||||
!process.env.S3_SECRET_KEY
|
||||
)
|
||||
return res.send({
|
||||
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({
|
||||
@ -40,7 +39,7 @@ const handler = async (
|
||||
const filePath = req.query.filePath as string | undefined
|
||||
if (!filePath) return badRequest(res)
|
||||
const presignedUrl = await s3.presignedPutObject(
|
||||
process.env.S3_BUCKET as string,
|
||||
process.env.S3_BUCKET ?? 'typebot',
|
||||
filePath
|
||||
)
|
||||
|
||||
|
@ -14,6 +14,7 @@ import { NextPageContext } from 'next/types'
|
||||
|
||||
const DashboardPage = () => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [showBanner, setShowBanner] = useState(false)
|
||||
const { query, isReady } = useRouter()
|
||||
const { user } = useUser()
|
||||
const toast = useToast({
|
||||
@ -37,6 +38,9 @@ const DashboardPage = () => {
|
||||
const couponCode = query.coupon?.toString()
|
||||
const stripeStatus = query.stripe?.toString()
|
||||
|
||||
if (document.location.hostname.includes('app.typebot.io'))
|
||||
setShowBanner(true)
|
||||
|
||||
if (stripeStatus === 'success')
|
||||
toast({
|
||||
title: 'Typebot Pro',
|
||||
@ -53,14 +57,16 @@ const DashboardPage = () => {
|
||||
|
||||
return (
|
||||
<Stack minH="100vh">
|
||||
<Banner id={'v1-navigation'}>
|
||||
<Text>
|
||||
You are on Typebot 2.0. To access the old version, navigate to
|
||||
</Text>
|
||||
<Link href="https://old.typebot.io" isExternal textDecor="underline">
|
||||
https://old.typebot.io
|
||||
</Link>
|
||||
</Banner>
|
||||
{showBanner && (
|
||||
<Banner id={'v1-navigation'}>
|
||||
<Text>
|
||||
You are on Typebot 2.0. To access the old version, navigate to
|
||||
</Text>
|
||||
<Link href="https://old.typebot.io" isExternal textDecor="underline">
|
||||
https://old.typebot.io
|
||||
</Link>
|
||||
</Banner>
|
||||
)}
|
||||
<Seo title="My typebots" />
|
||||
<DashboardHeader />
|
||||
<TypebotDndContext>
|
||||
|
@ -29,7 +29,7 @@ test.describe('Send email step', () => {
|
||||
await page.goto(`/typebots/${typebotId}/edit`)
|
||||
await page.click('text=Configure...')
|
||||
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')
|
||||
const createButton = page.locator('button >> text=Create')
|
||||
|
@ -10,16 +10,16 @@ export const sendEmailNotification = ({
|
||||
content: string
|
||||
}) => {
|
||||
const transporter = createTransport({
|
||||
host: process.env.AUTH_EMAIL_SERVER_HOST,
|
||||
port: Number(process.env.AUTH_EMAIL_SERVER_PORT),
|
||||
host: process.env.SMTP_HOST,
|
||||
port: Number(process.env.SMTP_PORT),
|
||||
auth: {
|
||||
user: process.env.AUTH_EMAIL_SERVER_USER,
|
||||
pass: process.env.AUTH_EMAIL_SERVER_PASSWORD,
|
||||
user: process.env.SMTP_USERNAME,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
},
|
||||
})
|
||||
|
||||
return transporter.sendMail({
|
||||
from: `"${process.env.AUTH_EMAIL_FROM_NAME}" <${process.env.AUTH_EMAIL_FROM_EMAIL}>`,
|
||||
from: process.env.NEXT_PUBLIC_SMTP_FROM,
|
||||
to,
|
||||
subject,
|
||||
html: content,
|
||||
|
@ -77,7 +77,7 @@ export const executeWebhook = (
|
||||
{ blockId, stepId }: { blockId: string; stepId: string }
|
||||
) =>
|
||||
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',
|
||||
body: {
|
||||
variables,
|
||||
|
@ -135,3 +135,6 @@ export const timeSince = (date: string) => {
|
||||
}
|
||||
return Math.floor(seconds) + ' seconds'
|
||||
}
|
||||
|
||||
export const isCloudProdInstance = () =>
|
||||
window.location.hostname === 'app.typebot.io'
|
||||
|
Reference in New Issue
Block a user