✨ Introducing Radar, fraud detection
This commit is contained in:
@@ -101,6 +101,7 @@
|
|||||||
"@typebot.io/prisma": "workspace:*",
|
"@typebot.io/prisma": "workspace:*",
|
||||||
"@typebot.io/schemas": "workspace:*",
|
"@typebot.io/schemas": "workspace:*",
|
||||||
"@typebot.io/tsconfig": "workspace:*",
|
"@typebot.io/tsconfig": "workspace:*",
|
||||||
|
"@typebot.io/radar": "workspace:*",
|
||||||
"@types/canvas-confetti": "1.6.0",
|
"@types/canvas-confetti": "1.6.0",
|
||||||
"@types/jsonwebtoken": "9.0.2",
|
"@types/jsonwebtoken": "9.0.2",
|
||||||
"@types/micro-cors": "0.1.3",
|
"@types/micro-cors": "0.1.3",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ type UpdateTypebotPayload = Partial<
|
|||||||
| 'resultsTablePreferences'
|
| 'resultsTablePreferences'
|
||||||
| 'isClosed'
|
| 'isClosed'
|
||||||
| 'whatsAppCredentialsId'
|
| 'whatsAppCredentialsId'
|
||||||
|
| 'riskLevel'
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export const convertPublicTypebotToTypebot = (
|
|||||||
resultsTablePreferences: existingTypebot.resultsTablePreferences,
|
resultsTablePreferences: existingTypebot.resultsTablePreferences,
|
||||||
selectedThemeTemplateId: existingTypebot.selectedThemeTemplateId,
|
selectedThemeTemplateId: existingTypebot.selectedThemeTemplateId,
|
||||||
whatsAppCredentialsId: existingTypebot.whatsAppCredentialsId,
|
whatsAppCredentialsId: existingTypebot.whatsAppCredentialsId,
|
||||||
|
riskLevel: existingTypebot.riskLevel,
|
||||||
events: typebot.events,
|
events: typebot.events,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { migrateTypebot } from '@typebot.io/lib/migrations/migrateTypebot'
|
|||||||
const omittedProps = {
|
const omittedProps = {
|
||||||
id: true,
|
id: true,
|
||||||
whatsAppCredentialsId: true,
|
whatsAppCredentialsId: true,
|
||||||
|
riskLevel: true,
|
||||||
isClosed: true,
|
isClosed: true,
|
||||||
isArchived: true,
|
isArchived: true,
|
||||||
createdAt: true,
|
createdAt: true,
|
||||||
@@ -64,6 +65,7 @@ const migrateImportingTypebot = (
|
|||||||
whatsAppCredentialsId: null,
|
whatsAppCredentialsId: null,
|
||||||
publicId: null,
|
publicId: null,
|
||||||
folderId: null,
|
folderId: null,
|
||||||
|
riskLevel: null,
|
||||||
} satisfies Typebot
|
} satisfies Typebot
|
||||||
return migrateTypebot(fullTypebot)
|
return migrateTypebot(fullTypebot)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import { isWriteTypebotForbidden } from '../helpers/isWriteTypebotForbidden'
|
|||||||
import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEvent'
|
import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEvent'
|
||||||
import { Plan } from '@typebot.io/prisma'
|
import { Plan } from '@typebot.io/prisma'
|
||||||
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
import { InputBlockType } from '@typebot.io/schemas/features/blocks/inputs/constants'
|
||||||
|
import { computeRiskLevel } from '@typebot.io/radar'
|
||||||
|
import { env } from '@typebot.io/env'
|
||||||
|
|
||||||
export const publishTypebot = authenticatedProcedure
|
export const publishTypebot = authenticatedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -78,6 +80,52 @@ export const publishTypebot = authenticatedProcedure
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (existingTypebot.riskLevel && existingTypebot.riskLevel > 80)
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'FORBIDDEN',
|
||||||
|
message:
|
||||||
|
'Radar detected a potential malicious typebot. This bot is being manually reviewed by Fraud Prevention team.',
|
||||||
|
})
|
||||||
|
|
||||||
|
const riskLevel = computeRiskLevel({
|
||||||
|
name: existingTypebot.name,
|
||||||
|
groups: parseGroups(existingTypebot.groups, {
|
||||||
|
typebotVersion: existingTypebot.version,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (riskLevel > 0) {
|
||||||
|
if (env.MESSAGE_WEBHOOK_URL)
|
||||||
|
await fetch(env.MESSAGE_WEBHOOK_URL, {
|
||||||
|
method: 'POST',
|
||||||
|
body: `🚨 *Radar detected a potential malicious typebot* 🚨\n\n*Typebot:* ${existingTypebot.name}\n*Risk level:* ${riskLevel}/100\n*Typebot ID:* ${existingTypebot.id}\n*Workspace ID:* ${existingTypebot.workspaceId}\n*User ID:* ${user.id}`,
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error('Failed to send message', err)
|
||||||
|
})
|
||||||
|
|
||||||
|
await prisma.typebot.updateMany({
|
||||||
|
where: {
|
||||||
|
id: existingTypebot.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
riskLevel,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (riskLevel > 80) {
|
||||||
|
if (existingTypebot.publishedTypebot)
|
||||||
|
await prisma.publicTypebot.deleteMany({
|
||||||
|
where: {
|
||||||
|
id: existingTypebot.publishedTypebot.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
throw new TRPCError({
|
||||||
|
code: 'FORBIDDEN',
|
||||||
|
message:
|
||||||
|
'Radar detected a potential malicious typebot. This bot is being manually reviewed by Fraud Prevention team.',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (existingTypebot.publishedTypebot)
|
if (existingTypebot.publishedTypebot)
|
||||||
await prisma.publicTypebot.updateMany({
|
await prisma.publicTypebot.updateMany({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ const typebotUpdateSchemaPick = {
|
|||||||
customDomain: true,
|
customDomain: true,
|
||||||
isClosed: true,
|
isClosed: true,
|
||||||
whatsAppCredentialsId: true,
|
whatsAppCredentialsId: true,
|
||||||
|
riskLevel: true,
|
||||||
events: true,
|
events: true,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import prisma from '@typebot.io/lib/prisma'
|
|||||||
import { googleSheetsScopes } from './consent-url'
|
import { googleSheetsScopes } from './consent-url'
|
||||||
import { stringify } from 'querystring'
|
import { stringify } from 'querystring'
|
||||||
import { badRequest, notAuthenticated } from '@typebot.io/lib/api'
|
import { badRequest, notAuthenticated } from '@typebot.io/lib/api'
|
||||||
import { oauth2Client } from '@/lib/googleSheets'
|
|
||||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||||
import { env } from '@typebot.io/env'
|
import { env } from '@typebot.io/env'
|
||||||
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
||||||
|
import { OAuth2Client } from 'google-auth-library'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const user = await getAuthenticatedUser(req, res)
|
const user = await getAuthenticatedUser(req, res)
|
||||||
@@ -22,6 +22,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
if (!workspaceId) return badRequest(res)
|
if (!workspaceId) return badRequest(res)
|
||||||
if (!code)
|
if (!code)
|
||||||
return res.status(400).send({ message: "Bad request, couldn't get code" })
|
return res.status(400).send({ message: "Bad request, couldn't get code" })
|
||||||
|
const oauth2Client = new OAuth2Client(
|
||||||
|
env.GOOGLE_CLIENT_ID,
|
||||||
|
env.GOOGLE_CLIENT_SECRET,
|
||||||
|
`${env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
||||||
|
)
|
||||||
const { tokens } = await oauth2Client.getToken(code)
|
const { tokens } = await oauth2Client.getToken(code)
|
||||||
if (!tokens?.access_token) {
|
if (!tokens?.access_token) {
|
||||||
console.error('Error getting oAuth tokens:')
|
console.error('Error getting oAuth tokens:')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { oauth2Client } from '@/lib/googleSheets'
|
import { env } from '@typebot.io/env'
|
||||||
|
import { OAuth2Client } from 'google-auth-library'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
export const googleSheetsScopes = [
|
export const googleSheetsScopes = [
|
||||||
@@ -9,6 +10,11 @@ export const googleSheetsScopes = [
|
|||||||
|
|
||||||
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
const oauth2Client = new OAuth2Client(
|
||||||
|
env.GOOGLE_CLIENT_ID,
|
||||||
|
env.GOOGLE_CLIENT_SECRET,
|
||||||
|
`${env.NEXTAUTH_URL}/api/credentials/google-sheets/callback`
|
||||||
|
)
|
||||||
const url = oauth2Client.generateAuthUrl({
|
const url = oauth2Client.generateAuthUrl({
|
||||||
access_type: 'offline',
|
access_type: 'offline',
|
||||||
scope: googleSheetsScopes,
|
scope: googleSheetsScopes,
|
||||||
|
|||||||
@@ -15003,6 +15003,10 @@
|
|||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
|
},
|
||||||
|
"riskLevel": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -15026,7 +15030,8 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId",
|
||||||
|
"riskLevel"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
@@ -18922,6 +18927,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
|
"riskLevel": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"events": {
|
"events": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"null"
|
"null"
|
||||||
@@ -22591,6 +22600,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
},
|
},
|
||||||
|
"riskLevel": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
"events": {
|
"events": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
@@ -26396,6 +26409,10 @@
|
|||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
|
},
|
||||||
|
"riskLevel": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -26419,7 +26436,8 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId",
|
||||||
|
"riskLevel"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
@@ -30276,6 +30294,10 @@
|
|||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
|
},
|
||||||
|
"riskLevel": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -30298,7 +30320,8 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId",
|
||||||
|
"riskLevel"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
@@ -34024,6 +34047,10 @@
|
|||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
|
},
|
||||||
|
"riskLevel": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -34047,7 +34074,8 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId",
|
||||||
|
"riskLevel"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
@@ -52980,6 +53008,10 @@
|
|||||||
"whatsAppCredentialsId": {
|
"whatsAppCredentialsId": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
|
},
|
||||||
|
"riskLevel": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -53003,7 +53035,8 @@
|
|||||||
"resultsTablePreferences",
|
"resultsTablePreferences",
|
||||||
"isArchived",
|
"isArchived",
|
||||||
"isClosed",
|
"isClosed",
|
||||||
"whatsAppCredentialsId"
|
"whatsAppCredentialsId",
|
||||||
|
"riskLevel"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
|
|||||||
11
packages/env/env.ts
vendored
11
packages/env/env.ts
vendored
@@ -66,6 +66,16 @@ const baseEnv = {
|
|||||||
.default('FREE'),
|
.default('FREE'),
|
||||||
DEBUG: boolean.optional().default('false'),
|
DEBUG: boolean.optional().default('false'),
|
||||||
CHAT_API_TIMEOUT: z.coerce.number().optional(),
|
CHAT_API_TIMEOUT: z.coerce.number().optional(),
|
||||||
|
RADAR_HIGH_RISK_KEYWORDS: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.transform((val) => val.split(','))
|
||||||
|
.optional(),
|
||||||
|
RADAR_INTERMEDIATE_RISK_KEYWORDS: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.transform((val) => val.split(','))
|
||||||
|
.optional(),
|
||||||
},
|
},
|
||||||
client: {
|
client: {
|
||||||
NEXT_PUBLIC_E2E_TEST: boolean.optional(),
|
NEXT_PUBLIC_E2E_TEST: boolean.optional(),
|
||||||
@@ -294,6 +304,7 @@ const telemetryEnv = {
|
|||||||
server: {
|
server: {
|
||||||
TELEMETRY_WEBHOOK_URL: z.string().url().optional(),
|
TELEMETRY_WEBHOOK_URL: z.string().url().optional(),
|
||||||
TELEMETRY_WEBHOOK_BEARER_TOKEN: z.string().min(1).optional(),
|
TELEMETRY_WEBHOOK_BEARER_TOKEN: z.string().min(1).optional(),
|
||||||
|
MESSAGE_WEBHOOK_URL: z.string().url().optional(),
|
||||||
USER_CREATED_WEBHOOK_URL: z.string().url().optional(),
|
USER_CREATED_WEBHOOK_URL: z.string().url().optional(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const parseTestTypebot = (partialTypebot: Partial<Typebot>): Typebot => {
|
|||||||
isClosed: false,
|
isClosed: false,
|
||||||
resultsTablePreferences: null,
|
resultsTablePreferences: null,
|
||||||
whatsAppCredentialsId: null,
|
whatsAppCredentialsId: null,
|
||||||
|
riskLevel: null,
|
||||||
events:
|
events:
|
||||||
version === '6'
|
version === '6'
|
||||||
? [
|
? [
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ model Typebot {
|
|||||||
isArchived Boolean @default(false)
|
isArchived Boolean @default(false)
|
||||||
isClosed Boolean @default(false)
|
isClosed Boolean @default(false)
|
||||||
whatsAppCredentialsId String?
|
whatsAppCredentialsId String?
|
||||||
|
riskLevel Int?
|
||||||
|
|
||||||
@@index([workspaceId])
|
@@index([workspaceId])
|
||||||
@@index([folderId])
|
@@index([folderId])
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Typebot" ADD COLUMN "riskLevel" INTEGER;
|
||||||
@@ -185,6 +185,7 @@ model Typebot {
|
|||||||
isArchived Boolean @default(false)
|
isArchived Boolean @default(false)
|
||||||
isClosed Boolean @default(false)
|
isClosed Boolean @default(false)
|
||||||
whatsAppCredentialsId String?
|
whatsAppCredentialsId String?
|
||||||
|
riskLevel Int?
|
||||||
|
|
||||||
@@index([workspaceId])
|
@@index([workspaceId])
|
||||||
@@index([isArchived, createdAt(sort: Desc)])
|
@@index([isArchived, createdAt(sort: Desc)])
|
||||||
|
|||||||
56
packages/radar/index.ts
Normal file
56
packages/radar/index.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { Group } from '@typebot.io/schemas'
|
||||||
|
import { env } from '@typebot.io/env'
|
||||||
|
import { BubbleBlockType } from '@typebot.io/schemas/features/blocks/bubbles/constants'
|
||||||
|
import { TDescendant, TElement, TText } from '@udecode/plate-common'
|
||||||
|
|
||||||
|
export const computeRiskLevel = ({
|
||||||
|
name,
|
||||||
|
groups,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
groups: Group[]
|
||||||
|
}) => {
|
||||||
|
if (!env.RADAR_HIGH_RISK_KEYWORDS) return 0
|
||||||
|
if (
|
||||||
|
env.RADAR_HIGH_RISK_KEYWORDS.some((keyword) =>
|
||||||
|
name.toLowerCase().includes(keyword)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 100
|
||||||
|
let hasSuspiciousKeywords = false
|
||||||
|
for (const group of groups) {
|
||||||
|
for (const block of group.blocks) {
|
||||||
|
if (block.type !== BubbleBlockType.TEXT) continue
|
||||||
|
for (const descendant of block.content?.richText as TDescendant[]) {
|
||||||
|
if (
|
||||||
|
env.RADAR_HIGH_RISK_KEYWORDS &&
|
||||||
|
richTextElementContainsKeywords(env.RADAR_HIGH_RISK_KEYWORDS)(
|
||||||
|
descendant
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 100
|
||||||
|
if (
|
||||||
|
env.RADAR_INTERMEDIATE_RISK_KEYWORDS &&
|
||||||
|
richTextElementContainsKeywords(env.RADAR_INTERMEDIATE_RISK_KEYWORDS)(
|
||||||
|
descendant
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hasSuspiciousKeywords = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasSuspiciousKeywords ? 50 : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const richTextElementContainsKeywords =
|
||||||
|
(keywords: string[]) => (element: TElement | TText) => {
|
||||||
|
if (element.text)
|
||||||
|
return keywords.some((keyword) =>
|
||||||
|
(element.text as string).toLowerCase().includes(keyword)
|
||||||
|
)
|
||||||
|
if (element.children)
|
||||||
|
return (element.children as TDescendant[]).some(
|
||||||
|
richTextElementContainsKeywords(keywords)
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
18
packages/radar/package.json
Normal file
18
packages/radar/package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "@typebot.io/radar",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "./index.ts",
|
||||||
|
"types": "./index.ts",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@udecode/plate-common": "21.1.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@typebot.io/schemas": "workspace:*",
|
||||||
|
"@typebot.io/prisma": "workspace:*",
|
||||||
|
"@typebot.io/tsconfig": "workspace:*",
|
||||||
|
"@typebot.io/env": "workspace:*",
|
||||||
|
"typescript": "5.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,6 +55,7 @@ export const typebotV5Schema = z.preprocess(
|
|||||||
isArchived: z.boolean(),
|
isArchived: z.boolean(),
|
||||||
isClosed: z.boolean(),
|
isClosed: z.boolean(),
|
||||||
whatsAppCredentialsId: z.string().nullable(),
|
whatsAppCredentialsId: z.string().nullable(),
|
||||||
|
riskLevel: z.number().nullable(),
|
||||||
}) satisfies z.ZodType<TypebotPrisma, z.ZodTypeDef, unknown>
|
}) satisfies z.ZodType<TypebotPrisma, z.ZodTypeDef, unknown>
|
||||||
)
|
)
|
||||||
export type TypebotV5 = z.infer<typeof typebotV5Schema>
|
export type TypebotV5 = z.infer<typeof typebotV5Schema>
|
||||||
|
|||||||
25
pnpm-lock.yaml
generated
25
pnpm-lock.yaml
generated
@@ -288,6 +288,9 @@ importers:
|
|||||||
'@typebot.io/prisma':
|
'@typebot.io/prisma':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/prisma
|
version: link:../../packages/prisma
|
||||||
|
'@typebot.io/radar':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../../packages/radar
|
||||||
'@typebot.io/schemas':
|
'@typebot.io/schemas':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/schemas
|
version: link:../../packages/schemas
|
||||||
@@ -1269,6 +1272,28 @@ importers:
|
|||||||
specifier: 5.3.2
|
specifier: 5.3.2
|
||||||
version: 5.3.2
|
version: 5.3.2
|
||||||
|
|
||||||
|
packages/radar:
|
||||||
|
dependencies:
|
||||||
|
'@udecode/plate-common':
|
||||||
|
specifier: 21.1.5
|
||||||
|
version: 21.1.5(@babel/core@7.22.9)(@types/react@18.2.15)(react-dom@18.2.0)(react@18.2.0)(scheduler@0.23.0)(slate-history@0.93.0)(slate-react@0.94.2)(slate@0.94.1)
|
||||||
|
devDependencies:
|
||||||
|
'@typebot.io/env':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../env
|
||||||
|
'@typebot.io/prisma':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../prisma
|
||||||
|
'@typebot.io/schemas':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../schemas
|
||||||
|
'@typebot.io/tsconfig':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../tsconfig
|
||||||
|
typescript:
|
||||||
|
specifier: 5.3.2
|
||||||
|
version: 5.3.2
|
||||||
|
|
||||||
packages/schemas:
|
packages/schemas:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@udecode/plate-common':
|
'@udecode/plate-common':
|
||||||
|
|||||||
Reference in New Issue
Block a user