├── .gitignore ├── package.json ├── LICENSE.md ├── .github └── workflows │ └── npm-publish.yml ├── README.md └── lib └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "strapi-provider-upload-do", 3 | "version": "3.6.9", 4 | "description": "Digital ocean spaces provider for Strapi upload plugin", 5 | "homepage": "http://strapi.io", 6 | "keywords": [ 7 | "upload", 8 | "digitalocean", 9 | "spaces", 10 | "strapi", 11 | "provider" 12 | ], 13 | "directories": { 14 | "lib": "./lib" 15 | }, 16 | "main": "./lib", 17 | "dependencies": { 18 | "aws-sdk": "2.1350.0", 19 | "urijs": "1.19.6" 20 | }, 21 | "strapi": { 22 | "isProvider": true 23 | }, 24 | "author": { 25 | "email": "stanley@hsjm.io", 26 | "name": "Stanley Horwood", 27 | "url": "https://hsjm.io" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/shorwood/strapi-provider-upload-do.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/shorwood/strapi-provider-upload-do/issues" 35 | }, 36 | "engines": { 37 | "node": ">= 10.0.0", 38 | "npm": ">= 6.0.0" 39 | }, 40 | "license": "MIT" 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2019 Strapi Solutions. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Publish Node.js Package 5 | 6 | on: push 7 | 8 | jobs: 9 | build: 10 | name: Building and testing Node.js Package. 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 12 17 | - run: npm ci 18 | - run: npm test 19 | 20 | publish-npm: 21 | name: Publishing Node.js Package to NPM package registry. 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v1 25 | - uses: actions/setup-node@v1 26 | with: 27 | node-version: 12 28 | - run: npm install 29 | - run: npm test 30 | - uses: JS-DevTools/npm-publish@v1 31 | with: 32 | registry: https://registry.npmjs.org/ 33 | token: ${{ secrets.NPM_TOKEN }} 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Strapi Upload Provider for Digital Ocean Spaces 2 | - This provider is a fork of [AdamZikmund's](https://github.com/AdamZikmund) [strapi upload provider](https://github.com/AdamZikmund/strapi-provider-upload-digitalocean) for Digital Ocean spaces. 3 | 4 | This provider will upload to the space using the AWS S3 API. 5 | 6 | ## Parameters 7 | - **key** : [Space access key](https://cloud.digitalocean.com/account/api/tokens) 8 | - **secret** : [Space access secret](https://cloud.digitalocean.com/account/api/tokens) 9 | - **endpoint** : Base URL of the space (e.g. `fra.digitaloceanspaces.com`) 10 | - **space** : Name of the space in the Digital Ocean panel. 11 | - **directory** : Name of the sub-directory you want to store your files in. (Optionnal - e.g. `/example`) 12 | - **cdn** : CDN Endpoint - URL of the cdn of the space (Optionnal - e.g. `cdn.example.com`) 13 | 14 | ## How to use 15 | 16 | 1. Install this package 17 | 18 | ```bash 19 | npm i strapi-provider-upload-do 20 | ``` 21 | ```bash 22 | yarn add strapi-provider-upload-do 23 | ``` 24 | ```bash 25 | pnpm add strapi-provider-upload-do 26 | ``` 27 | 28 | 2. Create or update config in `./config/plugins.js` with content 29 | 30 | ```js 31 | module.exports = ({env}) => ({ 32 | // ... 33 | upload: { 34 | config: { 35 | provider: "strapi-provider-upload-do", 36 | providerOptions: { 37 | key: env('DO_SPACE_ACCESS_KEY'), 38 | secret: env('DO_SPACE_SECRET_KEY'), 39 | endpoint: env('DO_SPACE_ENDPOINT'), 40 | space: env('DO_SPACE_BUCKET'), 41 | directory: env('DO_SPACE_DIRECTORY'), 42 | cdn: env('DO_SPACE_CDN'), 43 | } 44 | }, 45 | }, 46 | // ... 47 | }) 48 | 49 | ``` 50 | 51 | 3. Create `.env` and add provide Digital Ocean config. 52 | 53 | ```bash 54 | DO_SPACE_ACCESS_KEY= 55 | DO_SPACE_SECRET_KEY= 56 | DO_SPACE_ENDPOINT= 57 | DO_SPACE_BUCKET= 58 | DO_SPACE_DIRECTORY= 59 | DO_SPACE_CDN= 60 | ``` 61 | 62 | with values obtained from tutorial: 63 | 64 | > https://www.digitalocean.com/community/tutorials/how-to-create-a-digitalocean-space-and-api-key 65 | 66 | Parameter `DO_SPACE_DIRECTORY` and `DO_SPACE_CDN` is optional and you can ommit them both in `.env` and `settings`. 67 | 68 | ## Resources 69 | 70 | - [MIT License](LICENSE.md) 71 | 72 | ## Links 73 | 74 | - [Strapi website](http://strapi.io/) 75 | - [Strapi community on Slack](http://slack.strapi.io) 76 | - [Strapi news on Twitter](https://twitter.com/strapijs) 77 | - [Strapi docs about upload](https://strapi.io/documentation/3.0.0-beta.x/plugins/upload.html#configuration) 78 | 79 | ## Contributors 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const AWS = require('aws-sdk'); 3 | const URI = require('urijs'); 4 | const crypto = require('crypto'); 5 | 6 | class FileLocationConverter { 7 | constructor(config) { 8 | this.config = config; 9 | } 10 | 11 | getKey(file) { 12 | const filename = `${file.hash}${file.ext}`; 13 | if (!this.config.directory) return filename; 14 | return `${this.config.directory}/${filename}`; 15 | } 16 | 17 | getUrl(data) { 18 | if (!this.config.cdn) return data.Location; 19 | var parts = {}; 20 | URI.parseHost(this.config.cdn, parts); 21 | parts.protocol = "https"; // Force https 22 | parts.path = data.Key; 23 | return URI.build(parts); 24 | } 25 | } 26 | 27 | module.exports = { 28 | provider: "do", 29 | name: "Digital Ocean Spaces", 30 | auth: { 31 | key: { 32 | label: "Key", 33 | type: "text" 34 | }, 35 | secret: { 36 | label: "Secret", 37 | type: "text" 38 | }, 39 | endpoint: { 40 | label: "Endpoint (e.g. 'fra1.digitaloceanspaces.com')", 41 | type: "text", 42 | }, 43 | cdn: { 44 | label: "CDN Endpoint (Optional - e.g. 'https://cdn.space.com')", 45 | type: "text", 46 | }, 47 | space: { 48 | label: "Space (e.g. myspace)", 49 | type: "text", 50 | }, 51 | directory: { 52 | label: 'Directory (Optional - e.g. directory - place when you want to save files)', 53 | type: 'text' 54 | } 55 | }, 56 | init: config => { 57 | const endpoint = new AWS.Endpoint(config.endpoint); 58 | const converter = new FileLocationConverter(config); 59 | 60 | const S3 = new AWS.S3({ 61 | endpoint: endpoint, 62 | accessKeyId: config.key, 63 | secretAccessKey: config.secret, 64 | params: { 65 | ACL: 'public-read', 66 | Bucket: config.space, 67 | CacheControl: 'public, max-age=31536000, immutable' 68 | }, 69 | }); 70 | 71 | const upload = file => new Promise((resolve, reject) => { 72 | //--- Compute the file key. 73 | file.hash = crypto.createHash('md5').update(file.hash).digest("hex"); 74 | 75 | //--- Upload the file into the space (technically the S3 Bucket) 76 | S3.upload({ 77 | Key: converter.getKey(file), 78 | Body: Buffer.from(file.buffer, "binary"), 79 | ContentType: file.mime 80 | }, 81 | 82 | //--- Callback handler 83 | (err, data) => { 84 | if (err) return reject(err); 85 | file.url = converter.getUrl(data); 86 | delete file.buffer; 87 | resolve(); 88 | }); 89 | }); 90 | 91 | return { 92 | upload, 93 | 94 | uploadStream: file => new Promise((resolve, reject) => { 95 | const _buf = []; 96 | 97 | file.stream.on('data', chunk => _buf.push(chunk)); 98 | file.stream.on('end', () => { 99 | file.buffer = Buffer.concat(_buf); 100 | resolve(upload(file)); 101 | }); 102 | file.stream.on('error', err => reject(err)); 103 | }), 104 | 105 | delete: file => new Promise((resolve, reject) => { 106 | 107 | //--- Delete the file from the space 108 | S3.deleteObject({ 109 | Bucket: config.bucket, 110 | Key: converter.getKey(file), 111 | }, 112 | 113 | //--- Callback handler 114 | (err, data) => { 115 | if (err) return reject(err); 116 | else resolve(); 117 | }) 118 | } 119 | ) 120 | } 121 | } 122 | } 123 | --------------------------------------------------------------------------------