2
0

Use minio for presigned urls and remove aws sdk

This commit is contained in:
Baptiste Arnaud
2023-08-30 09:08:05 +02:00
parent 5d402d9a38
commit 9a79bc38ee
16 changed files with 236 additions and 327 deletions

View File

@ -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'
import { uploadFiles } from '@typebot.io/lib/uploadFiles'
type Props = {
block: FileInputBlock

View File

@ -4,7 +4,7 @@ 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'
import { uploadFiles } from '@typebot.io/lib/uploadFiles'
import { Button } from '@/components/Button'
import { Spinner } from '@/components/Spinner'

View File

@ -14,7 +14,7 @@ export const deleteFilesFromBucket = async ({
const minioClient = new Client({
endPoint: env.S3_ENDPOINT,
port: env.S3_PORT,
useSSL: env.S3_SSL ?? true,
useSSL: env.S3_SSL,
accessKey: env.S3_ACCESS_KEY,
secretKey: env.S3_SECRET_KEY,
region: env.S3_REGION,

View File

@ -1,46 +1,32 @@
import { env } from '@typebot.io/env'
import { config, Endpoint, S3 } from 'aws-sdk'
import { Client } from 'minio'
type GeneratePresignedUrlProps = {
filePath: string
fileType?: string
sizeLimit?: number
}
const tenMB = 10 * 1024 * 1024
const tenMinutes = 10 * 60
export const generatePresignedUrl = ({
export const generatePresignedUrl = async ({
filePath,
fileType,
sizeLimit = tenMB,
}: GeneratePresignedUrlProps): S3.PresignedPost => {
}: GeneratePresignedUrlProps): Promise<string> => {
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'
)
config.update({
accessKeyId: env.S3_ACCESS_KEY,
secretAccessKey: env.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,
sslEnabled: env.S3_SSL ?? true,
})
const protocol = env.S3_SSL ?? true ? 'https' : 'http'
const s3 = new S3({
endpoint: new Endpoint(
`${protocol}://${env.S3_ENDPOINT}${env.S3_PORT ? `:${env.S3_PORT}` : ''}`
),
})
const presignedUrl = s3.createPresignedPost({
Bucket: env.S3_BUCKET ?? 'typebot',
Fields: {
key: filePath,
'Content-Type': fileType,
},
Expires: tenMinutes,
Conditions: [['content-length-range', 0, sizeLimit]],
return minioClient.presignedUrl('PUT', env.S3_BUCKET, filePath, tenMinutes, {
'Content-Type': fileType,
})
return presignedUrl
}

View File

@ -12,14 +12,12 @@
"@typebot.io/schemas": "workspace:*",
"@typebot.io/tsconfig": "workspace:*",
"@types/nodemailer": "6.4.8",
"aws-sdk": "2.1415.0",
"next": "13.4.3",
"nodemailer": "6.9.3",
"typescript": "5.1.6",
"@typebot.io/env": "workspace:*"
},
"peerDependencies": {
"aws-sdk": "2.1152.0",
"next": "13.0.0",
"nodemailer": "6.7.8"
},

View File

@ -0,0 +1,49 @@
import { sendRequest } from './utils'
type UploadFileProps = {
basePath?: string
files: {
file: File
path: string
}[]
onUploadProgress?: (percent: number) => void
}
type UrlList = (string | null)[]
export const uploadFiles = async ({
basePath = '/api',
files,
onUploadProgress,
}: UploadFileProps): Promise<UrlList> => {
const urls = []
let i = 0
for (const { file, path } of files) {
onUploadProgress && onUploadProgress((i / files.length) * 100)
i += 1
const { data } = await sendRequest<{
presignedUrl: string
hasReachedStorageLimit: boolean
}>(
`${basePath}/storage/upload-url?filePath=${encodeURIComponent(
path
)}&fileType=${file.type}`
)
if (!data?.presignedUrl) continue
const url = data.presignedUrl
if (data.hasReachedStorageLimit) urls.push(null)
else {
const upload = await fetch(url, {
method: 'PUT',
body: file,
})
if (!upload.ok) continue
urls.push(url.split('?')[0])
}
}
return urls
}

View File

@ -196,57 +196,6 @@ export const generateId = (idDesiredLength: number): string => {
.join('')
}
type UploadFileProps = {
basePath?: string
files: {
file: File
path: string
}[]
onUploadProgress?: (percent: number) => void
}
type UrlList = (string | null)[]
export const uploadFiles = async ({
basePath = '/api',
files,
onUploadProgress,
}: UploadFileProps): Promise<UrlList> => {
const urls = []
let i = 0
for (const { file, path } of files) {
onUploadProgress && onUploadProgress((i / files.length) * 100)
i += 1
const { data } = await sendRequest<{
presignedUrl: { url: string; fields: any }
hasReachedStorageLimit: boolean
}>(
`${basePath}/storage/upload-url?filePath=${encodeURIComponent(
path
)}&fileType=${file.type}`
)
if (!data?.presignedUrl) continue
const { url, fields } = data.presignedUrl
if (data.hasReachedStorageLimit) urls.push(null)
else {
const formData = new FormData()
Object.entries({ ...fields, file }).forEach(([key, value]) => {
formData.append(key, value as string | Blob)
})
const upload = await fetch(url, {
method: 'POST',
body: formData,
})
if (!upload.ok) continue
urls.push(`${url.split('?')[0]}/${path}`)
}
}
return urls
}
export const hasValue = (
value: string | undefined | null
): value is NonNullable<string> =>