upload endpoint moved from vercel to backend (#119)

This commit is contained in:
Igor Lobanov 2023-07-13 15:19:52 +02:00 committed by GitHub
parent 9504fada02
commit cf0c4455f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 13 additions and 107 deletions

View File

@ -1,14 +0,0 @@
import formidable from 'formidable'
export const formidablePromise = async (request, options) => {
return new Promise((resolve, reject) => {
const form = formidable(options)
form.parse(request, (err, fields, files) => {
if (err) {
return reject(err)
}
return resolve({ fields, files })
})
})
}

View File

@ -1,76 +0,0 @@
import { S3Client } from '@aws-sdk/client-s3'
import { Upload } from '@aws-sdk/lib-storage'
import formidable from 'formidable'
import { Writable } from 'stream'
import path from 'path'
import crypto from 'crypto'
import { formidablePromise } from './_shared/formidablePromise.js'
const { STORJ_ACCESS_KEY, STORJ_SECRET_KEY, STORJ_END_POINT, STORJ_BUCKET_NAME, CDN_DOMAIN } = process.env
const storjS3Client = new S3Client({
endpoint: STORJ_END_POINT,
credentials: {
accessKeyId: STORJ_ACCESS_KEY,
secretAccessKey: STORJ_SECRET_KEY
}
})
const fileConsumer = (acc) => {
return new Writable({
write: (chunk, _enc, next) => {
acc.push(chunk)
next()
}
})
}
const formidableConfig = {
keepExtensions: true,
maxFileSize: 10_000_000,
maxFieldsSize: 10_000_000,
maxFields: 7,
allowEmptyFiles: false,
multiples: false
}
const handleFileUpload = async (request) => {
const chunks = []
const { files } = await formidablePromise(request, {
...formidableConfig,
// consume this, otherwise formidable tries to save the file to disk
fileWriteStreamHandler: () => fileConsumer(chunks)
})
const data = Buffer.concat(chunks)
const { originalFilename, mimetype } = files.file
const fileExtension = path.extname(originalFilename)
const filename = crypto.randomUUID() + fileExtension
const params = {
Bucket: STORJ_BUCKET_NAME,
Key: filename,
Body: data,
ContentType: mimetype
}
const upload = new Upload({ params, client: storjS3Client })
await upload.done()
return `http://${CDN_DOMAIN}/${filename}`
}
const handler = async (request, response) => {
try {
const location = await handleFileUpload(request)
return response.status(200).json(location)
} catch (error) {
console.error(error)
response.status(500).json({ error: error.message })
}
}
export default handler

View File

@ -29,18 +29,11 @@
"typecheck:watch": "tsc --noEmit --watch" "typecheck:watch": "tsc --noEmit --watch"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/abort-controller": "3.303.0",
"@aws-sdk/client-s3": "3.303.0",
"@aws-sdk/lib-storage": "3.303.0",
"@hocuspocus/provider": "2.0.6", "@hocuspocus/provider": "2.0.6",
"@solid-primitives/media": "2.2.3",
"form-data": "4.0.0", "form-data": "4.0.0",
"formidable": "2.1.1",
"i18next": "22.4.15", "i18next": "22.4.15",
"mailgun.js": "8.2.1", "mailgun.js": "8.2.1",
"node-fetch": "3.3.1", "node-fetch": "3.3.1"
"solid-popper": "0.3.0",
"typograf": "7.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.21.8", "@babel/core": "7.21.8",
@ -54,6 +47,7 @@
"@nanostores/router": "0.8.3", "@nanostores/router": "0.8.3",
"@nanostores/solid": "0.3.2", "@nanostores/solid": "0.3.2",
"@popperjs/core": "2.11.7", "@popperjs/core": "2.11.7",
"@solid-primitives/media": "2.2.3",
"@solid-primitives/memo": "1.2.4", "@solid-primitives/memo": "1.2.4",
"@solid-primitives/share": "2.0.4", "@solid-primitives/share": "2.0.4",
"@solid-primitives/storage": "1.3.9", "@solid-primitives/storage": "1.3.9",
@ -155,6 +149,7 @@
"rollup-plugin-visualizer": "5.9.0", "rollup-plugin-visualizer": "5.9.0",
"sass": "1.62.1", "sass": "1.62.1",
"solid-js": "1.7.5", "solid-js": "1.7.5",
"solid-popper": "0.3.0",
"solid-tiptap": "0.6.0", "solid-tiptap": "0.6.0",
"solid-transition-group": "0.2.2", "solid-transition-group": "0.2.2",
"sort-package-json": "2.4.1", "sort-package-json": "2.4.1",
@ -165,6 +160,7 @@
"swiper": "9.4.1", "swiper": "9.4.1",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"typescript": "5.0.4", "typescript": "5.0.4",
"typograf": "7.1.0",
"undici": "5.21.0", "undici": "5.21.0",
"uniqolor": "1.1.0", "uniqolor": "1.1.0",
"unique-names-generator": "4.7.1", "unique-names-generator": "4.7.1",

View File

@ -26,8 +26,8 @@ export const UploadModalContent = (props: Props) => {
const runUpload = async (file) => { const runUpload = async (file) => {
try { try {
setIsUploading(true) setIsUploading(true)
const fileUrl = await handleFileUpload(file) const result = await handleFileUpload(file)
props.onClose(fileUrl) props.onClose(result.url)
setIsUploading(false) setIsUploading(false)
} catch (error) { } catch (error) {
setIsUploading(false) setIsUploading(false)

View File

@ -29,7 +29,7 @@ export const DropArea = (props: Props) => {
const results: string[] = [] const results: string[] = []
for (const file of files) { for (const file of files) {
const result = await handleFileUpload(file) const result = await handleFileUpload(file)
results.push(result) results.push(result.url)
} }
props.onUpload(results) props.onUpload(results)
setLoading(false) setLoading(false)

View File

@ -106,7 +106,7 @@ export const SolidSwiper = (props: Props) => {
const results: string[] = [] const results: string[] = []
for (const file of selectedFiles) { for (const file of selectedFiles) {
const result = await handleFileUpload(file) const result = await handleFileUpload(file)
results.push(result) results.push(result.url)
} }
props.onImagesAdd(composeMediaItem(results)) props.onImagesAdd(composeMediaItem(results))
setLoading(false) setLoading(false)

View File

@ -61,8 +61,8 @@ export const ProfileSettingsPage = () => {
await selectFiles(async ([uploadFile]) => { await selectFiles(async ([uploadFile]) => {
try { try {
setIsUserpicUpdating(true) setIsUserpicUpdating(true)
const fileUrl = await handleFileUpload(uploadFile) const result = await handleFileUpload(uploadFile)
updateFormField('userpic', fileUrl) updateFormField('userpic', result.url)
setIsUserpicUpdating(false) setIsUserpicUpdating(false)
} catch (error) { } catch (error) {
console.error('[upload avatar] error', error) console.error('[upload avatar] error', error)

View File

@ -1,12 +1,12 @@
import { UploadFile } from '@solid-primitives/upload' import { UploadFile } from '@solid-primitives/upload'
import { isDev } from './config' import { apiBaseUrl } from './config'
const api = isDev ? 'https://new.discours.io/api/upload' : '/api/upload' const apiUrl = `${apiBaseUrl}/upload`
export const handleFileUpload = async (uploadFile: UploadFile) => { export const handleFileUpload = async (uploadFile: UploadFile) => {
const formData = new FormData() const formData = new FormData()
formData.append('file', uploadFile.file, uploadFile.name) formData.append('file', uploadFile.file, uploadFile.name)
const response = await fetch(api, { const response = await fetch(apiUrl, {
method: 'POST', method: 'POST',
body: formData body: formData
}) })