diff --git a/package.json b/package.json index 738849c1..e7d858ba 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@aws-sdk/s3-presigned-post": "^3.216.0", "@aws-sdk/signature-v4-multi-region": "^3.215.0", "@aws-sdk/util-user-agent-node": "^3.215.0", + "formidable": "^2.1.1", "mailgun.js": "^8.0.2" }, "devDependencies": { diff --git a/src/components/Pages/profile/ProfileSettingsPage.tsx b/src/components/Pages/profile/ProfileSettingsPage.tsx index a4e9d8f3..b10b6334 100644 --- a/src/components/Pages/profile/ProfileSettingsPage.tsx +++ b/src/components/Pages/profile/ProfileSettingsPage.tsx @@ -26,9 +26,23 @@ export const ProfileSettingsPage = (props: PageProps) => { selectFilesAsync(async ([{ source, name, size, file }]) => { try { console.log({ source, name, size, file }) - // DO UPLOAD STUFF HERE AND RETURN URL + const res = await fetch(`/api/upload?file=${name}&fileType=${file.type}`) + + const { url, fields } = await res.json() + const formData = new FormData() + + Object.entries({ ...fields, file }).forEach(([key, value]) => { + formData.append(key, value as string) + }) + + await fetch(url, { + method: 'POST', + body: formData + }).then((response) => { + console.log('!!! response:', response) + }) } catch (error) { - console.log(error) + console.log('[upload Error]', error) } }) } diff --git a/src/pages/api/upload.ts b/src/pages/api/upload.ts index 3113894b..8161a9f4 100644 --- a/src/pages/api/upload.ts +++ b/src/pages/api/upload.ts @@ -2,8 +2,49 @@ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3' import { createPresignedPost } from '@aws-sdk/s3-presigned-post' +import formidable from 'formidable' +import fs from 'fs' -export default async function handler(req, res) { +const putObject = async (s3Client, key, body, contentType) => { + // workaround for the issue: https://github.com/aws/aws-sdk-js-v3/issues/1800 + s3Client.middlewareStack.add( + (next, _context) => (args) => { + delete args.request.headers['content-type'] + return next(args) + }, + { + step: 'build' + } + ) + + const objectParams = { + ACL: 'public-read', + Bucket: process.env.S3_BUCKET_NAME, + Key: key, + Body: body, + ContentType: contentType + } + + const results = await s3Client.send(new PutObjectCommand(objectParams)) + return results +} + +const parseFormData = (req) => { + return new Promise((resolve, reject) => { + const form = new formidable.IncomingForm({ + multiples: true, + keepExtensions: true + }) + form.parse(req, (err, fields, files) => { + if (err) { + reject(err) + } + resolve({ fields, files }) + }) + }) +} + +export async function handler(req, res) { const s3Client = new S3Client({ region: process.env.S3_REGION || 'eu-west-1', credentials: { @@ -12,18 +53,28 @@ export default async function handler(req, res) { } }) - const post = await createPresignedPost(s3Client, { - Bucket: process.env.S3_BUCKET_NAME || 'discours-io', - Key: req.query.file, - Fields: { - acl: 'public-read', - 'Content-Type': req.query.fileType - }, - Expires: 600, // seconds - Conditions: [ - ['content-length-range', 0, 22 * 1048576] // up to 22 MB - ] - }) + if (req.method === 'POST') { + const data: any = await parseFormData(req) + const files = data?.files + const image = files.image + const body = fs.readFileSync(image.path) - res.status(200).json(post) + // const resp = await putObject(s3Client, image.name, body, image.type) + + const resp = await createPresignedPost(s3Client, { + Bucket: process.env.S3_BUCKET_NAME || 'discours-io', + Key: image.path, + Fields: { + acl: 'public-read', + 'Content-Type': image.type + }, + Expires: 600, // seconds + Conditions: [ + ['content-length-range', 0, 22 * 1048576] // up to 22 MB + ] + }) + return res.status(200).json(resp) + } + + return res.status(405).end() } diff --git a/yarn.lock b/yarn.lock index 5acfca4c..601836a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3554,7 +3554,7 @@ arrify@^1.0.1: resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== -asap@~2.0.3: +asap@^2.0.0, asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -4633,6 +4633,14 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +dezalgo@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + diff-sequences@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" @@ -5715,6 +5723,16 @@ formdata-polyfill@^4.0.10: dependencies: fetch-blob "^3.1.2" +formidable@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.1.tgz#81269cbea1a613240049f5f61a9d97731517414f" + integrity sha512-0EcS9wCFEzLvfiks7omJ+SiYJAiD+TzK4Pcw1UlUoGnhUxDcMKjt0P7x8wEb0u6OHu8Nb98WG3nxtlF5C7bvUQ== + dependencies: + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + qs "^6.11.0" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -6218,6 +6236,11 @@ header-case@^2.0.4: capital-case "^1.0.4" tslib "^2.0.3" +hexoid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" + integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -8491,7 +8514,7 @@ object.values@^1.1.5: define-properties "^1.1.4" es-abstract "^1.20.4" -once@^1.3.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== @@ -9128,6 +9151,13 @@ pvutils@^1.1.3: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== +qs@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"