├── .gitignore ├── LICENSE ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # publish-react-app 2 | Tool for automatically publishing a react app to the internet via S3 3 | 4 | ## Exampe usage 5 | 6 | ```bash 7 | npm install -g create-react-app publish-react-app 8 | create-react-app myapp 9 | cd myapp/ 10 | npm run build 11 | AWS_ACCESS_KEY_ID={Your AWS key ID here} AWS_SECRET_ACCESS_KEY={Your AWS secret here} publish-react-app 12 | # Your website is now available: [your url will be here] 13 | ``` 14 | 15 | ## What it does 16 | 17 | So you've created a React app. Now you want to get it off of `localhost` and put it on the real internet. But you don't want to think about how cloud hosting works. You just want to run a command. This tool does just that. Given a users AWS credentials, this tool will upload all of the contents generated by a project started by create-react-app and get a sharable link to the final product. Since S3 is free for the first year, and then cheap after that, it is an ideal way to share your React apps. 18 | 19 | In short, this package will take the contents of your `./build` folder and upload them to S3. The package will create bucket in your account. You will be given a randomly generated URL of where the S3 bucket is. 20 | 21 | ## What are AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY? 22 | 23 | If you do not have your own AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY here is how you can get one: 24 | 25 | 1. Create an account by signing up to https://aws.amazon.com/ 26 | 2. Add a user by going to https://console.aws.amazon.com/iam/home?#/users$new?step=details 27 | * Select __Programmatic access__ as __Access Type__ 28 | * Hit the __Next: Permissions__ button 29 | * Hit __Create group__ 30 | * Make group name something like "Admin" and select `AdministratorAccess` as the policy name 31 | * Hit the __Create policy button.__ 32 | * Hit the __Next: Review__ button 33 | * Hit the __Create user__ 34 | 3. At the final screen, the Access key ID and Secret access key strings will be visible. 35 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | (function main () { 3 | var S3 = require('aws-sdk/clients/s3') 4 | var fs = require('fs') 5 | var path = require('path') 6 | var mime = require('mime') 7 | var randomWords = require('random-words') 8 | 9 | var bucketName = randomWords(5).join('-') 10 | var awsLocation = 'us-west-1' 11 | var params = { 12 | ACL: "public-read", 13 | Bucket: bucketName, 14 | CreateBucketConfiguration: { 15 | LocationConstraint: awsLocation 16 | } 17 | } 18 | var s3 = new S3({apiVersion: '2006-03-01'}) 19 | var doneFileCount = 0 20 | var totalFileCount = 0 21 | s3.createBucket(params, function(err, data) { 22 | if (err) console.log(err, err.stack) 23 | else { 24 | var Location = data.Location 25 | var policyPublic = { 26 | Sid: 'AddPublicReadPermissions', 27 | Effect: 'Allow', 28 | Principal: '*', 29 | Action: 's3:GetObject', 30 | Resource: 'arn:aws:s3:::' + bucketName + '/*' 31 | } 32 | 33 | var policy = { 34 | Statement: [policyPublic] 35 | } 36 | 37 | var websiteConfig = { 38 | Bucket: bucketName, 39 | WebsiteConfiguration: { 40 | IndexDocument: { 41 | Suffix: 'index.html' 42 | } 43 | } 44 | } 45 | s3.putBucketPolicy({ Bucket: bucketName, Policy: JSON.stringify(policy) }, function(err, data) { 46 | if (err) console.log(err, err.stack) 47 | else { 48 | s3.putBucketWebsite(websiteConfig, function (err, data) { 49 | if (err) console.log(err, err.stack) 50 | else { 51 | s3.getBucketWebsite({ Bucket: bucketName }, function (err, website) { 52 | if (err) console.log(err, err.stack) 53 | else { 54 | uploadFilesToS3('./build') 55 | } 56 | }) 57 | } 58 | }) 59 | } 60 | }) 61 | } 62 | }) 63 | function pathWithoutBuild(path) { 64 | return path.substring(6) 65 | } 66 | function uploadFilesToS3 (directory) { 67 | fs.readdir(directory, (err, files) => { 68 | if(!files || files.length === 0) { 69 | console.log('No built files. Did you run `npm run build`?'); 70 | return; 71 | } 72 | totalFileCount += files.length 73 | files.forEach(function (file) { 74 | const filePath = path.join(directory, file); 75 | if (fs.lstatSync(filePath).isDirectory()) { 76 | doneFileCount++ 77 | uploadFilesToS3(directory + '/' + file) 78 | } else { 79 | var params = { 80 | Bucket: bucketName, 81 | Key: pathWithoutBuild(filePath), 82 | Body: fs.createReadStream(filePath), 83 | ContentType: mime.getType(filePath) 84 | } 85 | s3.putObject(params, function (err, data) { 86 | if (err) console.log(err) 87 | else { 88 | doneFileCount++ 89 | if (doneFileCount === totalFileCount) { 90 | console.log('Your website is now available:') 91 | console.log('http://' + bucketName + '.s3-website-' + awsLocation + '.amazonaws.com/') 92 | } 93 | } 94 | }) 95 | } 96 | }) 97 | }) 98 | } 99 | })() 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "publish-react-app", 3 | "version": "1.0.1", 4 | "description": "Tool for publishing a react app publicly via S3", 5 | "main": "index.js", 6 | "bin": "index.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/FenrisWulf/publish-react-app.git" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "deploy", 17 | "publish" 18 | ], 19 | "author": "FenrisWulf", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/FenrisWulf/publish-react-app/issues" 23 | }, 24 | "homepage": "https://github.com/FenrisWulf/publish-react-app#readme", 25 | "dependencies": { 26 | "aws-sdk": "^2.166.0", 27 | "mime": "^2.0.3", 28 | "random-words": "0.0.1" 29 | } 30 | } 31 | --------------------------------------------------------------------------------