diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | app.js | 105 | ||||
-rw-r--r-- | app.json | 8 | ||||
-rw-r--r-- | docs/guides/images/s3-image-upload/bucket-policy-editor.png | bin | 0 -> 54641 bytes | |||
-rw-r--r-- | docs/guides/images/s3-image-upload/bucket-property.png | bin | 0 -> 71762 bytes | |||
-rw-r--r-- | docs/guides/images/s3-image-upload/create-bucket.png | bin | 0 -> 70260 bytes | |||
-rw-r--r-- | docs/guides/images/s3-image-upload/custom-policy.png | bin | 0 -> 55804 bytes | |||
-rw-r--r-- | docs/guides/images/s3-image-upload/iam-user.png | bin | 0 -> 91526 bytes | |||
-rw-r--r-- | docs/guides/images/s3-image-upload/review-policy.png | bin | 0 -> 100067 bytes | |||
-rw-r--r-- | docs/guides/s3-image-upload.md | 81 | ||||
-rw-r--r-- | lib/config.js | 16 | ||||
-rw-r--r-- | package.json | 5 | ||||
-rw-r--r-- | public/uploads/.gitkeep | 0 |
14 files changed, 205 insertions, 20 deletions
@@ -25,3 +25,6 @@ public/js/config.js # ignore webpack build public/build public/views/build + +public/uploads/* +!public/uploads/.gitkeep @@ -131,6 +131,11 @@ Environment variables (will overwrite other server configs) | HMD_GOOGLE_CLIENTID | no example | Google API client id | | HMD_GOOGLE_CLIENTSECRET | no example | Google API client secret | | HMD_IMGUR_CLIENTID | no example | Imgur API client id | +| HMD_IMAGE_UPLOAD_TYPE | `imgur`, `s3` or `filesystem` | Where to upload image. For S3, see our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) | +| HMD_S3_ACCESS_KEY_ID | no example | AWS access key id | +| HMD_S3_SECRET_ACCESS_KEY | no example | AWS secret key | +| HMD_S3_REGION | `ap-northeast-1` | AWS S3 region | +| HMD_S3_BUCKET | no example | AWS S3 bucket name | Server settings `config.json` --- @@ -166,6 +171,8 @@ Server settings `config.json` | heartbeatinterval | `5000` | socket.io heartbeat interval | | heartbeattimeout | `10000` | socket.io heartbeat timeout | | documentmaxlength | `100000` | note max length | +| imageUploadType | `imgur`(default), `s3` or `filesystem` | Where to upload image +| s3 | `{ "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", "secretAccessKey": "YOUR_S3_ACCESS_KEY", "region": "YOUR_S3_REGION", "bucket": "YOUR_S3_BUCKET_NAME" }` | When `imageUploadType` be setted to `s3`, you would also need to setup this key, check our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) | Third-party integration api key settings --- @@ -406,33 +406,102 @@ app.get('/me', function (req, res) { }); } }); -//upload to imgur + +//upload image app.post('/uploadimage', function (req, res) { var form = new formidable.IncomingForm(); + + form.keepExtensions = true; + + if (config.imageUploadType === 'filesystem') { + form.uploadDir = "public/uploads"; + } + + function preprocessImage(path) { + return new Promise((resolve) => { + var oldFile = `${path}-old`; + fs.rename(path, oldFile, function() { + var sharp = require('sharp'); + sharp(oldFile).toFile(path).then(() => { + fs.unlink(oldFile, function() { + resolve(path); + }) + }); + }); + }); + } + form.parse(req, function (err, fields, files) { if (err || !files.image || !files.image.path) { response.errorForbidden(res); } else { - if (config.debug) - logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)); - imgur.setClientId(config.imgur.clientID); - try { - imgur.uploadFile(files.image.path) - .then(function (json) { - if (config.debug) - logger.info('SERVER uploadimage success: ' + JSON.stringify(json)); + preprocessImage(files.image.path).then(() => { + if (config.debug) + logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)); + + var path = require('path'); + try { + switch (config.imageUploadType) { + case 'filesystem': res.send({ - link: json.data.link.replace(/^http:\/\//i, 'https://') + link: path.join(config.serverurl, files.image.path.match(/^public(.+$)/)[1]) }); - }) - .catch(function (err) { - logger.error(err); - return res.status(500).end('upload image error'); - }); - } catch (err) { + + break; + + case 's3': + var AWS = require('aws-sdk'); + var awsConfig = new AWS.Config(config.s3); + var s3 = new AWS.S3(awsConfig); + + fs.readFile(files.image.path, function (err, buffer) { + var params = { + Bucket: 'hackmd', + Key: path.join('uploads', path.basename(files.image.path)), + Body: buffer + }; + + s3.putObject(params, function (err, data) { + if (err) { + logger.error(err); + res.status(500).end('upload image error'); + } else { + res.send({ + link: `https://s3-${config.s3.region}.amazonaws.com/${config.s3bucket}/${params.Key}` + }); + } + }); + + }); + + break; + + case 'imgur': + default: + imgur.setClientId(config.imgur.clientID); + imgur.uploadFile(files.image.path) + .then(function (json) { + if (config.debug) + logger.info('SERVER uploadimage success: ' + JSON.stringify(json)); + res.send({ + link: json.data.link.replace(/^http:\/\//i, 'https://') + }); + }) + .catch(function (err) { + logger.error(err); + return res.status(500).end('upload image error'); + }); + break; + } + } catch (err) { + logger.error(err); + return res.status(500).end('upload image error'); + } + + }).catch((err) => { logger.error(err); - return res.status(500).end('upload image error'); - } + return res.status(500).end('process image error'); + }); } }); }); @@ -107,5 +107,13 @@ }, "addons": [ "heroku-postgresql" + ], + "buildpacks": [ + { + "url": "https://github.com/alex88/heroku-buildpack-vips" + }, + { + "url": "https://github.com/heroku/heroku-buildpack-nodejs" + } ] } diff --git a/docs/guides/images/s3-image-upload/bucket-policy-editor.png b/docs/guides/images/s3-image-upload/bucket-policy-editor.png Binary files differnew file mode 100644 index 00000000..d5f4427f --- /dev/null +++ b/docs/guides/images/s3-image-upload/bucket-policy-editor.png diff --git a/docs/guides/images/s3-image-upload/bucket-property.png b/docs/guides/images/s3-image-upload/bucket-property.png Binary files differnew file mode 100644 index 00000000..9f4dc828 --- /dev/null +++ b/docs/guides/images/s3-image-upload/bucket-property.png diff --git a/docs/guides/images/s3-image-upload/create-bucket.png b/docs/guides/images/s3-image-upload/create-bucket.png Binary files differnew file mode 100644 index 00000000..925f0926 --- /dev/null +++ b/docs/guides/images/s3-image-upload/create-bucket.png diff --git a/docs/guides/images/s3-image-upload/custom-policy.png b/docs/guides/images/s3-image-upload/custom-policy.png Binary files differnew file mode 100644 index 00000000..01c570cd --- /dev/null +++ b/docs/guides/images/s3-image-upload/custom-policy.png diff --git a/docs/guides/images/s3-image-upload/iam-user.png b/docs/guides/images/s3-image-upload/iam-user.png Binary files differnew file mode 100644 index 00000000..b26be122 --- /dev/null +++ b/docs/guides/images/s3-image-upload/iam-user.png diff --git a/docs/guides/images/s3-image-upload/review-policy.png b/docs/guides/images/s3-image-upload/review-policy.png Binary files differnew file mode 100644 index 00000000..7173788a --- /dev/null +++ b/docs/guides/images/s3-image-upload/review-policy.png diff --git a/docs/guides/s3-image-upload.md b/docs/guides/s3-image-upload.md new file mode 100644 index 00000000..3d6259b9 --- /dev/null +++ b/docs/guides/s3-image-upload.md @@ -0,0 +1,81 @@ +# Guide - Setup HackMD S3 image upload + +1. Go to [AWS S3 console](https://console.aws.amazon.com/s3/home) and create a new bucket. + + ![create-bucket](images/s3-image-upload/create-bucket.png) + +2. Click on bucket, select **Properties** on the side panel, and find **Permission** section. Click **Edit bucket policy**. + + ![bucket-property](images/s3-image-upload/bucket-property.png) + +3. Enter the following policy, replace `bucket_name` with your bucket name: + + ![bucket-policy-editor](images/s3-image-upload/bucket-policy-editor.png) + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": "*", + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::bucket_name/uploads/*" + } + ] + } + ``` + +4. Go to IAM console and create a new IAM user. Remember your user credentials(`key`/`access token`) + +5. Enter user page, select **Permission** tab, look at **Inline Policies** section, and click **Create User Policy** + + ![iam-user](images/s3-image-upload/iam-user.png) + +6. Select **Custom Policy** + + ![custom-policy](images/s3-image-upload/custom-policy.png) + +7. Enter the following policy, replace `bucket_name` with your bucket name: + + ![review-policy](images/s3-image-upload/review-policy.png) + + ```json + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:*" + ], + "Resource": [ + "arn:aws:s3:::bucket_name/uploads/*" + ] + } + ] + } + ``` + +8. Edit `config.json` and set following keys: + + ```javascript + { + "production": { + ... + "imageUploadType": "s3", + "s3": { + "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", + "secretAccessKey": "YOUR_S3_ACCESS_KEY", + "region": "YOUR_S3_REGION", // example: ap-northeast-1 + "bucket": "YOUR_S3_BUCKET_NAME" + } + } + } + ``` + +9. In additional to edit `config.json` directly, you could also try [environment variable](https://github.com/hackmdio/hackmd#environment-variables-will-overwrite-other-server-configs). + +## Related Tools + +* [AWS Policy Generator](http://awspolicygen.s3.amazonaws.com/policygen.html) diff --git a/lib/config.js b/lib/config.js index 76b21f0d..a906dfa4 100644 --- a/lib/config.js +++ b/lib/config.js @@ -56,6 +56,17 @@ var heartbeattimeout = config.heartbeattimeout || 10000; // document var documentmaxlength = config.documentmaxlength || 100000; +// image upload setting, available options are imgur/s3/filesystem +var imageUploadType = process.env.HMD_IMAGE_UPLOAD_TYPE || config.imageUploadType || 'imgur'; + +config.s3 = config.s3 || {}; +var s3 = { + accessKeyId: process.env.HMD_S3_ACCESS_KEY_ID || config.s3.accessKeyId, + secretAccessKey: process.env.HMD_S3_SECRET_ACCESS_KEY || config.s3.secretAccessKey, + region: process.env.HMD_S3_REGION || config.s3.region +} +var s3bucket = process.env.HMD_S3_BUCKET || config.s3.bucket; + // auth var facebook = (process.env.HMD_FACEBOOK_CLIENTID && process.env.HMD_FACEBOOK_CLIENTSECRET) ? { clientID: process.env.HMD_FACEBOOK_CLIENTID, @@ -139,5 +150,8 @@ module.exports = { gitlab: gitlab, dropbox: dropbox, google: google, - imgur: imgur + imgur: imgur, + imageUploadType: imageUploadType, + s3: s3, + s3bucket: s3bucket }; diff --git a/package.json b/package.json index bdbfe748..13485336 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "app.js", "license": "MIT", "scripts": { - "dev": "webpack --config webpack.config.js --progress --colors --watch", + "dev": "webpack --config webpack.config.js --progress --colors --watch & nodemon app.js", "build": "webpack --config webpack.production.js --progress --colors", "assets:install": "bower install", "postinstall": "bin/heroku", @@ -14,6 +14,7 @@ "dependencies": { "Idle.Js": "github:shawnmclean/Idle.js", "async": "^2.0.1", + "aws-sdk": "^2.7.0", "blueimp-md5": "^2.4.0", "body-parser": "^1.15.2", "bootstrap": "^3.3.7", @@ -95,6 +96,7 @@ "sequelize": "^3.24.3", "select2": "^3.5.2-browserify", "sequelize-cli": "^2.4.0", + "sharp": "^0.16.2", "shortid": "2.2.6", "socket.io": "~1.6.0", "socket.io-client": "~1.6.0", @@ -149,6 +151,7 @@ "json-loader": "^0.5.4", "less": "^2.7.1", "less-loader": "^2.2.3", + "nodemon": "^1.11.0", "optimize-css-assets-webpack-plugin": "^1.3.0", "script-loader": "^0.7.0", "style-loader": "^0.13.1", diff --git a/public/uploads/.gitkeep b/public/uploads/.gitkeep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/public/uploads/.gitkeep |