ryanto / next-s3-upload

Upload files from your Next.js app to S3
https://next-s3-upload.codingvalue.com/
543 stars 76 forks source link

Has anyone been able to use it with `serverless-s3-local`? #120

Closed williamrjribeiro closed 1 year ago

williamrjribeiro commented 1 year ago

Hello there.

I'm trying to use next-s3-upload with my local serverless-s3-local but I can't get the configs right.

My app must use usePresignedUpload hook for using Pre-Signed URLs.

serverless-s3-local starts the bucket at http://localhost:4569/my-bucket but the generated pre-signed URL is: http://my-bucket.localhost:4569.

When I try to upload some files using the provided code example, I get CORS errors when an OPTIONS request is made to this URL:

http://my-bucket.localhost:4569/sample.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA43MKBFWYOEDFLCFA%2F20221220%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20221220T133051Z&X-Amz-Expires=3600&X-Amz-Signature=b65d92f1740fab94f2b0f206a7be48ef2c4b03a092b18f29cc59a2b8d861db0a&X-Amz-SignedHeaders=host&x-id=PutObject

But the client makes a GET request to the correct path http://localhost:4569/my-bucket/sample.png but since the upload failed, it gets a 404.

I'm puzzled. 🤔

Here's my /api/s3-upload.js

import { APIRoute } from "next-s3-upload";

export default APIRoute.configure({
  bucket: "my-bucket",
  endpoint: "http://localhost:4569"
});

And here's my serverless config:

service: s3-handler

provider:
  name: aws
  runtime: nodejs16.x
  stage: dev
  region: us-east-1

plugins:
  - serverless-s3-local
  - serverless-offline

custom:  
  s3:
    host: localhost
    directory: /tmp
    cors: ./cors.xml
    allowMismatchedSignatures: true

resources:
  Resources:
    NewResource:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: my-bucket

functions:
  evidenceBucketLambda:
    handler: s3-handler.upload
    events:
      - s3:
          bucket: my-bucket
          event: s3:*

Here's my cors.xml:

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    <MaxAgeSeconds>30000</MaxAgeSeconds>
    <ExposeHeader>x-amz-server-side-encryption</ExposeHeader>
    <ExposeHeader>ETag</ExposeHeader>
  </CORSRule>
</CORSConfiguration>

Any tips appreciated. Thanks! 🙏

ryanto commented 1 year ago

Hi there!

Thanks for the issue, I haven't used serverless-s3-local, but if you could help make an example/demo project that I can run that would be great!

From reading their docs, my guess is we need to support forcePathStyle: true, in the S3 config. That might help fix the strange localhost/subdomain url you're seeing!

williamrjribeiro commented 1 year ago

@ryanto here you go: https://stackblitz.com/edit/nextjs-6cb1o7?file=pages/index.js

My suspicious is on forcePathStyle as well...

ryanto commented 1 year ago

Awesome, thanks for throwing together that stackblitz. It made this super easy to debug!

It looks like the problem was forcePathStyle, I released a new version of next-s3-upload (0.2.9) that allows you to set this option.

I was able to get it working with your serverless config using the following API route:

// pages/api/s3-upload.js

import { APIRoute } from 'next-s3-upload';

export default APIRoute.configure({
  accessKeyId: "S3RVER",
  secretAccessKey: "S3RVER",
  bucket: 'my-bucket',
  forcePathStyle: true,
  endpoint: 'http://localhost:4569',
});

If you upgrade to version 0.2.9 let me know how it goes!

Again thanks so much for that stackblitz. I wouldn't have been able to fix this without that 😄

williamrjribeiro commented 1 year ago

Thank you very much. Cheers! 🍻