diff --git a/package.json b/package.json index 6e94b204..eaa5b57d 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "babel-preset-react": "^6.3.13", "babel-preset-react-hmre": "^1.0.1", "babel-preset-stage-0": "^6.5.0", + "crypto": "0.0.3", "debug": "^2.2.0", "extract-text-webpack-plugin": "^1.0.1", "html-webpack-plugin": "^2.16.1", @@ -47,6 +48,7 @@ "koa-router": "^7.0.1", "koa-sendfile": "^2.0.0", "localenv": "^0.2.2", + "moment": "^2.13.0", "pg": "^4.5.3", "pg-hstore": "^2.3.2", "querystring": "^0.2.0", @@ -57,6 +59,7 @@ "sequelize": "^3.21.0", "sequelize-cli": "^2.3.1", "sequelize-encrypted": "^0.1.0", + "uuid": "^2.0.2", "validator": "^5.2.0" }, "devDependencies": { diff --git a/server/api/user.js b/server/api/user.js index a23cf783..61726691 100644 --- a/server/api/user.js +++ b/server/api/user.js @@ -1,5 +1,10 @@ +import uuid from 'uuid'; import Router from 'koa-router'; +import { + makePolicy, + signPolicy, +} from '../utils/s3'; import auth from './authentication'; import { presentUser } from '../presenters'; import { User } from '../models'; @@ -7,7 +12,40 @@ import { User } from '../models'; const router = new Router(); router.post('user.info', auth(), async (ctx) => { - ctx.body = { data: presentUser(ctx.state.user) }; + ctx.body = { data: await presentUser(ctx.state.user) }; +}); + +router.post('user.s3Upload', auth(), async (ctx) => { + let { filename, kind, size } = ctx.request.body; + ctx.assertPresent(filename, 'filename is required'); + ctx.assertPresent(kind, 'kind is required'); + ctx.assertPresent(size, 'size is required'); + + const s3Key = uuid.v4(); + const key = `${s3Key}/${filename}`; + const policy = makePolicy(); + + console.log(policy, signPolicy(policy)); + + ctx.body = { data: { + max_upload_size: process.env.AWS_S3_UPLOAD_MAX_SIZE, + upload_url: process.env.AWS_S3_UPLOAD_BUCKET_URL, + form: { + AWSAccessKeyId: process.env.AWS_ACCESS_KEY_ID, + "Cache-Control": "max-age=31557600", + "Content-Type": kind, + key: key, + acl: "public-read", + signature: signPolicy(policy), + policy: policy, + }, + asset: { + content_type: kind, + url: `${process.env.AWS_S3_UPLOAD_BUCKET_URL}${s3Key}/${filename}`, + name: filename, + size: size, + }, + }}; }); export default router; \ No newline at end of file diff --git a/server/utils/s3.js b/server/utils/s3.js new file mode 100644 index 00000000..f9f8b033 --- /dev/null +++ b/server/utils/s3.js @@ -0,0 +1,33 @@ +import crypto from 'crypto'; +import moment from 'moment'; + +const makePolicy = () => { + const policy = { + conditions: [ + {'bucket': process.env.AWS_S3_UPLOAD_BUCKET_NAME}, + ['starts-with', '$key', ''], + {'acl': 'public-read'}, + ['content-length-range', 0, process.env.AWS_S3_UPLOAD_MAX_SIZE], + ['starts-with', '$Content-Type', 'image'], + ['starts-with', '$Cache-Control', ''], + ], + expiration: moment().add(24*60, 'minutes').format('YYYY-MM-DDTHH:mm:ss\\Z'), + }; + + console.log(policy) + + return new Buffer(JSON.stringify(policy)).toString('base64') +}; + +const signPolicy = (policy) => { + const signature = crypto.createHmac( + 'sha1', + process.env.AWS_SECRET_ACCESS_KEY + ).update(policy).digest('base64'); + return signature; +}; + +export { + makePolicy, + signPolicy, +}; diff --git a/src/Components/MarkdownEditor/MarkdownEditor.js b/src/Components/MarkdownEditor/MarkdownEditor.js index c11d7fe3..b0e730e7 100644 --- a/src/Components/MarkdownEditor/MarkdownEditor.js +++ b/src/Components/MarkdownEditor/MarkdownEditor.js @@ -53,12 +53,13 @@ class MarkdownAtlas extends React.Component { } editor.setCursor(newCursorPositionLine, 0); - client.post('/v0/user/s3', { + client.post('/user.s3Upload', { kind: file.type, size: file.size, filename: file.name, }) - .then(data => { + .then(response => { + const data = response.data; // Upload using FormData API let formData = new FormData();