Bladeren bron

Add example for Uppy with S3 and a Node.js server (#4129)

Co-authored-by: Merlijn Vos <merlijn@soverin.net>
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
Raúl Ibáñez 2 jaren geleden
bovenliggende
commit
3f07d79de6

+ 1 - 1
.eslintrc.js

@@ -192,7 +192,7 @@ module.exports = {
         '*.mjs',
         'e2e/clients/**/*.js',
         'examples/aws-companion/*.js',
-        'examples/aws-presigned-url/*.js',
+        'examples/aws-php/*.js',
         'examples/bundled/*.js',
         'examples/custom-provider/client/*.js',
         'examples/digitalocean-spaces/*.js',

+ 1 - 1
.gitignore

@@ -19,7 +19,7 @@ dist/
 lib/
 coverage/
 examples/dev/bundle.js
-examples/aws-presigned-url/vendor/*
+examples/aws-php/vendor/*
 test/endtoend/create-react-app/build/
 test/endtoend/create-react-app/coverage/
 uppy-*.tgz

+ 2 - 2
CHANGELOG.md

@@ -312,7 +312,7 @@ Released: 2022-08-02
 - @uppy/transloadit: send `assembly-cancelled` only once (Antoine du Hamel / #3937)
 - meta: `keepNames` in bundle (Antoine du Hamel / #3926)
 - meta: e2e: fix Transloadit test suite with Cypress 10 (Antoine du Hamel / #3936)
-- meta: Bump guzzlehttp/guzzle from 7.4.1 to 7.4.5 in /examples/aws-presigned-url (dependabot[bot] / #3842)
+- meta: Bump guzzlehttp/guzzle from 7.4.1 to 7.4.5 in /examples/aws-php (dependabot[bot] / #3842)
 - @uppy/tus: fix dependencies (Antoine du Hamel / #3923)
 - meta: doc: fix linter failure in `image-editor.md` (Antoine du Hamel / #3924)
 - meta: doc: Fix typo in image-editor.md (Ikko Ashimine / #3921)
@@ -841,7 +841,7 @@ Released: 2022-01-10
 - @uppy/tus: pause all requests in response to server rate limiting (Antoine du Hamel / #3394)
 - @uppy/transloadit: better defaults for rate limiting (Antoine du Hamel / #3414)
 - @uppy/companion: Fix Companion deploys (kiloreux / #3388)
-- meta: update aws-presigned-url example to use esm (Antoine du Hamel / #3413)
+- meta: update aws-php example to use esm (Antoine du Hamel / #3413)
 - @uppy/image-editor: namespace input range css (Merlijn Vos / #3406)
 - @uppy/screen-capture: Add missing option to the screen capture types (Mustafa Navruz / #3400)
 - @uppy/drag-drop: fix `undefined is not a function` TypeError (Antoine du Hamel / #3397)

+ 102 - 0
examples/aws-nodejs/README.md

@@ -0,0 +1,102 @@
+# Uppy + AWS S3 with Node.JS
+
+A simple and fully working example of Uppy and AWS S3 storage with Node.js (and Express.js) It uses presigned URL at the backend level.
+
+This README file starts with AWS as this one need to be resolved first.
+
+# AWS Configuration
+
+It's assumed that you are familiar with AWS, at least, with the storage service (S3) and users & policies (IAM).
+
+These instructions are not fit for production but tightening the security is out of the scope here.
+
+## S3 Setup
+
+- Create new S3 bucket in AWS (e.g. `aws-nodejs`).
+- Add a bucket policy.
+```{
+    "Version": "2012-10-17",
+    "Statement": [
+        {
+            "Sid": "PublicAccess",
+            "Effect": "Allow",
+            "Principal": "*",
+            "Action": "s3:GetObject",
+            "Resource": "arn:aws:s3:::aws-nodejs/*"
+        }
+    ]
+}
+```
+
+- Make the S3 bucket public.
+- Add CORS configuration.
+```[
+    {
+        "AllowedHeaders": [
+            "*"
+        ],
+        "AllowedMethods": [
+            "GET",
+            "PUT",
+            "HEAD",
+            "POST",
+            "DELETE"
+        ],
+        "AllowedOrigins": [
+            "*"
+        ],
+        "ExposeHeaders": []
+    }
+]
+```
+
+## AWS Credentials
+
+You may use existing AWS credentials or create a new user in the IAM page.
+
+- Make sure you setup the AWS credentials properly and write down the Access Key ID and Secret Access Key.
+- You may configure AWS S3 credentials using [environment variables](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-environment.html) or a [credentials file in `~/.aws/credentials`](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html).
+- You will need at least `PutObject` and `PutObjectAcl` permissions.
+```{
+    "Version": "2012-10-17",
+    "Statement": [
+        {
+            "Sid": "VisualEditor0",
+            "Effect": "Allow",
+            "Action": [
+                "s3:PutObject",
+                "s3:PutObjectAcl"
+            ],
+            "Resource": "arn:aws:s3:::aws-nodejs/*"
+        }
+    ]
+}
+```
+# Install
+
+Download this code or clone repository into a folder and install dependencies:
+
+```bash
+corepack yarn install
+```
+
+Add a `.env` file to the root directory and define the S3 bucket name and port variables like the example below:
+
+```
+S3_BUCKET=aws-nodejs
+PORT=8080
+```
+
+# Enjoy it
+
+Start the application:
+
+```bash
+corepack yarn workspace @uppy-example/aws-nodejs start
+```
+
+Dashboard demo should now be available at http://localhost:8080.
+
+You have also a Drag & Drop demo on http://localhost:8080/drag.
+
+*Feel free to check how the demo works and feel free to open an issue.*

+ 53 - 0
examples/aws-nodejs/index.js

@@ -0,0 +1,53 @@
+require('dotenv').config({ path: path.join(__dirname, '..', '..', '.env') })
+
+const express = require('express')
+
+const app = express()
+const path = require('node:path')
+
+const port = process.env.PORT
+const bodyParser = require('body-parser')
+
+const aws = require('aws-sdk')
+
+app.use(bodyParser.json())
+
+app.get('/', (req, res) => {
+  const htmlPath = path.join(__dirname, 'public', 'index.html')
+  res.sendFile(htmlPath)
+})
+
+app.get('/drag', (req, res) => {
+  const htmlPath = path.join(__dirname, 'public', 'drag.html')
+  res.sendFile(htmlPath)
+})
+
+app.post('/sign-s3', (req, res) => {
+  const s3 = new aws.S3()
+  const fileName = req.body.filename
+  const { contentType } = req.body
+  const s3Params = {
+    Bucket: process.env.S3_BUCKET,
+    Key: fileName,
+    Expires: 60,
+    ContentType: contentType,
+    ACL: 'public-read',
+  }
+
+  s3.getSignedUrl('putObject', s3Params, (err, data) => {
+    if (err) {
+      console.log(err)
+      return res.end()
+    }
+    const returnData = {
+      url: data,
+      method: 'PUT',
+    }
+    res.write(JSON.stringify(returnData))
+    res.end()
+  })
+})
+
+app.listen(port, () => {
+  console.log(`Example app listening on port ${port}`)
+})

+ 17 - 0
examples/aws-nodejs/package.json

@@ -0,0 +1,17 @@
+{
+  "name": "@uppy-example/aws-nodejs",
+  "version": "1.0.0",
+  "description": "Uppy for AWS S3 with a custom Node.js backend for signing URLs",
+  "main": "index.js",
+  "scripts": {
+    "start": "node index.js"
+  },
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "aws-sdk": "^2.1038.0",
+    "body-parser": "^1.20.0",
+    "dotenv": "^16.0.0",
+    "express": "^4.18.1"
+  }
+}

+ 85 - 0
examples/aws-nodejs/public/drag.html

@@ -0,0 +1,85 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Uppy</title>
+    <link href="https://releases.transloadit.com/uppy/v3.0.1/uppy.min.css" rel="stylesheet">
+  </head>
+  <body>
+    <section class="example">
+    <div id="drag-drop-area"></div>
+    <div class="for-ProgressBar"></div>
+    <div class="uploaded-files">
+      <h5>Uploaded files:</h5>
+      <ol></ol>
+    </div>
+      <script type="module">
+        import {Uppy, DragDrop, ProgressBar, AwsS3 } from "https://releases.transloadit.com/uppy/v3.0.2/uppy.min.mjs"
+
+        // Function for displaying uploaded files
+        const onUploadSuccess = (elForUploadedFiles) => (file, response) => {
+          const url = response.uploadURL
+          const fileName = file.name
+
+          const li = document.createElement('li')
+          const a = document.createElement('a')
+          a.href = url
+          a.target = '_blank'
+          a.appendChild(document.createTextNode(fileName))
+          li.appendChild(a)
+
+          document.querySelector(elForUploadedFiles).appendChild(li)
+        }
+
+        var uppy = new Uppy({
+            autoProceed: true,
+            restrictions: {
+              maxNumberOfFiles: 10,
+            }
+          })
+          .use(DragDrop, {
+              inline: true,
+              target: '#drag-drop-area'
+            })
+          .use(ProgressBar, { target: '.example .for-ProgressBar', hideAfterFinish: true })
+          .use(AwsS3, {
+            getUploadParameters (file) {
+              // Send a request to our PHP signing endpoint.
+              return fetch('/sign-s3', {
+                method: 'post',
+                // Send and receive JSON.
+                headers: {
+                  accept: 'application/json',
+                  'content-type': 'application/json',
+                },
+                body: JSON.stringify({
+                  filename: file.name,
+                  contentType: file.type,
+                }),
+              }).then((response) => {
+                // Parse the JSON response.
+                return response.json()
+              }).then((data) => {
+                // Return an object in the correct shape.
+                return {
+                  method: data.method,
+                  url: data.url,
+                  fields: data.fields,
+                  // Provide content type header required by S3
+                  headers: {
+                    'Content-Type': file.type,
+                  },
+                }
+              })
+            },
+        });
+
+        uppy.on('complete', (result) => {
+          console.log('Upload complete! We’ve uploaded these files:', result.successful)
+        });
+
+        uppy.on('upload-success', onUploadSuccess('.example .uploaded-files ol'));
+      </script>
+    </section>
+  </body>
+</html>

+ 58 - 0
examples/aws-nodejs/public/index.html

@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Uppy</title>
+    <link href="https://releases.transloadit.com/uppy/v3.0.1/uppy.min.css" rel="stylesheet">
+  </head>
+  <body>
+    <div id="drag-drop-area"></div>
+    <script type="module">
+      import { Uppy, Dashboard, AwsS3 } from "https://releases.transloadit.com/uppy/v3.0.2/uppy.min.mjs"
+      var uppy = new Uppy()
+        .use(Dashboard, {
+            inline: true,
+            target: '#drag-drop-area',
+          })
+        .use(AwsS3, {
+          getUploadParameters (file) {
+            // Send a request to our PHP signing endpoint.
+            return fetch('/sign-s3', {
+              method: 'post',
+              // Send and receive JSON.
+              headers: {
+                accept: 'application/json',
+                'content-type': 'application/json',
+              },
+              body: JSON.stringify({
+                filename: file.name,
+                contentType: file.type,
+              }),
+            }).then((response) => {
+              // Parse the JSON response.
+              return response.json()
+            }).then((data) => {
+              // Return an object in the correct shape.
+              return {
+                method: data.method,
+                url: data.url,
+                fields: data.fields, // For presigned PUT uploads, this should be left empty.
+                // Provide content type header required by S3
+                headers: {
+                  'Content-Type': file.type,
+                },
+              }
+            })
+          },
+      });
+
+      uppy.on('complete', (result) => {
+        console.log('Upload complete! We’ve uploaded these files:', result.successful)
+      })
+
+      uppy.on('upload-success', (file, data) => {
+        console.log('Upload success! We’ve uploaded this file:', file.meta['name'])
+      })
+    </script>
+  </body>
+</html>

+ 0 - 0
examples/aws-presigned-url/.gitignore → examples/aws-php/.gitignore


+ 0 - 0
examples/aws-presigned-url/composer.json → examples/aws-php/composer.json


+ 0 - 0
examples/aws-presigned-url/composer.lock → examples/aws-php/composer.lock


+ 0 - 0
examples/aws-presigned-url/index.html → examples/aws-php/index.html


+ 0 - 0
examples/aws-presigned-url/main.js → examples/aws-php/main.js


+ 1 - 1
examples/aws-presigned-url/package.json → examples/aws-php/package.json

@@ -1,5 +1,5 @@
 {
-  "name": "@uppy-example/aws-presigned-url",
+  "name": "@uppy-example/aws-php",
   "version": "0.0.0",
   "dependencies": {
     "@uppy/aws-s3": "workspace:*",

+ 3 - 3
examples/aws-presigned-url/readme.md → examples/aws-php/readme.md

@@ -17,7 +17,7 @@ This example also uses the AWS PHP SDK.
 To install it, [get composer](https://getcomposer.org) and run `composer update` in this folder.
 
 ```bash
-corepack yarn workspace @uppy-example/aws-presigned-url exec "composer update"
+corepack yarn workspace @uppy-example/aws-php exec "composer update"
 ```
 
 Configure AWS S3 credentials using [environment variables](https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html#environment-credentials) or a [credentials file in `~/.aws/credentials`](https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html#credential-profiles).
@@ -26,7 +26,7 @@ Configure a bucket name and region in the `s3-sign.php` file.
 Then, again in the **repository root**, start this example by doing:
 
 ```bash
-corepack yarn workspace @uppy-example/aws-presigned-url start
+corepack yarn workspace @uppy-example/aws-php start
 ```
 
 The demo should now be available at http://localhost:8080.
@@ -37,5 +37,5 @@ You can use a different S3-compatible service like GCS by configuring that servi
 AWS_PROFILE="gcs" \
 COMPANION_AWS_ENDPOINT="https://storage.googleapis.com" \
 COMPANION_AWS_BUCKET="test-bucket-name" \
-  corepack yarn run example aws-presigned-url
+  corepack yarn run example aws-php
 ```

+ 0 - 0
examples/aws-presigned-url/s3-sign.php → examples/aws-php/s3-sign.php


+ 0 - 0
examples/aws-presigned-url/serve.php → examples/aws-php/serve.php


+ 1 - 1
website/src/docs/aws-s3.md

@@ -347,7 +347,7 @@ uppy.use(AwsS3, {
 })
 ```
 
-See the [aws-presigned-url example in the uppy repository](https://github.com/transloadit/uppy/tree/main/examples/aws-presigned-url) for a small example that implements both the server-side and the client-side.
+See the [aws-php example in the uppy repository](https://github.com/transloadit/uppy/tree/main/examples/aws-php) for a small example that implements both the server-side and the client-side.
 
 ### Retrieving presign parameters of the uploaded file
 

+ 42 - 6
yarn.lock

@@ -6993,8 +6993,8 @@ __metadata:
 
 "@types/jasmine@file:./private/@types/jasmine::locator=%40uppy-dev%2Fbuild%40workspace%3A.":
   version: 0.0.0
-  resolution: "@types/jasmine@file:./private/@types/jasmine#./private/@types/jasmine::hash=917119&locator=%40uppy-dev%2Fbuild%40workspace%3A."
-  checksum: cc86c9a338a6943da64fb636e13bdc8f2e41922ae62c3cfd95525b0a0965a28ecd7545173773f2b61a3c30ae70c8eeb9a725eb71fd9006a07a8733ecbac849b9
+  resolution: "@types/jasmine@file:./private/@types/jasmine#./private/@types/jasmine::hash=cf2699&locator=%40uppy-dev%2Fbuild%40workspace%3A."
+  checksum: f8900f2d42d5c061a69cebc73bc83950095b354964834ab459dbd144a006f4afa0ffc436cfd5c0e74522f5752a7e30d60e1c6c2f69a184f59a749df405a54cdc
   languageName: node
   linkType: hard
 
@@ -7007,8 +7007,8 @@ __metadata:
 
 "@types/jasminewd2@file:./private/@types/jasmine::locator=%40uppy-dev%2Fbuild%40workspace%3A.":
   version: 0.0.0
-  resolution: "@types/jasminewd2@file:./private/@types/jasmine#./private/@types/jasmine::hash=fe65e0&locator=%40uppy-dev%2Fbuild%40workspace%3A."
-  checksum: 028a3a54e144661b64d14b439c585db2ade1a22ba4b21de85cb469afd2f1bf5e78079ab3f3e5c058ed4146e7cef5300e27c8d176d9f168222d88576671608135
+  resolution: "@types/jasminewd2@file:./private/@types/jasmine#./private/@types/jasmine::hash=17481a&locator=%40uppy-dev%2Fbuild%40workspace%3A."
+  checksum: ccc420c3ba7580cc562cb093705871265a961bce06bc366365fb32f9f73b276900d07ceb08591e4974bd40f766acc442145e78a70a0cf41a6dc1621999965073
   languageName: node
   linkType: hard
 
@@ -7959,9 +7959,9 @@ __metadata:
   languageName: unknown
   linkType: soft
 
-"@uppy-example/aws-presigned-url@workspace:examples/aws-presigned-url":
+"@uppy-example/aws-php@workspace:examples/aws-php":
   version: 0.0.0-use.local
-  resolution: "@uppy-example/aws-presigned-url@workspace:examples/aws-presigned-url"
+  resolution: "@uppy-example/aws-php@workspace:examples/aws-php"
   dependencies:
     "@uppy/aws-s3": "workspace:*"
     "@uppy/core": "workspace:*"
@@ -8202,6 +8202,17 @@ __metadata:
   languageName: unknown
   linkType: soft
 
+"@uppy-example/uppy-s3-example@workspace:examples/uppy-s3-example":
+  version: 0.0.0-use.local
+  resolution: "@uppy-example/uppy-s3-example@workspace:examples/uppy-s3-example"
+  dependencies:
+    aws-sdk: ^2.1225.0
+    body-parser: ^1.20.0
+    dotenv: ^16.0.2
+    express: ^4.18.1
+  languageName: unknown
+  linkType: soft
+
 "@uppy-example/uppy-with-companion@workspace:examples/uppy-with-companion":
   version: 0.0.0-use.local
   resolution: "@uppy-example/uppy-with-companion@workspace:examples/uppy-with-companion"
@@ -10774,6 +10785,24 @@ __metadata:
   languageName: node
   linkType: hard
 
+"aws-sdk@npm:^2.1225.0":
+  version: 2.1225.0
+  resolution: "aws-sdk@npm:2.1225.0"
+  dependencies:
+    buffer: 4.9.2
+    events: 1.1.1
+    ieee754: 1.1.13
+    jmespath: 0.16.0
+    querystring: 0.2.0
+    sax: 1.2.1
+    url: 0.10.3
+    util: ^0.12.4
+    uuid: 8.0.0
+    xml2js: 0.4.19
+  checksum: 4483fad82f3cf27540b6beeedbb0c3dd8d8e00751cbd1c7f996bc28822d052879db24b2a646ae1add561112051e0f68705ad77eaa16e13f12d51feb67d8094b1
+  languageName: node
+  linkType: hard
+
 "aws-sign2@npm:~0.7.0":
   version: 0.7.0
   resolution: "aws-sign2@npm:0.7.0"
@@ -15033,6 +15062,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"dotenv@npm:^16.0.2":
+  version: 16.0.3
+  resolution: "dotenv@npm:16.0.3"
+  checksum: afcf03f373d7a6d62c7e9afea6328e62851d627a4e73f2e12d0a8deae1cd375892004f3021883f8aec85932cd2834b091f568ced92b4774625b321db83b827f8
+  languageName: node
+  linkType: hard
+
 "dotenv@npm:^7.0.0":
   version: 7.0.0
   resolution: "dotenv@npm:7.0.0"