✨ (preview) Add preview runtime dropdown
User can select between Web and API previews Closes #247
This commit is contained in:
@@ -27,7 +27,7 @@ import {
|
||||
setResultAsCompleted,
|
||||
startBotFlow,
|
||||
} from '../utils'
|
||||
import { omit } from 'utils'
|
||||
import { env, omit } from 'utils'
|
||||
|
||||
export const sendMessageProcedure = publicProcedure
|
||||
.meta({
|
||||
@@ -41,61 +41,63 @@ export const sendMessageProcedure = publicProcedure
|
||||
})
|
||||
.input(sendMessageInputSchema)
|
||||
.output(chatReplySchema)
|
||||
.query(async ({ input: { sessionId, message, startParams } }) => {
|
||||
const session = sessionId ? await getSession(sessionId) : null
|
||||
.query(
|
||||
async ({ input: { sessionId, message, startParams }, ctx: { user } }) => {
|
||||
const session = sessionId ? await getSession(sessionId) : null
|
||||
|
||||
if (!session) {
|
||||
const {
|
||||
sessionId,
|
||||
typebot,
|
||||
messages,
|
||||
input,
|
||||
resultId,
|
||||
dynamicTheme,
|
||||
logs,
|
||||
clientSideActions,
|
||||
} = await startSession(startParams)
|
||||
return {
|
||||
sessionId,
|
||||
typebot: typebot
|
||||
? {
|
||||
id: typebot.id,
|
||||
theme: typebot.theme,
|
||||
settings: typebot.settings,
|
||||
}
|
||||
: undefined,
|
||||
messages,
|
||||
input,
|
||||
resultId,
|
||||
dynamicTheme,
|
||||
logs,
|
||||
clientSideActions,
|
||||
}
|
||||
} else {
|
||||
const { messages, input, clientSideActions, newSessionState, logs } =
|
||||
await continueBotFlow(session.state)(message)
|
||||
if (!session) {
|
||||
const {
|
||||
sessionId,
|
||||
typebot,
|
||||
messages,
|
||||
input,
|
||||
resultId,
|
||||
dynamicTheme,
|
||||
logs,
|
||||
clientSideActions,
|
||||
} = await startSession(startParams, user?.id)
|
||||
return {
|
||||
sessionId,
|
||||
typebot: typebot
|
||||
? {
|
||||
id: typebot.id,
|
||||
theme: typebot.theme,
|
||||
settings: typebot.settings,
|
||||
}
|
||||
: undefined,
|
||||
messages,
|
||||
input,
|
||||
resultId,
|
||||
dynamicTheme,
|
||||
logs,
|
||||
clientSideActions,
|
||||
}
|
||||
} else {
|
||||
const { messages, input, clientSideActions, newSessionState, logs } =
|
||||
await continueBotFlow(session.state)(message)
|
||||
|
||||
await prisma.chatSession.updateMany({
|
||||
where: { id: session.id },
|
||||
data: {
|
||||
state: newSessionState,
|
||||
},
|
||||
})
|
||||
await prisma.chatSession.updateMany({
|
||||
where: { id: session.id },
|
||||
data: {
|
||||
state: newSessionState,
|
||||
},
|
||||
})
|
||||
|
||||
if (!input && session.state.result?.hasStarted)
|
||||
await setResultAsCompleted(session.state.result.id)
|
||||
if (!input && session.state.result?.hasStarted)
|
||||
await setResultAsCompleted(session.state.result.id)
|
||||
|
||||
return {
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
dynamicTheme: parseDynamicThemeReply(newSessionState),
|
||||
logs,
|
||||
return {
|
||||
messages,
|
||||
input,
|
||||
clientSideActions,
|
||||
dynamicTheme: parseDynamicThemeReply(newSessionState),
|
||||
logs,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const startSession = async (startParams?: StartParams) => {
|
||||
const startSession = async (startParams?: StartParams, userId?: string) => {
|
||||
if (!startParams?.typebot)
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
@@ -105,7 +107,7 @@ const startSession = async (startParams?: StartParams) => {
|
||||
const isPreview =
|
||||
startParams?.isPreview || typeof startParams?.typebot !== 'string'
|
||||
|
||||
const typebot = await getTypebot(startParams)
|
||||
const typebot = await getTypebot(startParams, userId)
|
||||
|
||||
const startVariables = startParams.prefilledVariables
|
||||
? parsePrefilledVariables(typebot.variables, startParams.prefilledVariables)
|
||||
@@ -198,14 +200,16 @@ const startSession = async (startParams?: StartParams) => {
|
||||
} satisfies ChatReply
|
||||
}
|
||||
|
||||
const getTypebot = async ({
|
||||
typebot,
|
||||
isPreview,
|
||||
}: Pick<StartParams, 'typebot' | 'isPreview'>): Promise<StartTypebot> => {
|
||||
const getTypebot = async (
|
||||
{ typebot, isPreview }: Pick<StartParams, 'typebot' | 'isPreview'>,
|
||||
userId?: string
|
||||
): Promise<StartTypebot> => {
|
||||
if (typeof typebot !== 'string') return typebot
|
||||
if (isPreview && !userId && env('E2E_TEST') !== 'true')
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })
|
||||
const typebotQuery = isPreview
|
||||
? await prisma.typebot.findUnique({
|
||||
where: { id: typebot },
|
||||
? await prisma.typebot.findFirst({
|
||||
where: { id: typebot, workspace: { members: { some: { userId } } } },
|
||||
select: {
|
||||
id: true,
|
||||
groups: true,
|
||||
|
||||
@@ -3,12 +3,14 @@ import { captureException } from '@sentry/nextjs'
|
||||
import { createOpenApiNextHandler } from 'trpc-openapi'
|
||||
import cors from 'nextjs-cors'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { createContext } from '@/utils/server/context'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
await cors(req, res)
|
||||
|
||||
return createOpenApiNextHandler({
|
||||
router: appRouter,
|
||||
createContext,
|
||||
onError({ error }) {
|
||||
if (error.code === 'INTERNAL_SERVER_ERROR') {
|
||||
captureException(error)
|
||||
|
||||
35
apps/viewer/src/utils/server/context.ts
Normal file
35
apps/viewer/src/utils/server/context.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { inferAsyncReturnType } from '@trpc/server'
|
||||
import * as trpcNext from '@trpc/server/adapters/next'
|
||||
import { User } from 'db'
|
||||
import { NextApiRequest } from 'next'
|
||||
|
||||
export async function createContext(opts: trpcNext.CreateNextContextOptions) {
|
||||
const user = await getAuthenticatedUser(opts.req)
|
||||
|
||||
return {
|
||||
user,
|
||||
}
|
||||
}
|
||||
|
||||
const getAuthenticatedUser = async (
|
||||
req: NextApiRequest
|
||||
): Promise<User | undefined> => {
|
||||
const bearerToken = extractBearerToken(req)
|
||||
if (!bearerToken) return
|
||||
return authenticateByToken(bearerToken)
|
||||
}
|
||||
|
||||
const authenticateByToken = async (
|
||||
apiToken: string
|
||||
): Promise<User | undefined> => {
|
||||
if (typeof window !== 'undefined') return
|
||||
return (await prisma.user.findFirst({
|
||||
where: { apiTokens: { some: { token: apiToken } } },
|
||||
})) as User
|
||||
}
|
||||
|
||||
const extractBearerToken = (req: NextApiRequest) =>
|
||||
req.headers['authorization']?.slice(7)
|
||||
|
||||
export type Context = inferAsyncReturnType<typeof createContext>
|
||||
@@ -1,13 +1,22 @@
|
||||
import { initTRPC } from '@trpc/server'
|
||||
import { OpenApiMeta } from 'trpc-openapi'
|
||||
import superjson from 'superjson'
|
||||
import { Context } from './context'
|
||||
|
||||
const t = initTRPC.meta<OpenApiMeta>().create({
|
||||
const t = initTRPC.context<Context>().meta<OpenApiMeta>().create({
|
||||
transformer: superjson,
|
||||
})
|
||||
|
||||
const injectUser = t.middleware(({ next, ctx }) => {
|
||||
return next({
|
||||
ctx: {
|
||||
user: ctx.user,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
export const middleware = t.middleware
|
||||
|
||||
export const router = t.router
|
||||
|
||||
export const publicProcedure = t.procedure
|
||||
export const publicProcedure = t.procedure.use(injectUser)
|
||||
|
||||
Reference in New Issue
Block a user