diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..db47f05
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+
Illegal Music Site
+
+
+ {{@each(it.songs) => s, i}}
+
+
{{s.Title}} - {{s.Artist}}
+
+
+ {{/each}}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/index.js b/src/index.js
index 56fe79d..41b7c61 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,48 +1,144 @@
var sqrl = require('squirrelly')
const fs = require('fs')
const AWS = require('aws-sdk');
+const busboy = require('busboy')
const docClient = new AWS.DynamoDB.DocumentClient();
+const s3 = new AWS.S3()
+// load html template
+const template = fs.readFileSync('./index.html').toString()
+
+// parameter for full table scan
const params = {
TableName: 'MusicTable'
}
exports.handler = async (e, ctx) => {
+
+ // log every request into CloudWatch
console.log(e)
+
+ // handle 'POST /add' for adding songs to DB
if (e.requestContext.http.method == "POST" && e.requestContext.http.path == "/add") {
- // handle POST /add
- let data = JSON.parse(e.body) // body content sent as JSON
- docClient.put({
- TableName: 'MusicTable',
- Item: {
- id: `${data.Title}-${data.Artist}`,
- Title: data.Title,
- Artist: data.Artist,
- File: 'notyetimplemented.mp3'
- }
- }, (err, d) => {
- if (err) {
- console.log(err)
- } else {
- console.log(d)
+
+ // decode base64 body (if necessary)
+ if (e.isBase64Encoded == true) {
+ let raw64 = e.body
+ let buff = Buffer.from(raw64, 'base64')
+ e.body = buff.toString('ascii')
+ }
+
+ // get content headers
+ var contentType = e.headers['Content-Type'] || e.headers['content-type'];
+
+ // the form object
+ let result = {
+ filename: "",
+ Title: "",
+ Artist: ""
+ }
+ let data = null
+
+ // to store file info so it can be sent to s3
+ let fileinfo = {}
+
+ // busboy reads and decodes the event body (form data)
+ var bb = busboy({ headers: { 'content-type': contentType }})
+ bb.on('file', function (field, file, info) {
+ // handle a file in the form
+
+ // create the file name
+ let r = Math.floor((Math.random() * 10000000000000000)).toString(36)
+ result.filename = 'songs/' + r + '.mp3'
+
+ // get file info (mime type, etc)
+ fileinfo = info
+
+ // get file data
+ file.on('data', (d) => {
+ if (data === null) {
+ data = d
+ } else {
+ data = Buffer.concat([data, d])
+ }
+
+ // need file.resume(), otherwise .on('finish') will not fire.
+ // may have unintended side effects if the file is too large.
+ file.resume()
+ })
+
+ })
+ // handle non-file fields in form
+ .on('field', (fieldname, val) => {
+ result[fieldname] = val
+ })
+ .on('finish', () => {
+ // when the file is completely processed
+
+ // s3 paramters
+ let params = {
+ Bucket: 'group7-code-bucket-73h3fdsa',
+ Key: result.filename,
+ Body: data,
+ ContentType: fileinfo.mimeType,
+ ContentEncoding: fileinfo.encoding,
+ ACL: 'public-read',
}
+ let options = {partSize: 15 * 1024 * 1024, queueSize: 10} // 5 MB
+
+ // upload the file to s3
+ s3.upload(params, options, (err, data) => {
+ console.log(err, data)
+ }).on('httpUploadProgress', (evt) => {
+ console.log(evt)
+ }).send((err, data) => {
+ console.log(err, data)
+ })
+
+ // put the song entry into dynamodb
+ docClient.put({
+ TableName: 'MusicTable',
+ Item: {
+ id: `${result.Title}-${result.Artist}`,
+ Title: result.Title,
+ Artist: result.Artist,
+ File: `https://group7-code-bucket-73h3fdsa.s3.amazonaws.com/${result.filename}`
+ }
+ }, (err, d) => {
+ if (err) {
+ console.log(err)
+ } else {
+ console.log(d)
+ }
+ })
})
- return JSON.stringify({
- success: true
+ // form parsing error
+ .on('error', err => {
+ console.log('failed', err);
})
+ // finish form decode
+ bb.end(e.body)
} else {
+ // handle default 'GET /' requests
+
+ // scan whole MusicTable
let scan = await docClient.scan(params).promise()
let data = {
songs: []
}
+
+ // map scan into data object songs array
do {
scan.Items.forEach((i) => data.songs.push(i))
params.ExclusiveStartKey = scan.LastEvaluatedKey
} while (typeof scan.LastEvaluatedKey != 'undefined')
+
+ // render html template with songs
return response(sqrl.render(template, data))
}
}
+// returns standard HTML response (for serving template)
function response(html){
return {
"statusCode": 200,
@@ -51,73 +147,4 @@ function response(html){
"Content-Type": "text/html",
}
}
-}
-
-const template = `
-
-
-
-
-
Illegal Music Site
-
-
- {{@each(it.songs) => s, i}}
-
-
{{s.Title}} - {{s.Artist}}
-
-
- {{/each}}
-
-
-
-
-
-
-`
+}
\ No newline at end of file
diff --git a/src/node_modules/.package-lock.json b/src/node_modules/.package-lock.json
index b04d1d5..1c630ab 100644
--- a/src/node_modules/.package-lock.json
+++ b/src/node_modules/.package-lock.json
@@ -4,6 +4,17 @@
"lockfileVersion": 2,
"requires": true,
"packages": {
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
"node_modules/squirrelly": {
"version": "8.0.8",
"resolved": "https://registry.npmjs.org/squirrelly/-/squirrelly-8.0.8.tgz",
@@ -14,6 +25,14 @@
"funding": {
"url": "https://github.com/squirrellyjs/squirrelly?sponsor=1"
}
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
}
}
}
diff --git a/src/node_modules/busboy/.eslintrc.js b/src/node_modules/busboy/.eslintrc.js
new file mode 100644
index 0000000..be9311d
--- /dev/null
+++ b/src/node_modules/busboy/.eslintrc.js
@@ -0,0 +1,5 @@
+'use strict';
+
+module.exports = {
+ extends: '@mscdex/eslint-config',
+};
diff --git a/src/node_modules/busboy/.github/workflows/ci.yml b/src/node_modules/busboy/.github/workflows/ci.yml
new file mode 100644
index 0000000..799bae0
--- /dev/null
+++ b/src/node_modules/busboy/.github/workflows/ci.yml
@@ -0,0 +1,24 @@
+name: CI
+
+on:
+ pull_request:
+ push:
+ branches: [ master ]
+
+jobs:
+ tests-linux:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version: [10.16.0, 10.x, 12.x, 14.x, 16.x]
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - name: Install module
+ run: npm install
+ - name: Run tests
+ run: npm test
diff --git a/src/node_modules/busboy/.github/workflows/lint.yml b/src/node_modules/busboy/.github/workflows/lint.yml
new file mode 100644
index 0000000..9f9e1f5
--- /dev/null
+++ b/src/node_modules/busboy/.github/workflows/lint.yml
@@ -0,0 +1,23 @@
+name: lint
+
+on:
+ pull_request:
+ push:
+ branches: [ master ]
+
+env:
+ NODE_VERSION: 16.x
+
+jobs:
+ lint-js:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Use Node.js ${{ env.NODE_VERSION }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: ${{ env.NODE_VERSION }}
+ - name: Install ESLint + ESLint configs/plugins
+ run: npm install --only=dev
+ - name: Lint files
+ run: npm run lint
diff --git a/src/node_modules/busboy/LICENSE b/src/node_modules/busboy/LICENSE
new file mode 100644
index 0000000..290762e
--- /dev/null
+++ b/src/node_modules/busboy/LICENSE
@@ -0,0 +1,19 @@
+Copyright Brian White. All rights reserved.
+
+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:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+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.
\ No newline at end of file
diff --git a/src/node_modules/busboy/README.md b/src/node_modules/busboy/README.md
new file mode 100644
index 0000000..654af30
--- /dev/null
+++ b/src/node_modules/busboy/README.md
@@ -0,0 +1,191 @@
+# Description
+
+A node.js module for parsing incoming HTML form data.
+
+Changes (breaking or otherwise) in v1.0.0 can be found [here](https://github.com/mscdex/busboy/issues/266).
+
+# Requirements
+
+* [node.js](http://nodejs.org/) -- v10.16.0 or newer
+
+
+# Install
+
+ npm install busboy
+
+
+# Examples
+
+* Parsing (multipart) with default options:
+
+```js
+const http = require('http');
+
+const busboy = require('busboy');
+
+http.createServer((req, res) => {
+ if (req.method === 'POST') {
+ console.log('POST request');
+ const bb = busboy({ headers: req.headers });
+ bb.on('file', (name, file, info) => {
+ const { filename, encoding, mimeType } = info;
+ console.log(
+ `File [${name}]: filename: %j, encoding: %j, mimeType: %j`,
+ filename,
+ encoding,
+ mimeType
+ );
+ file.on('data', (data) => {
+ console.log(`File [${name}] got ${data.length} bytes`);
+ }).on('close', () => {
+ console.log(`File [${name}] done`);
+ });
+ });
+ bb.on('field', (name, val, info) => {
+ console.log(`Field [${name}]: value: %j`, val);
+ });
+ bb.on('close', () => {
+ console.log('Done parsing form!');
+ res.writeHead(303, { Connection: 'close', Location: '/' });
+ res.end();
+ });
+ req.pipe(bb);
+ } else if (req.method === 'GET') {
+ res.writeHead(200, { Connection: 'close' });
+ res.end(`
+
+
+
+