Skip to content

Commit

Permalink
feat(editor): πŸ”’οΈ Add verification on backend for file input deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Jun 13, 2022
1 parent 910b871 commit 14afd22
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 17 deletions.
3 changes: 2 additions & 1 deletion apps/builder/components/shared/buttons/PublishButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const PublishButton = () => {
publishedTypebot,
restorePublishedTypebot,
typebot,
isSavingLoading,
} = useTypebot()

const hasInputFile = typebot?.groups
Expand Down Expand Up @@ -73,7 +74,7 @@ export const PublishButton = () => {
>
<Button
colorScheme="blue"
isLoading={isPublishing}
isLoading={isPublishing || isSavingLoading}
isDisabled={isPublished}
onClick={handlePublishClick}
borderRightRadius={publishedTypebot && !isPublished ? 0 : undefined}
Expand Down
15 changes: 10 additions & 5 deletions apps/builder/contexts/TypebotContext/TypebotContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,12 @@ export const TypebotContext = ({
}

const savePublishedTypebot = async (newPublishedTypebot: PublicTypebot) => {
if (!localTypebot) return
setIsPublishing(true)
const { error } = await updatePublishedTypebot(
newPublishedTypebot.id,
newPublishedTypebot
newPublishedTypebot,
localTypebot.workspaceId
)
setIsPublishing(false)
if (error)
Expand Down Expand Up @@ -303,10 +305,13 @@ export const TypebotContext = ({
})
} else {
setIsPublishing(true)
const { data, error } = await createPublishedTypebot({
...parseTypebotToPublicTypebot(newLocalTypebot),
id: publishedTypebotId,
})
const { data, error } = await createPublishedTypebot(
{
...parseTypebotToPublicTypebot(newLocalTypebot),
id: publishedTypebotId,
},
localTypebot.workspaceId
)
setIsPublishing(false)
if (error)
return showToast({ title: error.name, description: error.message })
Expand Down
17 changes: 15 additions & 2 deletions apps/builder/pages/api/publicTypebots.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
import { withSentry } from '@sentry/nextjs'
import prisma from 'libs/prisma'
import { InputBlockType, PublicTypebot } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { canPublishFileInput } from 'services/api/dbRules'
import { getAuthenticatedUser } from 'services/api/utils'
import { methodNotAllowed, notAuthenticated } from 'utils'
import { badRequest, methodNotAllowed, notAuthenticated } from 'utils'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req)
if (!user) return notAuthenticated(res)
try {
if (req.method === 'POST') {
const data =
const workspaceId = req.query.workspaceId as string | undefined
if (!workspaceId) return badRequest(res, 'workspaceId is required')
const data = (
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
) as PublicTypebot
const typebotContainsFileInput = data.groups
.flatMap((g) => g.blocks)
.some((b) => b.type === InputBlockType.FILE)
if (
typebotContainsFileInput &&
!(await canPublishFileInput({ userId: user.id, workspaceId, res }))
)
return
const typebot = await prisma.publicTypebot.create({
data: { ...data },
})
Expand Down
21 changes: 18 additions & 3 deletions apps/builder/pages/api/publicTypebots/[id].ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
import { withSentry } from '@sentry/nextjs'
import prisma from 'libs/prisma'
import { InputBlockType, PublicTypebot } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { canPublishFileInput } from 'services/api/dbRules'
import { getAuthenticatedUser } from 'services/api/utils'
import { methodNotAllowed, notAuthenticated } from 'utils'
import { badRequest, methodNotAllowed, notAuthenticated } from 'utils'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req)
if (!user) return notAuthenticated(res)

const id = req.query.id.toString()
const id = req.query.id as string
const workspaceId = req.query.workspaceId as string | undefined

if (req.method === 'PUT') {
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
const data = (
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
) as PublicTypebot
if (!workspaceId) return badRequest(res, 'workspaceId is required')
const typebotContainsFileInput = data.groups
.flatMap((g) => g.blocks)
.some((b) => b.type === InputBlockType.FILE)
if (
typebotContainsFileInput &&
!(await canPublishFileInput({ userId: user.id, workspaceId, res }))
)
return
const typebots = await prisma.publicTypebot.update({
where: { id },
data,
Expand Down
31 changes: 31 additions & 0 deletions apps/builder/playwright/tests/inputs/file.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import test, { expect } from '@playwright/test'
import {
createTypebots,
freeWorkspaceId,
parseDefaultGroupWithBlock,
} from '../../services/database'
import { defaultFileInputOptions, InputBlockType } from 'models'
import { typebotViewer } from '../../services/selectorUtils'
import cuid from 'cuid'
import path from 'path'

test.describe.configure({ mode: 'parallel' })

test('options should work', async ({ page }) => {
const typebotId = cuid()
await createTypebots([
Expand Down Expand Up @@ -49,3 +52,31 @@ test('options should work', async ({ page }) => {
typebotViewer(page).locator(`text="3 files uploaded"`)
).toBeVisible()
})

test.describe('Free workspace', () => {
test.use({
storageState: path.join(__dirname, '../../freeUser.json'),
})
test("shouldn't be able to publish typebot", async ({ page }) => {
const typebotId = cuid()
await createTypebots([
{
id: typebotId,
...parseDefaultGroupWithBlock({
type: InputBlockType.FILE,
options: defaultFileInputOptions,
}),
workspaceId: freeWorkspaceId,
},
])
await page.goto(`/typebots/${typebotId}/edit`)
await page.click('text="Collect file"')
await page.click('text="Allow multiple files?"')
await page.click('text="Publish"')
await expect(
page.locator(
'text="You need to upgrade your plan in order to use file input blocks"'
)
).toBeVisible()
})
})
30 changes: 28 additions & 2 deletions apps/builder/services/api/dbRules.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { CollaborationType, Prisma, User, WorkspaceRole } from 'db'
import { isNotEmpty } from 'utils'
import { CollaborationType, Plan, Prisma, User, WorkspaceRole } from 'db'
import prisma from 'libs/prisma'
import { NextApiResponse } from 'next'
import { forbidden, isNotEmpty } from 'utils'

const parseWhereFilter = (
typebotIds: string[] | string,
Expand Down Expand Up @@ -51,3 +53,27 @@ export const canEditGuests = (user: User, typebotId: string) => ({
},
},
})

export const canPublishFileInput = async ({
userId,
workspaceId,
res,
}: {
userId: string
workspaceId: string
res: NextApiResponse
}) => {
const workspace = await prisma.workspace.findFirst({
where: { id: workspaceId, members: { some: { userId } } },
select: { plan: true },
})
if (!workspace) {
forbidden(res, 'workspace not found')
return false
}
if (workspace?.plan === Plan.FREE) {
forbidden(res, 'You need to upgrade your plan to use this feature')
return false
}
return true
}
12 changes: 8 additions & 4 deletions apps/builder/services/publicTypebot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,23 @@ export const parsePublicTypebotToTypebot = (
workspaceId: existingTypebot.workspaceId,
})

export const createPublishedTypebot = async (typebot: PublicTypebot) =>
export const createPublishedTypebot = async (
typebot: PublicTypebot,
workspaceId: string
) =>
sendRequest<PublicTypebot>({
url: `/api/publicTypebots`,
url: `/api/publicTypebots?workspaceId=${workspaceId}`,
method: 'POST',
body: typebot,
})

export const updatePublishedTypebot = async (
id: string,
typebot: Omit<PublicTypebot, 'id'>
typebot: Omit<PublicTypebot, 'id'>,
workspaceId: string
) =>
sendRequest({
url: `/api/publicTypebots/${id}`,
url: `/api/publicTypebots/${id}?workspaceId=${workspaceId}`,
method: 'PUT',
body: typebot,
})

4 comments on commit 14afd22

@vercel
Copy link

@vercel vercel bot commented on 14afd22 Jun 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 14afd22 Jun 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 14afd22 Jun 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-git-main-typebot-io.vercel.app
app.typebot.io
builder-v2-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 14afd22 Jun 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

landing-page-v2 – ./apps/landing-page

landing-page-v2-git-main-typebot-io.vercel.app
www.typebot.io
www.get-typebot.com
typebot.io
landing-page-v2-typebot-io.vercel.app
get-typebot.com

Please sign in to comment.