build: 👷 New compose file and entrypoints
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,7 +2,6 @@ node_modules
|
||||
.next
|
||||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
workspace.code-workspace
|
||||
.DS_Store
|
||||
.turbo
|
||||
@ -18,6 +17,8 @@ test-results
|
||||
build
|
||||
firebaseServiceAccount.json
|
||||
|
||||
.env.production
|
||||
|
||||
# Wordpress
|
||||
.svn
|
||||
tags
|
||||
|
12
Dockerfile
12
Dockerfile
@ -17,6 +17,7 @@ RUN yarn install --frozen-lockfile
|
||||
FROM base AS builder
|
||||
COPY --from=installer /app/ .
|
||||
COPY --from=pruner /app/out/full/ .
|
||||
COPY ./apps/${SCOPE}/.env.docker ./apps/${SCOPE}/.env.production
|
||||
RUN apt-get -qy update && apt-get -qy install openssl
|
||||
RUN yarn turbo run build --scope=${SCOPE} --include-dependencies --no-deps
|
||||
RUN find . -name node_modules | xargs rm -rf
|
||||
@ -31,7 +32,14 @@ COPY --from=builder /app/apps/${SCOPE}/public ./public
|
||||
COPY --from=builder /app/apps/${SCOPE}/package.json ./package.json
|
||||
COPY --from=builder /app/apps/${SCOPE}/.next/standalone ./
|
||||
COPY --from=builder /app/apps/${SCOPE}/.next/static ./.next/static
|
||||
COPY --from=builder /app/apps/${SCOPE}/.env.docker ./.env.production
|
||||
RUN apt-get -qy update && apt-get -qy install openssl
|
||||
|
||||
COPY entrypoint.sh ./
|
||||
COPY ${SCOPE}-entrypoint.sh ./
|
||||
RUN chmod +x ./${SCOPE}-entrypoint.sh
|
||||
RUN chmod +x ./entrypoint.sh
|
||||
ENTRYPOINT ./${SCOPE}-entrypoint.sh
|
||||
|
||||
EXPOSE 3000
|
||||
ENV PORT 3000
|
||||
CMD ["node", "server.js"]
|
||||
ENV PORT 3000
|
13
apps/builder/.env.docker
Normal file
13
apps/builder/.env.docker
Normal file
@ -0,0 +1,13 @@
|
||||
# Don't edit this file
|
||||
NEXT_PUBLIC_VIEWER_URL=DOCKER_PUBLIC_VIEWER_URL
|
||||
NEXT_PUBLIC_SMTP_FROM=DOCKER_NEXT_PUBLIC_SMTP_FROM
|
||||
NEXT_PUBLIC_SMTP_AUTH_DISABLED=DOCKER_NEXT_PUBLIC_SMTP_AUTH_DISABLED
|
||||
NEXT_PUBLIC_GOOGLE_CLIENT_ID=DOCKER_NEXT_PUBLIC_GOOGLE_CLIENT_ID
|
||||
NEXT_PUBLIC_GOOGLE_API_KEY=DOCKER_NEXT_PUBLIC_GOOGLE_API_KEY
|
||||
NEXT_PUBLIC_GITHUB_CLIENT_ID=DOCKER_NEXT_PUBLIC_GITHUB_CLIENT_ID
|
||||
NEXT_PUBLIC_GITLAB_CLIENT_ID=DOCKER_NEXT_PUBLIC_GITLAB_CLIENT_ID
|
||||
NEXT_PUBLIC_GITLAB_NAME=DOCKER_NEXT_PUBLIC_GITLAB_NAME
|
||||
NEXT_PUBLIC_FACEBOOK_CLIENT_ID=DOCKER_NEXT_PUBLIC_FACEBOOK_CLIENT_ID
|
||||
NEXT_PUBLIC_GIPHY_API_KEY=DOCKER_NEXT_PUBLIC_GIPHY_API_KEY
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=DOCKER_NEXT_PUBLIC_STRIPE_PUBLIC_KEY
|
||||
NEXT_PUBLIC_SENTRY_DSN=DOCKER_NEXT_PUBLIC_SENTRY_DSN
|
@ -14,14 +14,15 @@ import { DividerWithText } from './DividerWithText'
|
||||
import { SocialLoginButtons } from './SocialLoginButtons'
|
||||
import { useRouter } from 'next/router'
|
||||
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
const hasNoAuthProvider =
|
||||
(!process.env.NEXT_PUBLIC_SMTP_FROM ||
|
||||
(isEmpty(process.env.NEXT_PUBLIC_SMTP_FROM) ||
|
||||
process.env.NEXT_PUBLIC_SMTP_AUTH_DISABLED === 'true') &&
|
||||
!process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID &&
|
||||
!process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID &&
|
||||
!process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID &&
|
||||
!process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID
|
||||
isEmpty(process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID) &&
|
||||
isEmpty(process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID) &&
|
||||
isEmpty(process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID) &&
|
||||
isEmpty(process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID)
|
||||
|
||||
type Props = {
|
||||
defaultEmail?: string
|
||||
@ -77,7 +78,7 @@ export const SignInForm = ({
|
||||
return (
|
||||
<Stack spacing="4" w="330px">
|
||||
<SocialLoginButtons />
|
||||
{process.env.NEXT_PUBLIC_SMTP_FROM &&
|
||||
{!isEmpty(process.env.NEXT_PUBLIC_SMTP_FROM) &&
|
||||
process.env.NEXT_PUBLIC_SMTP_AUTH_DISABLED !== 'true' && (
|
||||
<>
|
||||
<DividerWithText mt="6">Or with your email</DividerWithText>
|
||||
|
@ -5,6 +5,7 @@ import { useRouter } from 'next/router'
|
||||
import React from 'react'
|
||||
import { stringify } from 'qs'
|
||||
import { FacebookLogo, GoogleLogo, GitlabLogo } from 'assets/logos'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
export const SocialLoginButtons = () => {
|
||||
const { query } = useRouter()
|
||||
@ -32,7 +33,7 @@ export const SocialLoginButtons = () => {
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
{process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID && (
|
||||
{!isEmpty(process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID) && (
|
||||
<Button
|
||||
leftIcon={<GithubIcon />}
|
||||
onClick={handleGitHubClick}
|
||||
@ -43,7 +44,7 @@ export const SocialLoginButtons = () => {
|
||||
Continue with GitHub
|
||||
</Button>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID && (
|
||||
{!isEmpty(process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID) && (
|
||||
<Button
|
||||
leftIcon={<GoogleLogo />}
|
||||
onClick={handleGoogleClick}
|
||||
@ -54,7 +55,7 @@ export const SocialLoginButtons = () => {
|
||||
Continue with Google
|
||||
</Button>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID && (
|
||||
{!isEmpty(process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID) && (
|
||||
<Button
|
||||
leftIcon={<FacebookLogo />}
|
||||
onClick={handleFacebookClick}
|
||||
@ -65,7 +66,7 @@ export const SocialLoginButtons = () => {
|
||||
Continue with Facebook
|
||||
</Button>
|
||||
)}
|
||||
{process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID && (
|
||||
{!isEmpty(process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID) && (
|
||||
<Button
|
||||
leftIcon={<GitlabLogo />}
|
||||
onClick={handleGitlabClick}
|
||||
@ -73,7 +74,10 @@ export const SocialLoginButtons = () => {
|
||||
isLoading={['loading', 'authenticated'].includes(status)}
|
||||
variant="outline"
|
||||
>
|
||||
Continue with {process.env.NEXT_PUBLIC_GITLAB_NAME || 'GitLab'}
|
||||
Continue with{' '}
|
||||
{isEmpty(process.env.NEXT_PUBLIC_GITLAB_NAME)
|
||||
? 'GitLab'
|
||||
: process.env.NEXT_PUBLIC_GITLAB_NAME}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
|
@ -5,6 +5,7 @@ import { BubbleParams } from 'typebot-js'
|
||||
import { parseInitBubbleCode, typebotJsHtml } from '../params'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type ChatEmbedCodeProps = {
|
||||
withStarterVariables?: boolean
|
||||
@ -20,8 +21,9 @@ export const ChatEmbedCode = ({
|
||||
const snippet = prettier.format(
|
||||
createSnippet({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${typebot?.publicId}`,
|
||||
button,
|
||||
proactiveMessage,
|
||||
|
@ -5,6 +5,7 @@ import { parseInitContainerCode, typebotJsHtml } from '../params'
|
||||
import { IframeParams } from 'typebot-js'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type ContainerEmbedCodeProps = {
|
||||
widthLabel: string
|
||||
@ -22,8 +23,9 @@ export const ContainerEmbedCode = ({
|
||||
const snippet = prettier.format(
|
||||
parseSnippet({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${typebot?.publicId}`,
|
||||
heightLabel,
|
||||
widthLabel,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { FlexProps } from '@chakra-ui/react'
|
||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type Props = {
|
||||
widthLabel: string
|
||||
@ -13,8 +14,9 @@ export const IframeEmbedCode = ({
|
||||
}: Props & FlexProps) => {
|
||||
const { typebot } = useTypebot()
|
||||
const src = `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${typebot?.publicId}`
|
||||
const code = `<iframe src="${src}" width="${widthLabel}" height="${heightLabel}" />`
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { useTypebot } from 'contexts/TypebotContext'
|
||||
import parserHtml from 'prettier/parser-html'
|
||||
import prettier from 'prettier/standalone'
|
||||
import { PopupParams } from 'typebot-js'
|
||||
import { isEmpty } from 'utils'
|
||||
import { parseInitPopupCode, typebotJsHtml } from '../params'
|
||||
|
||||
type PopupEmbedCodeProps = {
|
||||
@ -17,8 +18,9 @@ export const PopupEmbedCode = ({ delay }: PopupEmbedCodeProps & FlexProps) => {
|
||||
const snippet = prettier.format(
|
||||
createSnippet({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${typebot?.publicId}`,
|
||||
delay,
|
||||
}),
|
||||
|
@ -10,6 +10,7 @@ import parserBabel from 'prettier/parser-babel'
|
||||
import prettier from 'prettier/standalone'
|
||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||
import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type StandardReactDivProps = { widthLabel: string; heightLabel: string }
|
||||
export const StandardReactDiv = ({
|
||||
@ -20,8 +21,9 @@ export const StandardReactDiv = ({
|
||||
const snippet = prettier.format(
|
||||
parseContainerSnippet({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${typebot?.publicId}`,
|
||||
heightLabel,
|
||||
widthLabel,
|
||||
@ -71,8 +73,9 @@ export const PopupReactCode = ({ delay }: PopupEmbedCodeProps & FlexProps) => {
|
||||
const snippet = prettier.format(
|
||||
parsePopupSnippet({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${typebot?.publicId}`,
|
||||
delay,
|
||||
}),
|
||||
@ -121,8 +124,9 @@ export const ChatReactCode = ({
|
||||
const snippet = prettier.format(
|
||||
parseBubbleSnippet({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${typebot?.publicId}`,
|
||||
button,
|
||||
proactiveMessage,
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { createCustomDomain } from 'services/user'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
const hostnameRegex =
|
||||
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/
|
||||
@ -119,8 +120,11 @@ export const CustomDomainModal = ({
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text fontWeight="bold">Value</Text>
|
||||
<Text>{process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL}</Text>
|
||||
<Text>
|
||||
{isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL}
|
||||
</Text>
|
||||
</Stack>
|
||||
</HStack>
|
||||
) : (
|
||||
|
@ -11,6 +11,7 @@ import { PopupEmbedSettings } from 'components/share/codeSnippets/Popup/EmbedSet
|
||||
import { CodeEditor } from 'components/shared/CodeEditor'
|
||||
import { useState } from 'react'
|
||||
import { BubbleParams } from 'typebot-js'
|
||||
import { isEmpty } from 'utils'
|
||||
import { ModalProps } from '../../EmbedButton'
|
||||
|
||||
type GtmInstructionsProps = {
|
||||
@ -40,8 +41,9 @@ const StandardInstructions = ({ publicId }: Pick<ModalProps, 'publicId'>) => {
|
||||
|
||||
const jsCode = parseInitContainerCode({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${publicId}`,
|
||||
})
|
||||
const headCode = `${typebotJsHtml}
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { CopyButton } from 'components/shared/buttons/CopyButton'
|
||||
import { PublishFirstInfo } from 'components/shared/Info'
|
||||
import { isEmpty } from 'utils'
|
||||
import { ModalProps } from '../EmbedButton'
|
||||
|
||||
export const NotionModal = ({
|
||||
@ -45,15 +46,17 @@ export const NotionModal = ({
|
||||
pr="4.5rem"
|
||||
type={'text'}
|
||||
defaultValue={`${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${publicId}`}
|
||||
/>
|
||||
<InputRightElement width="4.5rem">
|
||||
<CopyButton
|
||||
textToCopy={`${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${publicId}`}
|
||||
/>
|
||||
</InputRightElement>
|
||||
|
@ -14,6 +14,7 @@ import { BubbleParams } from 'typebot-js'
|
||||
import { ModalProps } from '../../EmbedButton'
|
||||
import parserHtml from 'prettier/parser-html'
|
||||
import prettier from 'prettier/standalone'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type ShopifyInstructionsProps = {
|
||||
type: 'standard' | 'popup' | 'bubble'
|
||||
@ -45,8 +46,9 @@ const StandardInstructions = ({ publicId }: Pick<ModalProps, 'publicId'>) => {
|
||||
|
||||
const jsCode = parseInitContainerCode({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${publicId}`,
|
||||
})
|
||||
const headCode = prettier.format(
|
||||
|
@ -17,6 +17,7 @@ import {
|
||||
import { ExternalLinkIcon } from 'assets/icons'
|
||||
import { CopyButton } from 'components/shared/buttons/CopyButton'
|
||||
import { PublishFirstInfo } from 'components/shared/Info'
|
||||
import { isEmpty } from 'utils'
|
||||
import { ModalProps } from '../EmbedButton'
|
||||
|
||||
export const WordpressModal = ({
|
||||
@ -54,15 +55,17 @@ export const WordpressModal = ({
|
||||
pr="4.5rem"
|
||||
type={'text'}
|
||||
defaultValue={`${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${publicId}`}
|
||||
/>
|
||||
<InputRightElement width="4.5rem">
|
||||
<CopyButton
|
||||
textToCopy={`${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/${publicId}`}
|
||||
/>
|
||||
</InputRightElement>
|
||||
|
@ -9,6 +9,7 @@ import { useDebouncedCallback } from 'use-debounce'
|
||||
import { linter } from '@codemirror/lint'
|
||||
import { VariablesButton } from './buttons/VariablesButton'
|
||||
import { Variable } from 'models'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
const linterExtension = linter(jsonParseLinter())
|
||||
|
||||
@ -40,7 +41,7 @@ export const CodeEditor = ({
|
||||
setPlainTextValue(value)
|
||||
onChange && onChange(value)
|
||||
},
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? debounceTimeout : 0
|
||||
)
|
||||
|
||||
useEffect(
|
||||
|
@ -16,6 +16,7 @@ import { useTypebot } from 'contexts/TypebotContext'
|
||||
import { BaseEmoji, emojiIndex } from 'emoji-mart'
|
||||
import { emojis } from './emojis'
|
||||
import { Input } from '../Textbox/Input'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type Props = {
|
||||
url?: string
|
||||
@ -182,7 +183,7 @@ const EmojiContent = ({
|
||||
}
|
||||
|
||||
const GiphyContent = ({ onNewUrl }: ContentProps) => {
|
||||
if (!process.env.NEXT_PUBLIC_GIPHY_API_KEY)
|
||||
if (isEmpty(process.env.NEXT_PUBLIC_GIPHY_API_KEY))
|
||||
return <Text>NEXT_PUBLIC_GIPHY_API_KEY is missing in environment</Text>
|
||||
return (
|
||||
<SearchContextManager
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { useState, useRef, useEffect, ChangeEvent } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type Props = {
|
||||
selectedItem?: string
|
||||
@ -31,7 +32,7 @@ export const SearchableDropdown = ({
|
||||
const debounced = useDebouncedCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onValueChange ? onValueChange : () => {},
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? debounceTimeout : 0
|
||||
)
|
||||
const [filteredItems, setFilteredItems] = useState([
|
||||
...items
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
} from '@chakra-ui/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
export const SmartNumberInput = ({
|
||||
value,
|
||||
@ -22,7 +23,7 @@ export const SmartNumberInput = ({
|
||||
const [currentValue, setCurrentValue] = useState(value?.toString() ?? '')
|
||||
const debounced = useDebouncedCallback(
|
||||
onValueChange,
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? debounceTimeout : 0
|
||||
)
|
||||
|
||||
useEffect(
|
||||
|
@ -5,6 +5,7 @@ import React, { useEffect, useState } from 'react'
|
||||
import { isCloudProdInstance } from 'services/utils'
|
||||
import { planToReadable } from 'services/workspace'
|
||||
import { initBubble } from 'typebot-js'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
export const SupportBubble = () => {
|
||||
const { typebot } = useTypebot()
|
||||
@ -22,8 +23,9 @@ export const SupportBubble = () => {
|
||||
setLocalUserId(user?.id)
|
||||
initBubble({
|
||||
url: `${
|
||||
process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL ??
|
||||
process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
isEmpty(process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL)
|
||||
? process.env.NEXT_PUBLIC_VIEWER_URL
|
||||
: process.env.NEXT_PUBLIC_VIEWER_INTERNAL_URL
|
||||
}/typebot-support`,
|
||||
backgroundColor: '#ffffff',
|
||||
button: {
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
import { Variable } from 'models'
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { isEmpty } from 'utils'
|
||||
import { VariablesButton } from '../buttons/VariablesButton'
|
||||
|
||||
export type TextBoxProps = {
|
||||
@ -35,7 +36,7 @@ export const TextBox = ({
|
||||
(value) => {
|
||||
onChange(value)
|
||||
},
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? debounceTimeout : 0
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -17,7 +17,7 @@ import cuid from 'cuid'
|
||||
import { Variable } from 'models'
|
||||
import React, { useState, useRef, ChangeEvent, useEffect } from 'react'
|
||||
import { useDebouncedCallback } from 'use-debounce'
|
||||
import { byId, isNotDefined } from 'utils'
|
||||
import { byId, isEmpty, isNotDefined } from 'utils'
|
||||
|
||||
type Props = {
|
||||
initialVariableId?: string
|
||||
@ -47,7 +47,7 @@ export const VariableSearchInput = ({
|
||||
const variable = variables.find((v) => v.name === value)
|
||||
if (variable) onSelectVariable(variable)
|
||||
},
|
||||
process.env.NEXT_PUBLIC_E2E_TEST ? 0 : debounceTimeout
|
||||
isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? debounceTimeout : 0
|
||||
)
|
||||
const [filteredItems, setFilteredItems] = useState<Variable[]>(
|
||||
variables ?? []
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Text, HStack } from '@chakra-ui/react'
|
||||
import { SearchableDropdown } from '../../../shared/SearchableDropdown'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
type FontSelectorProps = {
|
||||
activeFont?: string
|
||||
@ -19,7 +20,7 @@ export const FontSelector = ({
|
||||
}, [])
|
||||
|
||||
const fetchPopularFonts = async () => {
|
||||
if (!process.env.NEXT_PUBLIC_GOOGLE_API_KEY) return []
|
||||
if (isEmpty(process.env.NEXT_PUBLIC_GOOGLE_API_KEY)) return []
|
||||
const response = await fetch(
|
||||
`https://www.googleapis.com/webfonts/v1/webfonts?key=${process.env.NEXT_PUBLIC_GOOGLE_API_KEY}&sort=popularity`
|
||||
)
|
||||
|
@ -30,7 +30,7 @@ import {
|
||||
} from 'services/typebots/typebots'
|
||||
import { fetcher, preventUserFromRefreshing } from 'services/utils'
|
||||
import useSWR from 'swr'
|
||||
import { isDefined, isNotDefined, omit } from 'utils'
|
||||
import { isDefined, isEmpty, isNotDefined, omit } from 'utils'
|
||||
import { BlocksActions, blocksActions } from './actions/blocks'
|
||||
import { stepsAction, StepsActions } from './actions/steps'
|
||||
import { variablesAction, VariablesActions } from './actions/variables'
|
||||
@ -410,7 +410,7 @@ export const useFetchedTypebot = ({
|
||||
},
|
||||
Error
|
||||
>(`/api/typebots/${typebotId}`, fetcher, {
|
||||
dedupingInterval: process.env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined,
|
||||
dedupingInterval: isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? undefined : 0,
|
||||
})
|
||||
if (error) onError(error)
|
||||
return {
|
||||
|
@ -10,10 +10,11 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { withSentry } from '@sentry/nextjs'
|
||||
import { CustomAdapter } from './adapter'
|
||||
import { User } from 'db'
|
||||
import { isNotEmpty } from 'utils'
|
||||
|
||||
const providers: Provider[] = []
|
||||
|
||||
if (process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID)
|
||||
if (isNotEmpty(process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID))
|
||||
providers.push(
|
||||
GitHubProvider({
|
||||
clientId: process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID,
|
||||
@ -22,7 +23,7 @@ if (process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID)
|
||||
)
|
||||
|
||||
if (
|
||||
process.env.NEXT_PUBLIC_SMTP_FROM &&
|
||||
isNotEmpty(process.env.NEXT_PUBLIC_SMTP_FROM) &&
|
||||
process.env.NEXT_PUBLIC_SMTP_AUTH_DISABLED !== 'true'
|
||||
)
|
||||
providers.push(
|
||||
@ -40,8 +41,8 @@ if (
|
||||
)
|
||||
|
||||
if (
|
||||
process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID &&
|
||||
process.env.GOOGLE_CLIENT_SECRET
|
||||
isNotEmpty(process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.GOOGLE_CLIENT_SECRET)
|
||||
)
|
||||
providers.push(
|
||||
GoogleProvider({
|
||||
@ -51,8 +52,8 @@ if (
|
||||
)
|
||||
|
||||
if (
|
||||
process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID &&
|
||||
process.env.FACEBOOK_CLIENT_SECRET
|
||||
isNotEmpty(process.env.NEXT_PUBLIC_FACEBOOK_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.FACEBOOK_CLIENT_SECRET)
|
||||
)
|
||||
providers.push(
|
||||
FacebookProvider({
|
||||
@ -62,8 +63,8 @@ if (
|
||||
)
|
||||
|
||||
if (
|
||||
process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID &&
|
||||
process.env.GITLAB_CLIENT_SECRET
|
||||
isNotEmpty(process.env.NEXT_PUBLIC_GITLAB_CLIENT_ID) &&
|
||||
isNotEmpty(process.env.GITLAB_CLIENT_SECRET)
|
||||
) {
|
||||
const BASE_URL = process.env.GITLAB_BASE_URL || 'https://gitlab.com'
|
||||
providers.push(
|
||||
|
@ -9,6 +9,7 @@ import { getAuthenticatedUser } from 'services/api/utils'
|
||||
import {
|
||||
badRequest,
|
||||
forbidden,
|
||||
isEmpty,
|
||||
isNotDefined,
|
||||
methodNotAllowed,
|
||||
notAuthenticated,
|
||||
@ -66,7 +67,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
await prisma.invitation.create({
|
||||
data: { email: email.toLowerCase(), type, typebotId },
|
||||
})
|
||||
if (isNotDefined(process.env.NEXT_PUBLIC_E2E_TEST))
|
||||
if (isEmpty(process.env.NEXT_PUBLIC_E2E_TEST))
|
||||
await sendEmailNotification({
|
||||
to: email,
|
||||
subject: "You've been invited to collaborate 🤝",
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { CollaborationType, Prisma, User, WorkspaceRole } from 'db'
|
||||
import { isNotEmpty } from 'utils'
|
||||
|
||||
const parseWhereFilter = (
|
||||
typebotIds: string[] | string,
|
||||
@ -19,7 +20,7 @@ const parseWhereFilter = (
|
||||
id: typeof typebotIds === 'string' ? typebotIds : { in: typebotIds },
|
||||
workspace:
|
||||
(type === 'read' && user.email === process.env.ADMIN_EMAIL) ||
|
||||
process.env.NEXT_PUBLIC_E2E_TEST
|
||||
isNotEmpty(process.env.NEXT_PUBLIC_E2E_TEST)
|
||||
? undefined
|
||||
: {
|
||||
members: {
|
||||
|
@ -2,7 +2,7 @@ import { DashboardFolder } from 'db'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from './utils'
|
||||
import { stringify } from 'qs'
|
||||
import { sendRequest } from 'utils'
|
||||
import { isNotEmpty, sendRequest } from 'utils'
|
||||
|
||||
export const useFolders = ({
|
||||
parentId,
|
||||
@ -17,7 +17,11 @@ export const useFolders = ({
|
||||
const { data, error, mutate } = useSWR<{ folders: DashboardFolder[] }, Error>(
|
||||
workspaceId ? `/api/folders?${params}` : null,
|
||||
fetcher,
|
||||
{ dedupingInterval: process.env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined }
|
||||
{
|
||||
dedupingInterval: isNotEmpty(process.env.NEXT_PUBLIC_E2E_TEST)
|
||||
? 0
|
||||
: undefined,
|
||||
}
|
||||
)
|
||||
if (error) onError(error)
|
||||
return {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { User } from 'db'
|
||||
import { loadStripe } from '@stripe/stripe-js/pure'
|
||||
import { sendRequest } from 'utils'
|
||||
import { isEmpty, sendRequest } from 'utils'
|
||||
|
||||
type Props = {
|
||||
user: User
|
||||
@ -10,7 +10,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export const pay = async ({ user, currency, plan, workspaceId }: Props) => {
|
||||
if (!process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY)
|
||||
if (isEmpty(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY))
|
||||
throw new Error('NEXT_PUBLIC_STRIPE_PUBLIC_KEY is missing in env')
|
||||
const stripe = await loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY)
|
||||
const { data, error } = await sendRequest<{ sessionId: string }>({
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { CollaborationType, Invitation } from 'db'
|
||||
import { fetcher } from 'services/utils'
|
||||
import useSWR from 'swr'
|
||||
import { sendRequest } from 'utils'
|
||||
import { isNotEmpty, sendRequest } from 'utils'
|
||||
|
||||
export const useInvitations = ({
|
||||
typebotId,
|
||||
@ -13,7 +13,11 @@ export const useInvitations = ({
|
||||
const { data, error, mutate } = useSWR<{ invitations: Invitation[] }, Error>(
|
||||
typebotId ? `/api/typebots/${typebotId}/invitations` : null,
|
||||
fetcher,
|
||||
{ dedupingInterval: process.env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined }
|
||||
{
|
||||
dedupingInterval: isNotEmpty(process.env.NEXT_PUBLIC_E2E_TEST)
|
||||
? 0
|
||||
: undefined,
|
||||
}
|
||||
)
|
||||
if (error) onError(error)
|
||||
return {
|
||||
|
@ -44,6 +44,7 @@ import useSWR from 'swr'
|
||||
import { fetcher, toKebabCase } from '../utils'
|
||||
import {
|
||||
isBubbleStepType,
|
||||
isNotEmpty,
|
||||
isWebhookStep,
|
||||
omit,
|
||||
stepHasItems,
|
||||
@ -80,7 +81,9 @@ export const useTypebots = ({
|
||||
{ typebots: TypebotInDashboard[] },
|
||||
Error
|
||||
>(workspaceId ? `/api/typebots?${params}` : null, fetcher, {
|
||||
dedupingInterval: process.env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined,
|
||||
dedupingInterval: isNotEmpty(process.env.NEXT_PUBLIC_E2E_TEST)
|
||||
? 0
|
||||
: undefined,
|
||||
})
|
||||
if (error) onError(error)
|
||||
return {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { MemberInWorkspace, WorkspaceInvitation } from 'db'
|
||||
import { fetcher } from 'services/utils'
|
||||
import useSWR from 'swr'
|
||||
import { sendRequest } from 'utils'
|
||||
import { isEmpty, sendRequest } from 'utils'
|
||||
|
||||
export type Member = MemberInWorkspace & {
|
||||
name: string | null
|
||||
@ -14,7 +14,7 @@ export const useMembers = ({ workspaceId }: { workspaceId?: string }) => {
|
||||
{ members: Member[]; invitations: WorkspaceInvitation[] },
|
||||
Error
|
||||
>(workspaceId ? `/api/workspaces/${workspaceId}/members` : null, fetcher, {
|
||||
dedupingInterval: process.env.NEXT_PUBLIC_E2E_TEST ? 0 : undefined,
|
||||
dedupingInterval: isEmpty(process.env.NEXT_PUBLIC_E2E_TEST) ? undefined : 0,
|
||||
})
|
||||
return {
|
||||
members: data?.members,
|
||||
|
@ -18,17 +18,13 @@ You need a server with Docker installed. If your server doesn't come with Docker
|
||||
|
||||
On your server:
|
||||
|
||||
1. Clone the repo:
|
||||
1. Download the latest `docker-compose.yml` file:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/baptistearno/typebot.io.git
|
||||
wget https://raw.githubusercontent.com/baptisteArno/typebot.io/latest/docker-compose.yml
|
||||
```
|
||||
|
||||
2. Set up environment variables
|
||||
|
||||
Copy `apps/builder/.env.production.example` to `apps/builder/.env.production`
|
||||
|
||||
Copy `apps/viewer/.env.production.example` to `apps/viewer/.env.production`
|
||||
2. Open the file and set the environment variables for both `typebot-builder` and `typebot-viewer`
|
||||
|
||||
Check out the [Configuration guide](https://docs.typebot.io/self-hosting/configuration) to add your environment variables
|
||||
|
||||
@ -45,4 +41,4 @@ On your server:
|
||||
- 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}` to have access to a Pro account automatically.
|
||||
You should see the login screen if you navigate to `http://{hostname}:8080`. Login with the `${ADMIN_EMAIL}` to have access to a Team plan workspace automatically.
|
||||
|
3
apps/viewer/.env.docker
Normal file
3
apps/viewer/.env.docker
Normal file
@ -0,0 +1,3 @@
|
||||
NEXT_PUBLIC_VIEWER_URL=DOCKER_PUBLIC_VIEWER_URL
|
||||
NEXT_PUBLIC_SMTP_FROM=DOCKER_NEXT_PUBLIC_SMTP_FROM
|
||||
NEXT_PUBLIC_GOOGLE_CLIENT_ID=DOCKER_NEXT_PUBLIC_GOOGLE_CLIENT_ID
|
@ -1,4 +1,3 @@
|
||||
DATABASE_URL=postgresql://postgres:typebot@db:5432/typebot
|
||||
NEXT_PUBLIC_VIEWER_URL=http://localhost:8081
|
||||
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
||||
ADMIN_EMAIL=contact@baptiste-arnaud.fr
|
||||
ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
export const ErrorPage = ({ error }: { error: Error }) => {
|
||||
return (
|
||||
@ -11,7 +12,7 @@ export const ErrorPage = ({ error }: { error: Error }) => {
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
{!process.env.NEXT_PUBLIC_VIEWER_URL ? (
|
||||
{isEmpty(process.env.NEXT_PUBLIC_VIEWER_URL) ? (
|
||||
<>
|
||||
<h1 style={{ fontWeight: 'bold', fontSize: '30px' }}>
|
||||
NEXT_PUBLIC_VIEWER_URL is missing
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { CollaborationType, Prisma, User, WorkspaceRole } from 'db'
|
||||
import { isNotEmpty } from 'utils'
|
||||
|
||||
const parseWhereFilter = (
|
||||
typebotIds: string[] | string,
|
||||
@ -19,7 +20,7 @@ const parseWhereFilter = (
|
||||
id: typeof typebotIds === 'string' ? typebotIds : { in: typebotIds },
|
||||
workspace:
|
||||
(type === 'read' && user.email === process.env.ADMIN_EMAIL) ||
|
||||
process.env.NEXT_PUBLIC_E2E_TEST
|
||||
isNotEmpty(process.env.NEXT_PUBLIC_E2E_TEST)
|
||||
? undefined
|
||||
: {
|
||||
members: {
|
||||
|
12
builder-entrypoint.sh
Normal file
12
builder-entrypoint.sh
Normal file
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
./entrypoint.sh
|
||||
|
||||
./node_modules/.bin/prisma generate;
|
||||
|
||||
echo 'Waiting 5s for db to be ready...';
|
||||
sleep 5;
|
||||
|
||||
./node_modules/.bin/prisma migrate deploy;
|
||||
|
||||
node server.js;
|
46
docker-compose.build.yml
Normal file
46
docker-compose.build.yml
Normal file
@ -0,0 +1,46 @@
|
||||
version: '3.9'
|
||||
services:
|
||||
typebot-db:
|
||||
image: postgres:13
|
||||
restart: always
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_DB=typebot
|
||||
- POSTGRES_PASSWORD=typebot
|
||||
typebot-builder:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- SCOPE=builder
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
ports:
|
||||
- '8080:3000'
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
# See https://docs.typebot.io/self-hosting/configuration for more configuration options
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
|
||||
- NEXTAUTH_URL=http://localhost:8080
|
||||
- NEXT_PUBLIC_VIEWER_URL=http://localhost:8081
|
||||
- ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
||||
- ADMIN_EMAIL=me@email.com
|
||||
- NEXTAUTH_URL_INTERNAL=http://host.docker.internal:8080
|
||||
- NEXT_PUBLIC_GOOGLE_CLIENT_ID=fnejwkn
|
||||
typebot-viewer:
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- SCOPE=viewer
|
||||
restart: always
|
||||
ports:
|
||||
- '8081:3000'
|
||||
# See https://docs.typebot.io/self-hosting/configuration for more configuration options
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
|
||||
- NEXT_PUBLIC_VIEWER_URL=http://localhost:8081
|
||||
- ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
||||
volumes:
|
||||
db_data:
|
@ -1,6 +1,6 @@
|
||||
version: '3.9'
|
||||
services:
|
||||
db:
|
||||
typebot-db:
|
||||
image: postgres:13
|
||||
restart: always
|
||||
volumes:
|
||||
@ -8,42 +8,35 @@ services:
|
||||
environment:
|
||||
- POSTGRES_DB=typebot
|
||||
- POSTGRES_PASSWORD=typebot
|
||||
builder:
|
||||
depends_on:
|
||||
- db
|
||||
typebot-builder:
|
||||
image: baptistearno/typebot-builder:latest
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- SCOPE=builder
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
ports:
|
||||
- '8080:3000'
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
env_file:
|
||||
- './apps/builder/.env.production'
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
./node_modules/.bin/prisma generate;
|
||||
echo 'Waiting 5s for db to be ready...';
|
||||
sleep 5;
|
||||
./node_modules/.bin/prisma migrate deploy;
|
||||
node server.js;"
|
||||
viewer:
|
||||
depends_on:
|
||||
- db
|
||||
# See https://docs.typebot.io/self-hosting/configuration for more configuration options
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
|
||||
- NEXTAUTH_URL=http://localhost:8080
|
||||
- NEXT_PUBLIC_VIEWER_URL=http://localhost:8081
|
||||
- ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
||||
- ADMIN_EMAIL=me@email.com
|
||||
typebot-viewer:
|
||||
image: baptistearno/typebot-viewer:latest
|
||||
restart: always
|
||||
build:
|
||||
context: .
|
||||
args:
|
||||
- SCOPE=viewer
|
||||
ports:
|
||||
- '8081:3000'
|
||||
env_file:
|
||||
- './apps/viewer/.env.production'
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
./node_modules/.bin/prisma generate;
|
||||
node server.js;"
|
||||
# See https://docs.typebot.io/self-hosting/configuration for more configuration options
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://postgres:typebot@typebot-db:5432/typebot
|
||||
- NEXT_PUBLIC_VIEWER_URL=http://localhost:8081
|
||||
- ENCRYPTION_SECRET=SgVkYp2s5v8y/B?E(H+MbQeThWmZq4t6
|
||||
volumes:
|
||||
db_data:
|
||||
s3_data:
|
||||
|
35
entrypoint.sh
Normal file
35
entrypoint.sh
Normal file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This entrypoint inject variables at runtime.
|
||||
# See https://raphaelpralat.medium.com/system-environment-variables-in-next-js-with-docker-1f0754e04cde
|
||||
|
||||
# no verbose
|
||||
set +x
|
||||
|
||||
# config
|
||||
envFilename='.env.production'
|
||||
nextFolder='./.next/'
|
||||
|
||||
function apply_path {
|
||||
# read all config file
|
||||
while read line; do
|
||||
# no comment or not empty
|
||||
if [ "${line:0:1}" == "#" ] || [ "${line}" == "" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# split
|
||||
configName="$(cut -d'=' -f1 <<<"$line")"
|
||||
configValue="$(cut -d'=' -f2 <<<"$line")" # get system env
|
||||
envValue=$(env | grep "^$configName=" | grep -oe '[^=]*$');
|
||||
|
||||
if [ -n "$configValue" ] && [ -n "$envValue" ]; then
|
||||
echo "Injecting ${configName}..."
|
||||
fi
|
||||
# replace all
|
||||
find $nextFolder \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i "s#$configValue#$envValue#g"
|
||||
done < $envFilename
|
||||
}
|
||||
|
||||
apply_path
|
||||
exec "$@"
|
@ -21,6 +21,7 @@ import {
|
||||
} from 'models'
|
||||
import { Log } from 'db'
|
||||
import { LiteBadge } from './LiteBadge'
|
||||
import { isEmpty } from 'utils'
|
||||
|
||||
export type TypebotViewerProps = {
|
||||
typebot: PublicTypebot
|
||||
@ -66,7 +67,7 @@ export const TypebotViewer = ({
|
||||
|
||||
const handleCompleted = () => onCompleted && onCompleted()
|
||||
|
||||
if (!apiHost)
|
||||
if (isEmpty(apiHost))
|
||||
return <p>process.env.NEXT_PUBLIC_VIEWER_URL is missing in env</p>
|
||||
return (
|
||||
<Frame
|
||||
|
@ -61,6 +61,12 @@ export const isNotDefined = <T>(
|
||||
value: T | undefined | null
|
||||
): value is undefined | null => value === undefined || value === null
|
||||
|
||||
export const isEmpty = (value: string | undefined | null): value is undefined =>
|
||||
value === undefined || value === null || value === ''
|
||||
|
||||
export const isNotEmpty = (value: string | undefined | null): value is string =>
|
||||
value !== undefined && value !== null && value !== ''
|
||||
|
||||
export const isInputStep = (step: Step): step is InputStep =>
|
||||
(Object.values(InputStepType) as string[]).includes(step.type)
|
||||
|
||||
|
7
viewer-entrypoint.sh
Normal file
7
viewer-entrypoint.sh
Normal file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
./entrypoint.sh
|
||||
|
||||
./node_modules/.bin/prisma generate;
|
||||
|
||||
node server.js;
|
Reference in New Issue
Block a user