🔥 Remove streamer Pages API endpoint
This commit is contained in:
@@ -3,7 +3,7 @@ import { authenticatedProcedure } from '@/helpers/server/trpc'
|
|||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
||||||
import { decrypt } from '@typebot.io/lib/api'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import {
|
import {
|
||||||
OpenAICredentials,
|
OpenAICredentials,
|
||||||
defaultBaseUrl,
|
defaultBaseUrl,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { authenticatedProcedure } from '@/helpers/server/trpc'
|
|||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
||||||
import { decrypt } from '@typebot.io/lib/api'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { ZemanticAiCredentials } from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
import { ZemanticAiCredentials } from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
||||||
import got from 'got'
|
import got from 'got'
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { stripeCredentialsSchema } from '@typebot.io/schemas/features/blocks/inp
|
|||||||
import { googleSheetsCredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/googleSheets/schemas'
|
import { googleSheetsCredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/googleSheets/schemas'
|
||||||
import { openAICredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/openai'
|
import { openAICredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/openai'
|
||||||
import { smtpCredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/sendEmail'
|
import { smtpCredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/sendEmail'
|
||||||
import { encrypt } from '@typebot.io/lib/api/encryption'
|
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { whatsAppCredentialsSchema } from '@typebot.io/schemas/features/whatsapp'
|
import { whatsAppCredentialsSchema } from '@typebot.io/schemas/features/whatsapp'
|
||||||
import { Credentials, zemanticAiCredentialsSchema } from '@typebot.io/schemas'
|
import { Credentials, zemanticAiCredentialsSchema } from '@typebot.io/schemas'
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { authenticatedProcedure } from '@/helpers/server/trpc'
|
|||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import got from 'got'
|
import got from 'got'
|
||||||
import prisma from '@typebot.io/lib/prisma'
|
import prisma from '@typebot.io/lib/prisma'
|
||||||
import { decrypt } from '@typebot.io/lib/api'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import got from 'got'
|
|||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
||||||
import prisma from '@typebot.io/lib/prisma'
|
import prisma from '@typebot.io/lib/prisma'
|
||||||
import { decrypt } from '@typebot.io/lib/api/encryption'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
|
|
||||||
const inputSchema = z.object({
|
const inputSchema = z.object({
|
||||||
token: z.string().optional(),
|
token: z.string().optional(),
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ import { Credentials as CredentialsFromDb } from '@typebot.io/prisma'
|
|||||||
import { OAuth2Client, Credentials } from 'google-auth-library'
|
import { OAuth2Client, Credentials } from 'google-auth-library'
|
||||||
import { GoogleSheetsCredentials } from '@typebot.io/schemas'
|
import { GoogleSheetsCredentials } from '@typebot.io/schemas'
|
||||||
import { isDefined } from '@typebot.io/lib'
|
import { isDefined } from '@typebot.io/lib'
|
||||||
import { decrypt, encrypt } from '@typebot.io/lib/api'
|
|
||||||
import { env } from '@typebot.io/env'
|
import { env } from '@typebot.io/env'
|
||||||
import prisma from '@typebot.io/lib/prisma'
|
import prisma from '@typebot.io/lib/prisma'
|
||||||
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
|
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
||||||
|
|
||||||
export const oauth2Client = new OAuth2Client(
|
export const oauth2Client = new OAuth2Client(
|
||||||
env.GOOGLE_CLIENT_ID,
|
env.GOOGLE_CLIENT_ID,
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
forbidden,
|
forbidden,
|
||||||
methodNotAllowed,
|
methodNotAllowed,
|
||||||
notAuthenticated,
|
notAuthenticated,
|
||||||
encrypt,
|
|
||||||
} from '@typebot.io/lib/api'
|
} from '@typebot.io/lib/api'
|
||||||
|
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { Prisma } from '@typebot.io/prisma'
|
|||||||
import prisma from '@typebot.io/lib/prisma'
|
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, encrypt, notAuthenticated } from '@typebot.io/lib/api'
|
import { badRequest, notAuthenticated } from '@typebot.io/lib/api'
|
||||||
import { oauth2Client } from '@/lib/googleSheets'
|
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'
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import prisma from '@typebot.io/lib/prisma'
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
||||||
import {
|
import {
|
||||||
decrypt,
|
|
||||||
methodNotAllowed,
|
methodNotAllowed,
|
||||||
notAuthenticated,
|
notAuthenticated,
|
||||||
notFound,
|
notFound,
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
|
||||||
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
|
||||||
import got from 'got'
|
import got from 'got'
|
||||||
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
if (req.method === 'GET') {
|
if (req.method === 'GET') {
|
||||||
|
|||||||
1
apps/viewer/next-env.d.ts
vendored
1
apps/viewer/next-env.d.ts
vendored
@@ -1,5 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
/// <reference types="next/navigation-types/compat/navigation" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||||
|
|||||||
@@ -7,24 +7,25 @@ import OpenAI from 'openai'
|
|||||||
import { NextResponse } from 'next/dist/server/web/spec-extension/response'
|
import { NextResponse } from 'next/dist/server/web/spec-extension/response'
|
||||||
|
|
||||||
export const runtime = 'edge'
|
export const runtime = 'edge'
|
||||||
export const regions = ['lhr1']
|
export const preferredRegion = 'lhr1'
|
||||||
export const dynamic = 'force-dynamic'
|
export const dynamic = 'force-dynamic'
|
||||||
|
|
||||||
const responseHeaders = {
|
const responseHeaders = {
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Origin': '*',
|
||||||
}
|
}
|
||||||
|
|
||||||
const handler = async (req: Request) => {
|
export async function OPTIONS() {
|
||||||
if (req.method === 'OPTIONS') {
|
return new Response('ok', {
|
||||||
return new Response('ok', {
|
headers: {
|
||||||
headers: {
|
'Access-Control-Allow-Origin': '*',
|
||||||
'Access-Control-Allow-Origin': '*',
|
'Access-Control-Allow-Methods': 'POST',
|
||||||
'Access-Control-Allow-Methods': 'POST',
|
'Access-Control-Expose-Headers': 'Content-Length, X-JSON',
|
||||||
'Access-Control-Expose-Headers': 'Content-Length, X-JSON',
|
'Access-Control-Allow-Headers': '*',
|
||||||
'Access-Control-Allow-Headers': '*',
|
},
|
||||||
},
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
export async function POST(req: Request) {
|
||||||
const { sessionId, messages } = (await req.json()) as {
|
const { sessionId, messages } = (await req.json()) as {
|
||||||
sessionId: string
|
sessionId: string
|
||||||
messages: OpenAI.Chat.ChatCompletionMessage[]
|
messages: OpenAI.Chat.ChatCompletionMessage[]
|
||||||
@@ -110,5 +111,3 @@ const handler = async (req: Request) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default handler
|
|
||||||
@@ -2,9 +2,10 @@ import { Credentials as CredentialsFromDb } from '@typebot.io/prisma'
|
|||||||
import { OAuth2Client, Credentials } from 'google-auth-library'
|
import { OAuth2Client, Credentials } from 'google-auth-library'
|
||||||
import { GoogleSheetsCredentials } from '@typebot.io/schemas'
|
import { GoogleSheetsCredentials } from '@typebot.io/schemas'
|
||||||
import { isDefined } from '@typebot.io/lib'
|
import { isDefined } from '@typebot.io/lib'
|
||||||
import { decrypt, encrypt } from '@typebot.io/lib/api'
|
|
||||||
import { env } from '@typebot.io/env'
|
import { env } from '@typebot.io/env'
|
||||||
import prisma from '@typebot.io/lib/prisma'
|
import prisma from '@typebot.io/lib/prisma'
|
||||||
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
|
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
||||||
|
|
||||||
export const getAuthenticatedGoogleClient = async (
|
export const getAuthenticatedGoogleClient = async (
|
||||||
credentialsId: string
|
credentialsId: string
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
import { connect } from '@planetscale/database'
|
|
||||||
import { env } from '@typebot.io/env'
|
|
||||||
import { IntegrationBlockType, SessionState } from '@typebot.io/schemas'
|
|
||||||
import { StreamingTextResponse } from 'ai'
|
|
||||||
import { getChatCompletionStream } from '@typebot.io/bot-engine/blocks/integrations/openai/getChatCompletionStream'
|
|
||||||
import OpenAI from 'openai'
|
|
||||||
import { NextResponse } from 'next/dist/server/web/spec-extension/response'
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
runtime: 'edge',
|
|
||||||
regions: ['lhr1'],
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseHeaders = {
|
|
||||||
'Access-Control-Allow-Origin': '*',
|
|
||||||
}
|
|
||||||
|
|
||||||
const handler = async (req: Request) => {
|
|
||||||
if (req.method === 'OPTIONS') {
|
|
||||||
return new Response('ok', {
|
|
||||||
headers: {
|
|
||||||
'Access-Control-Allow-Origin': '*',
|
|
||||||
'Access-Control-Allow-Methods': 'POST',
|
|
||||||
'Access-Control-Expose-Headers': 'Content-Length, X-JSON',
|
|
||||||
'Access-Control-Allow-Headers': '*',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const { sessionId, messages } = (await req.json()) as {
|
|
||||||
sessionId: string
|
|
||||||
messages: OpenAI.Chat.ChatCompletionMessage[]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sessionId)
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: 'No session ID provided' },
|
|
||||||
{ status: 400, headers: responseHeaders }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!messages)
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: 'No messages provided' },
|
|
||||||
{ status: 400, headers: responseHeaders }
|
|
||||||
)
|
|
||||||
|
|
||||||
const conn = connect({ url: env.DATABASE_URL })
|
|
||||||
|
|
||||||
const chatSession = await conn.execute(
|
|
||||||
'select state from ChatSession where id=?',
|
|
||||||
[sessionId]
|
|
||||||
)
|
|
||||||
|
|
||||||
const state = (chatSession.rows.at(0) as { state: SessionState } | undefined)
|
|
||||||
?.state
|
|
||||||
|
|
||||||
if (!state)
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: 'No state found' },
|
|
||||||
{ status: 400, headers: responseHeaders }
|
|
||||||
)
|
|
||||||
|
|
||||||
const group = state.typebotsQueue[0].typebot.groups.find(
|
|
||||||
(group) => group.id === state.currentBlock?.groupId
|
|
||||||
)
|
|
||||||
const blockIndex =
|
|
||||||
group?.blocks.findIndex(
|
|
||||||
(block) => block.id === state.currentBlock?.blockId
|
|
||||||
) ?? -1
|
|
||||||
|
|
||||||
const block = blockIndex >= 0 ? group?.blocks[blockIndex ?? 0] : null
|
|
||||||
|
|
||||||
if (!block || !group)
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: 'Current block not found' },
|
|
||||||
{ status: 400, headers: responseHeaders }
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
block.type !== IntegrationBlockType.OPEN_AI ||
|
|
||||||
block.options.task !== 'Create chat completion'
|
|
||||||
)
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: 'Current block is not an OpenAI block' },
|
|
||||||
{ status: 400, headers: responseHeaders }
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const stream = await getChatCompletionStream(conn)(
|
|
||||||
state,
|
|
||||||
block.options,
|
|
||||||
messages
|
|
||||||
)
|
|
||||||
if (!stream)
|
|
||||||
return NextResponse.json(
|
|
||||||
{ message: 'Could not create stream' },
|
|
||||||
{ status: 400, headers: responseHeaders }
|
|
||||||
)
|
|
||||||
|
|
||||||
return new StreamingTextResponse(stream, {
|
|
||||||
headers: responseHeaders,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof OpenAI.APIError) {
|
|
||||||
const { name, status, message } = error
|
|
||||||
return NextResponse.json(
|
|
||||||
{ name, status, message },
|
|
||||||
{ status, headers: responseHeaders }
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handler
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import {
|
import {
|
||||||
badRequest,
|
badRequest,
|
||||||
decrypt,
|
|
||||||
forbidden,
|
forbidden,
|
||||||
initMiddleware,
|
initMiddleware,
|
||||||
methodNotAllowed,
|
methodNotAllowed,
|
||||||
} from '@typebot.io/lib/api'
|
} from '@typebot.io/lib/api'
|
||||||
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import Stripe from 'stripe'
|
import Stripe from 'stripe'
|
||||||
|
|
||||||
import Cors from 'cors'
|
import Cors from 'cors'
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { NextApiRequest, NextApiResponse } from 'next'
|
|||||||
import { createTransport, getTestMessageUrl } from 'nodemailer'
|
import { createTransport, getTestMessageUrl } from 'nodemailer'
|
||||||
import { isDefined, isEmpty, isNotDefined, omit } from '@typebot.io/lib'
|
import { isDefined, isEmpty, isNotDefined, omit } from '@typebot.io/lib'
|
||||||
import { parseAnswers } from '@typebot.io/lib/results'
|
import { parseAnswers } from '@typebot.io/lib/results'
|
||||||
import { methodNotAllowed, initMiddleware, decrypt } from '@typebot.io/lib/api'
|
import { methodNotAllowed, initMiddleware } from '@typebot.io/lib/api'
|
||||||
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
|
|
||||||
import Cors from 'cors'
|
import Cors from 'cors'
|
||||||
import Mail from 'nodemailer/lib/mailer'
|
import Mail from 'nodemailer/lib/mailer'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PrismaClient } from '@typebot.io/prisma'
|
import { PrismaClient } from '@typebot.io/prisma'
|
||||||
import { SmtpCredentials } from '@typebot.io/schemas'
|
import { SmtpCredentials } from '@typebot.io/schemas'
|
||||||
import { encrypt } from '@typebot.io/lib/api'
|
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
||||||
import { proWorkspaceId } from '@typebot.io/lib/playwright/databaseSetup'
|
import { proWorkspaceId } from '@typebot.io/lib/playwright/databaseSetup'
|
||||||
|
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|||||||
@@ -3,8 +3,21 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"]
|
"@/*": [
|
||||||
}
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"strictNullChecks": true
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
StripeCredentials,
|
StripeCredentials,
|
||||||
} from '@typebot.io/schemas'
|
} from '@typebot.io/schemas'
|
||||||
import Stripe from 'stripe'
|
import Stripe from 'stripe'
|
||||||
import { decrypt } from '@typebot.io/lib/api/encryption'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { parseVariables } from '../../../variables/parseVariables'
|
import { parseVariables } from '../../../variables/parseVariables'
|
||||||
import prisma from '@typebot.io/lib/prisma'
|
import prisma from '@typebot.io/lib/prisma'
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
import { env } from '@typebot.io/env'
|
import { env } from '@typebot.io/env'
|
||||||
import { decrypt, encrypt } from '@typebot.io/lib/api/encryption'
|
import { encrypt } from '@typebot.io/lib/api/encryption/encrypt'
|
||||||
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { isDefined } from '@typebot.io/lib/utils'
|
import { isDefined } from '@typebot.io/lib/utils'
|
||||||
import { GoogleSheetsCredentials } from '@typebot.io/schemas/features/blocks/integrations/googleSheets/schemas'
|
import { GoogleSheetsCredentials } from '@typebot.io/schemas/features/blocks/integrations/googleSheets/schemas'
|
||||||
import { Credentials as CredentialsFromDb } from '@typebot.io/prisma'
|
import { Credentials as CredentialsFromDb } from '@typebot.io/prisma'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Block,
|
Block,
|
||||||
BubbleBlockType,
|
BubbleBlockType,
|
||||||
|
Credentials,
|
||||||
SessionState,
|
SessionState,
|
||||||
TypebotInSession,
|
TypebotInSession,
|
||||||
} from '@typebot.io/schemas'
|
} from '@typebot.io/schemas'
|
||||||
@@ -10,7 +11,7 @@ import {
|
|||||||
chatCompletionMessageRoles,
|
chatCompletionMessageRoles,
|
||||||
} from '@typebot.io/schemas/features/blocks/integrations/openai'
|
} from '@typebot.io/schemas/features/blocks/integrations/openai'
|
||||||
import { byId, isEmpty } from '@typebot.io/lib'
|
import { byId, isEmpty } from '@typebot.io/lib'
|
||||||
import { decrypt, isCredentialsV2 } from '@typebot.io/lib/api/encryption'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { resumeChatCompletion } from './resumeChatCompletion'
|
import { resumeChatCompletion } from './resumeChatCompletion'
|
||||||
import { parseChatCompletionMessages } from './parseChatCompletionMessages'
|
import { parseChatCompletionMessages } from './parseChatCompletionMessages'
|
||||||
import { executeChatCompletionOpenAIRequest } from './executeChatCompletionOpenAIRequest'
|
import { executeChatCompletionOpenAIRequest } from './executeChatCompletionOpenAIRequest'
|
||||||
@@ -167,3 +168,6 @@ const getNextBlock =
|
|||||||
)
|
)
|
||||||
: connectedGroup?.blocks.at(0)
|
: connectedGroup?.blocks.at(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isCredentialsV2 = (credentials: Pick<Credentials, 'iv'>) =>
|
||||||
|
credentials.iv.length === 24
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Connection } from '@planetscale/database'
|
import { Connection } from '@planetscale/database'
|
||||||
import { decrypt } from '@typebot.io/lib/api/encryption'
|
import { decryptV2 } from '@typebot.io/lib/api/encryption/decryptV2'
|
||||||
import { isNotEmpty } from '@typebot.io/lib/utils'
|
import { isNotEmpty } from '@typebot.io/lib/utils'
|
||||||
import {
|
import {
|
||||||
ChatCompletionOpenAIOptions,
|
ChatCompletionOpenAIOptions,
|
||||||
@@ -27,7 +27,7 @@ export const getChatCompletionStream =
|
|||||||
console.error('Could not find credentials in database')
|
console.error('Could not find credentials in database')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const { apiKey } = (await decrypt(
|
const { apiKey } = (await decryptV2(
|
||||||
credentials.data,
|
credentials.data,
|
||||||
credentials.iv
|
credentials.iv
|
||||||
)) as OpenAICredentials['data']
|
)) as OpenAICredentials['data']
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { createTransport } from 'nodemailer'
|
|||||||
import Mail from 'nodemailer/lib/mailer'
|
import Mail from 'nodemailer/lib/mailer'
|
||||||
import { byId, isDefined, isEmpty, isNotDefined, omit } from '@typebot.io/lib'
|
import { byId, isDefined, isEmpty, isNotDefined, omit } from '@typebot.io/lib'
|
||||||
import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
||||||
import { decrypt } from '@typebot.io/lib/api'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { defaultFrom, defaultTransportOptions } from './constants'
|
import { defaultFrom, defaultTransportOptions } from './constants'
|
||||||
import { findUniqueVariableValue } from '../../../variables/findUniqueVariableValue'
|
import { findUniqueVariableValue } from '../../../variables/findUniqueVariableValue'
|
||||||
import { env } from '@typebot.io/env'
|
import { env } from '@typebot.io/env'
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
ZemanticAiResponse,
|
ZemanticAiResponse,
|
||||||
} from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
} from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
|
||||||
import got from 'got'
|
import got from 'got'
|
||||||
import { decrypt } from '@typebot.io/lib/api/encryption'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { byId, isDefined, isEmpty } from '@typebot.io/lib'
|
import { byId, isDefined, isEmpty } from '@typebot.io/lib'
|
||||||
import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
import { getDefinedVariables, parseAnswers } from '@typebot.io/lib/results'
|
||||||
import prisma from '@typebot.io/lib/prisma'
|
import prisma from '@typebot.io/lib/prisma'
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { sendChatReplyToWhatsApp } from './sendChatReplyToWhatsApp'
|
|||||||
import { startWhatsAppSession } from './startWhatsAppSession'
|
import { startWhatsAppSession } from './startWhatsAppSession'
|
||||||
import { getSession } from '../queries/getSession'
|
import { getSession } from '../queries/getSession'
|
||||||
import { continueBotFlow } from '../continueBotFlow'
|
import { continueBotFlow } from '../continueBotFlow'
|
||||||
import { decrypt } from '@typebot.io/lib/api'
|
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
|
||||||
import { saveStateToDatabase } from '../saveStateToDatabase'
|
import { saveStateToDatabase } from '../saveStateToDatabase'
|
||||||
import prisma from '@typebot.io/lib/prisma'
|
import prisma from '@typebot.io/lib/prisma'
|
||||||
import { isDefined } from '@typebot.io/lib/utils'
|
import { isDefined } from '@typebot.io/lib/utils'
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
import { Credentials } from '@typebot.io/schemas/features/credentials'
|
|
||||||
import { decryptV1 } from './encryptionV1'
|
|
||||||
import { env } from '@typebot.io/env'
|
|
||||||
|
|
||||||
const algorithm = 'AES-GCM'
|
|
||||||
const secretKey = env.ENCRYPTION_SECRET
|
|
||||||
|
|
||||||
export const encrypt = async (
|
|
||||||
data: object
|
|
||||||
): Promise<{ encryptedData: string; iv: string }> => {
|
|
||||||
if (!secretKey) throw new Error('ENCRYPTION_SECRET is not in environment')
|
|
||||||
const iv = crypto.getRandomValues(new Uint8Array(12))
|
|
||||||
const encodedData = new TextEncoder().encode(JSON.stringify(data))
|
|
||||||
|
|
||||||
const key = await crypto.subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
new TextEncoder().encode(secretKey),
|
|
||||||
algorithm,
|
|
||||||
false,
|
|
||||||
['encrypt']
|
|
||||||
)
|
|
||||||
|
|
||||||
const encryptedBuffer = await crypto.subtle.encrypt(
|
|
||||||
{ name: algorithm, iv },
|
|
||||||
key,
|
|
||||||
encodedData
|
|
||||||
)
|
|
||||||
|
|
||||||
const encryptedData = btoa(
|
|
||||||
String.fromCharCode.apply(null, Array.from(new Uint8Array(encryptedBuffer)))
|
|
||||||
)
|
|
||||||
|
|
||||||
const ivHex = Array.from(iv)
|
|
||||||
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
||||||
.join('')
|
|
||||||
|
|
||||||
return {
|
|
||||||
encryptedData,
|
|
||||||
iv: ivHex,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const decrypt = async (
|
|
||||||
encryptedData: string,
|
|
||||||
ivHex: string
|
|
||||||
): Promise<object> => {
|
|
||||||
if (ivHex.length !== 24) return decryptV1(encryptedData, ivHex)
|
|
||||||
if (!secretKey) throw new Error('ENCRYPTION_SECRET is not in environment')
|
|
||||||
const iv = new Uint8Array(
|
|
||||||
ivHex.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? []
|
|
||||||
)
|
|
||||||
|
|
||||||
const key = await crypto.subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
new TextEncoder().encode(secretKey),
|
|
||||||
algorithm,
|
|
||||||
false,
|
|
||||||
['decrypt']
|
|
||||||
)
|
|
||||||
|
|
||||||
const encryptedBuffer = new Uint8Array(
|
|
||||||
Array.from(atob(encryptedData)).map((char) => char.charCodeAt(0))
|
|
||||||
)
|
|
||||||
|
|
||||||
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
||||||
{ name: algorithm, iv },
|
|
||||||
key,
|
|
||||||
encryptedBuffer
|
|
||||||
)
|
|
||||||
|
|
||||||
const decryptedData = new TextDecoder().decode(decryptedBuffer)
|
|
||||||
return JSON.parse(decryptedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isCredentialsV2 = (credentials: Pick<Credentials, 'iv'>) =>
|
|
||||||
credentials.iv.length === 24
|
|
||||||
10
packages/lib/api/encryption/decrypt.ts
Normal file
10
packages/lib/api/encryption/decrypt.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { decryptV1 } from './decryptV1'
|
||||||
|
import { decryptV2 } from './decryptV2'
|
||||||
|
|
||||||
|
export const decrypt = async (
|
||||||
|
encryptedData: string,
|
||||||
|
ivHex: string
|
||||||
|
): Promise<object> => {
|
||||||
|
if (ivHex.length !== 24) return decryptV1(encryptedData, ivHex)
|
||||||
|
return decryptV2(encryptedData, ivHex)
|
||||||
|
}
|
||||||
35
packages/lib/api/encryption/decryptV2.ts
Normal file
35
packages/lib/api/encryption/decryptV2.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { env } from '@typebot.io/env'
|
||||||
|
|
||||||
|
const algorithm = 'AES-GCM'
|
||||||
|
const secretKey = env.ENCRYPTION_SECRET
|
||||||
|
|
||||||
|
export const decryptV2 = async (
|
||||||
|
encryptedData: string,
|
||||||
|
ivHex: string
|
||||||
|
): Promise<object> => {
|
||||||
|
if (!secretKey) throw new Error('ENCRYPTION_SECRET is not in environment')
|
||||||
|
const iv = new Uint8Array(
|
||||||
|
ivHex.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) ?? []
|
||||||
|
)
|
||||||
|
|
||||||
|
const key = await crypto.subtle.importKey(
|
||||||
|
'raw',
|
||||||
|
new TextEncoder().encode(secretKey),
|
||||||
|
algorithm,
|
||||||
|
false,
|
||||||
|
['decrypt']
|
||||||
|
)
|
||||||
|
|
||||||
|
const encryptedBuffer = new Uint8Array(
|
||||||
|
Array.from(atob(encryptedData)).map((char) => char.charCodeAt(0))
|
||||||
|
)
|
||||||
|
|
||||||
|
const decryptedBuffer = await crypto.subtle.decrypt(
|
||||||
|
{ name: algorithm, iv },
|
||||||
|
key,
|
||||||
|
encryptedBuffer
|
||||||
|
)
|
||||||
|
|
||||||
|
const decryptedData = new TextDecoder().decode(decryptedBuffer)
|
||||||
|
return JSON.parse(decryptedData)
|
||||||
|
}
|
||||||
39
packages/lib/api/encryption/encrypt.ts
Normal file
39
packages/lib/api/encryption/encrypt.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { env } from '@typebot.io/env'
|
||||||
|
|
||||||
|
const algorithm = 'AES-GCM'
|
||||||
|
const secretKey = env.ENCRYPTION_SECRET
|
||||||
|
|
||||||
|
export const encrypt = async (
|
||||||
|
data: object
|
||||||
|
): Promise<{ encryptedData: string; iv: string }> => {
|
||||||
|
if (!secretKey) throw new Error('ENCRYPTION_SECRET is not in environment')
|
||||||
|
const iv = crypto.getRandomValues(new Uint8Array(12))
|
||||||
|
const encodedData = new TextEncoder().encode(JSON.stringify(data))
|
||||||
|
|
||||||
|
const key = await crypto.subtle.importKey(
|
||||||
|
'raw',
|
||||||
|
new TextEncoder().encode(secretKey),
|
||||||
|
algorithm,
|
||||||
|
false,
|
||||||
|
['encrypt']
|
||||||
|
)
|
||||||
|
|
||||||
|
const encryptedBuffer = await crypto.subtle.encrypt(
|
||||||
|
{ name: algorithm, iv },
|
||||||
|
key,
|
||||||
|
encodedData
|
||||||
|
)
|
||||||
|
|
||||||
|
const encryptedData = btoa(
|
||||||
|
String.fromCharCode.apply(null, Array.from(new Uint8Array(encryptedBuffer)))
|
||||||
|
)
|
||||||
|
|
||||||
|
const ivHex = Array.from(iv)
|
||||||
|
.map((byte) => byte.toString(16).padStart(2, '0'))
|
||||||
|
.join('')
|
||||||
|
|
||||||
|
return {
|
||||||
|
encryptedData,
|
||||||
|
iv: ivHex,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1 @@
|
|||||||
export * from './utils'
|
export * from './utils'
|
||||||
export * from './encryption'
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
PrismaClient,
|
PrismaClient,
|
||||||
WorkspaceRole,
|
WorkspaceRole,
|
||||||
} from '@typebot.io/prisma'
|
} from '@typebot.io/prisma'
|
||||||
import { encrypt } from '../api'
|
import { encrypt } from '../api/encryption/encrypt'
|
||||||
|
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user