@ -4,7 +4,7 @@ import { useTypebot } from '@/providers/TypebotProvider'
|
||||
import { InputSubmitContent } from '@/types'
|
||||
import { defaultFileInputOptions, FileInputBlock } from '@typebot.io/schemas'
|
||||
import React, { ChangeEvent, FormEvent, useState, DragEvent } from 'react'
|
||||
import { uploadFiles } from '@typebot.io/lib/s3/uploadFiles'
|
||||
import { uploadFiles } from '../helpers/uploadFiles'
|
||||
|
||||
type Props = {
|
||||
block: FileInputBlock
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { sendRequest } from '../utils'
|
||||
import { sendRequest } from '@typebot.io/lib/utils'
|
||||
|
||||
type UploadFileProps = {
|
||||
basePath?: string
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/js",
|
||||
"version": "0.1.25",
|
||||
"version": "0.1.26",
|
||||
"description": "Javascript library to display typebots on your website",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { SendButton } from '@/components/SendButton'
|
||||
import { BotContext, InputSubmitContent } from '@/types'
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
import { FileInputBlock } from '@typebot.io/schemas'
|
||||
import { defaultFileInputOptions } from '@typebot.io/schemas/features/blocks/inputs/file'
|
||||
import { createSignal, Match, Show, Switch } from 'solid-js'
|
||||
import { uploadFiles } from '@typebot.io/lib/s3/uploadFiles'
|
||||
import { Button } from '@/components/Button'
|
||||
import { Spinner } from '@/components/Spinner'
|
||||
import { uploadFiles } from '../helpers/uploadFiles'
|
||||
import { guessApiHost } from '@/utils/guessApiHost'
|
||||
|
||||
type Props = {
|
||||
context: BotContext
|
||||
@ -46,20 +46,23 @@ export const FileUploadForm = (props: Props) => {
|
||||
}
|
||||
|
||||
const startSingleFileUpload = async (file: File) => {
|
||||
if (props.context.isPreview)
|
||||
if (props.context.isPreview || !props.context.resultId)
|
||||
return props.onSubmit({
|
||||
label: `File uploaded`,
|
||||
value: 'http://fake-upload-url.com',
|
||||
})
|
||||
setIsUploading(true)
|
||||
const urls = await uploadFiles({
|
||||
basePath: `${props.context.apiHost ?? guessApiHost()}/api/typebots/${
|
||||
props.context.typebot.id
|
||||
}/blocks/${props.block.id}`,
|
||||
apiHost: props.context.apiHost ?? guessApiHost(),
|
||||
files: [
|
||||
{
|
||||
file,
|
||||
path: `public/results/${props.context.resultId}/${props.block.id}/${file.name}`,
|
||||
input: {
|
||||
resultId: props.context.resultId,
|
||||
typebotId: props.context.typebot.id,
|
||||
blockId: props.block.id,
|
||||
fileName: file.name,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -69,7 +72,8 @@ export const FileUploadForm = (props: Props) => {
|
||||
setErrorMessage('An error occured while uploading the file')
|
||||
}
|
||||
const startFilesUpload = async (files: File[]) => {
|
||||
if (props.context.isPreview)
|
||||
const resultId = props.context.resultId
|
||||
if (props.context.isPreview || !resultId)
|
||||
return props.onSubmit({
|
||||
label: `${files.length} file${files.length > 1 ? 's' : ''} uploaded`,
|
||||
value: files
|
||||
@ -78,12 +82,15 @@ export const FileUploadForm = (props: Props) => {
|
||||
})
|
||||
setIsUploading(true)
|
||||
const urls = await uploadFiles({
|
||||
basePath: `${props.context.apiHost ?? guessApiHost()}/api/typebots/${
|
||||
props.context.typebot.id
|
||||
}/blocks/${props.block.id}`,
|
||||
apiHost: props.context.apiHost ?? guessApiHost(),
|
||||
files: files.map((file) => ({
|
||||
file: file,
|
||||
path: `public/results/${props.context.resultId}/${props.block.id}/${file.name}`,
|
||||
input: {
|
||||
resultId,
|
||||
typebotId: props.context.typebot.id,
|
||||
blockId: props.block.id,
|
||||
fileName: file.name,
|
||||
},
|
||||
})),
|
||||
onUploadProgress: setUploadProgressPercent,
|
||||
})
|
||||
|
@ -0,0 +1,54 @@
|
||||
import { sendRequest } from '@typebot.io/lib/utils'
|
||||
|
||||
type UploadFileProps = {
|
||||
apiHost: string
|
||||
files: {
|
||||
file: File
|
||||
input: {
|
||||
typebotId: string
|
||||
blockId: string
|
||||
resultId: string
|
||||
fileName: string
|
||||
}
|
||||
}[]
|
||||
onUploadProgress?: (percent: number) => void
|
||||
}
|
||||
|
||||
type UrlList = (string | null)[]
|
||||
|
||||
export const uploadFiles = async ({
|
||||
apiHost,
|
||||
files,
|
||||
onUploadProgress,
|
||||
}: UploadFileProps): Promise<UrlList> => {
|
||||
const urls = []
|
||||
let i = 0
|
||||
for (const { input, file } of files) {
|
||||
onUploadProgress && onUploadProgress((i / files.length) * 100)
|
||||
i += 1
|
||||
const { data } = await sendRequest<{
|
||||
presignedUrl: string
|
||||
fileUrl: string
|
||||
}>({
|
||||
method: 'POST',
|
||||
url: `${apiHost}/api/v1/generate-upload-url`,
|
||||
body: {
|
||||
filePathProps: input,
|
||||
fileType: file.type,
|
||||
},
|
||||
})
|
||||
|
||||
if (!data?.presignedUrl) continue
|
||||
else {
|
||||
const upload = await fetch(data.presignedUrl, {
|
||||
method: 'PUT',
|
||||
body: file,
|
||||
})
|
||||
|
||||
if (!upload.ok) continue
|
||||
|
||||
urls.push(data.fileUrl)
|
||||
}
|
||||
}
|
||||
return urls
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/nextjs",
|
||||
"version": "0.1.25",
|
||||
"version": "0.1.26",
|
||||
"description": "Convenient library to display typebots on your Next.js website",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@typebot.io/react",
|
||||
"version": "0.1.25",
|
||||
"version": "0.1.26",
|
||||
"description": "Convenient library to display typebots on your React app",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
1
packages/env/env.ts
vendored
1
packages/env/env.ts
vendored
@ -170,6 +170,7 @@ const s3Env = {
|
||||
S3_ENDPOINT: z.string().min(1).optional(),
|
||||
S3_SSL: boolean.optional().default('true'),
|
||||
S3_REGION: z.string().min(1).optional(),
|
||||
S3_PUBLIC_CUSTOM_DOMAIN: z.string().url().optional(),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"got": "12.6.0",
|
||||
"minio": "7.1.1"
|
||||
"minio": "7.1.3"
|
||||
}
|
||||
}
|
||||
|
42
packages/lib/s3/getFolderSize.ts
Normal file
42
packages/lib/s3/getFolderSize.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { env } from '@typebot.io/env'
|
||||
import { Client } from 'minio'
|
||||
|
||||
type Props = {
|
||||
folderPath: string
|
||||
}
|
||||
|
||||
export const getFolderSize = async ({ folderPath }: Props) => {
|
||||
if (!env.S3_ENDPOINT || !env.S3_ACCESS_KEY || !env.S3_SECRET_KEY)
|
||||
throw new Error(
|
||||
'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY'
|
||||
)
|
||||
|
||||
const minioClient = new Client({
|
||||
endPoint: env.S3_ENDPOINT,
|
||||
port: env.S3_PORT,
|
||||
useSSL: env.S3_SSL,
|
||||
accessKey: env.S3_ACCESS_KEY,
|
||||
secretKey: env.S3_SECRET_KEY,
|
||||
region: env.S3_REGION,
|
||||
})
|
||||
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
let totalSize = 0
|
||||
|
||||
const stream = minioClient.listObjectsV2(
|
||||
env.S3_BUCKET,
|
||||
'public/' + folderPath,
|
||||
true
|
||||
)
|
||||
|
||||
stream.on('data', function (obj) {
|
||||
totalSize += obj.size
|
||||
})
|
||||
stream.on('error', function (err) {
|
||||
reject(err)
|
||||
})
|
||||
stream.on('end', function () {
|
||||
resolve(totalSize)
|
||||
})
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user