diff --git a/UploadData/UploadMe.txt b/UploadData/UploadMe.txt deleted file mode 100644 index 574826f..0000000 --- a/UploadData/UploadMe.txt +++ /dev/null @@ -1 +0,0 @@ -I should be uploaded \ No newline at end of file diff --git a/handler.js b/handler.js deleted file mode 100644 index 13dd03f..0000000 --- a/handler.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -module.exports.hello = async event => { - return { - statusCode: 200, - body: JSON.stringify( - { - message: 'Go Serverless v1.0! Your function executed successfully!', - input: event, - }, - null, - 2 - ), - }; - - // Use this code if you don't use the http event with the LAMBDA-PROXY integration - // return { message: 'Go Serverless v1.0! Your function executed successfully!', event }; -}; diff --git a/lambdas/common/API_Responses.js b/lambdas/common/API_Responses.js new file mode 100644 index 0000000..5592395 --- /dev/null +++ b/lambdas/common/API_Responses.js @@ -0,0 +1,27 @@ +const Responses = { + _200(data = {}) { + return { + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Methods': '*', + 'Access-Control-Allow-Origin': '*', + }, + statusCode: 200, + body: JSON.stringify(data), + }; + }, + + _400(data = {}) { + return { + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Methods': '*', + 'Access-Control-Allow-Origin': '*', + }, + statusCode: 400, + body: JSON.stringify(data), + }; + }, +}; + +module.exports = Responses; diff --git a/lambdas/common/Dynamo.js b/lambdas/common/Dynamo.js new file mode 100644 index 0000000..f5ca5d3 --- /dev/null +++ b/lambdas/common/Dynamo.js @@ -0,0 +1,54 @@ +const AWS = require('aws-sdk'); + +const documentClient = new AWS.DynamoDB.DocumentClient(); + +const Dynamo = { + async get(ID, TableName) { + const params = { + TableName, + Key: { + ID, + }, + }; + + const data = await documentClient.get(params).promise(); + + if (!data || !data.Item) { + throw Error(`There was an error fetching the data for ID of ${ID} from ${TableName}`); + } + console.log(data); + + return data.Item; + }, + + async write(data, TableName) { + if (!data.ID) { + throw Error('no ID on the data'); + } + + const params = { + TableName, + Item: data, + }; + + const res = await documentClient.put(params).promise(); + + if (!res) { + throw Error(`There was an error inserting ID of ${data.ID} in table ${TableName}`); + } + + return data; + }, + + async delete(ID, TableName) { + const params = { + TableName, + Key: { + ID, + }, + }; + + return documentClient.delete(params).promise(); + }, +}; +module.exports = Dynamo; diff --git a/lambdas/common/S3.js b/lambdas/common/S3.js new file mode 100644 index 0000000..90dca9a --- /dev/null +++ b/lambdas/common/S3.js @@ -0,0 +1,40 @@ +const AWS = require('aws-sdk'); + +const s3Client = new AWS.S3(); + +const S3 = { + async get(fileName, bucket) { + const params = { + Bucket: bucket, + Key: fileName, + }; + + let data = await s3Client.getObject(params).promise(); + + if (!data) { + throw Error(`Failed to get file ${fileName}, from ${bucket}`); + } + + if (fileName.slice(fileName.length - 4, fileName.length) == 'json') { + data = data.Body.toString(); + } + return data; + }, + async write(data, fileName, bucket) { + const params = { + Bucket: bucket, + Body: JSON.stringify(data), + Key: fileName, + }; + + const newData = await s3Client.putObject(params).promise(); + + if (!newData) { + throw Error('there was an error writing the file'); + } + + return newData; + }, +}; + +module.exports = S3; diff --git a/lambdas/common/websocketMessage.js b/lambdas/common/websocketMessage.js new file mode 100644 index 0000000..d24e79e --- /dev/null +++ b/lambdas/common/websocketMessage.js @@ -0,0 +1,24 @@ +const AWS = require('aws-sdk'); + +const create = (domainName, stage) => { + const endpoint = `${domainName}/${stage}`; + return new AWS.ApiGatewayManagementApi({ + apiVersion: '2018-11-29', + endpoint, + }); +}; + +const send = ({ domainName, stage, connectionID, message }) => { + const ws = create(domainName, stage); + + const postParams = { + Data: message, + ConnectionId: connectionID, + }; + + return ws.postToConnection(postParams).promise(); +}; + +module.exports = { + send, +}; diff --git a/lambdas/websockets/connect.js b/lambdas/websockets/connect.js new file mode 100644 index 0000000..626afdf --- /dev/null +++ b/lambdas/websockets/connect.js @@ -0,0 +1,22 @@ +const Responses = require('../common/API_Responses'); +const Dynamo = require('../common/Dynamo'); + +const tableName = process.env.tableName; + +exports.handler = async event => { + console.log('event', event); + + const { connectionId: connectionID, domainName, stage } = event.requestContext; + + const data = { + ID: connectionID, + date: Date.now(), + messages: [], + domainName, + stage, + }; + + await Dynamo.write(data, tableName); + + return Responses._200({ message: 'connected' }); +}; diff --git a/lambdas/websockets/default.js b/lambdas/websockets/default.js new file mode 100644 index 0000000..cf26a13 --- /dev/null +++ b/lambdas/websockets/default.js @@ -0,0 +1,7 @@ +const Responses = require('../common/API_Responses'); + +exports.handler = async event => { + console.log('event', event); + + return Responses._200({ message: 'default' }); +}; diff --git a/lambdas/websockets/disconnect.js b/lambdas/websockets/disconnect.js new file mode 100644 index 0000000..1724314 --- /dev/null +++ b/lambdas/websockets/disconnect.js @@ -0,0 +1,14 @@ +const Responses = require('../common/API_Responses'); +const Dynamo = require('../common/Dynamo'); + +const tableName = process.env.tableName; + +exports.handler = async event => { + console.log('event', event); + + const { connectionId: connectionID } = event.requestContext; + + await Dynamo.delete(connectionID, tableName); + + return Responses._200({ message: 'disconnected' }); +}; diff --git a/lambdas/websockets/message.js b/lambdas/websockets/message.js new file mode 100644 index 0000000..46a0096 --- /dev/null +++ b/lambdas/websockets/message.js @@ -0,0 +1,41 @@ +const Responses = require('../common/API_Responses'); +const Dynamo = require('../common/Dynamo'); +const WebSocket = require('../common/websocketMessage'); + +const tableName = process.env.tableName; + +exports.handler = async event => { + console.log('event', event); + + const { connectionId: connectionID } = event.requestContext; + + const body = JSON.parse(event.body); + + try { + const record = await Dynamo.get(connectionID, tableName); + const { messages, domainName, stage } = record; + + messages.push(body.message); + + const data = { + ...record, + messages, + }; + + await Dynamo.write(data, tableName); + + await WebSocket.send({ + domainName, + stage, + connectionID, + message: 'This is a reply to your message', + }); + console.log('sent message'); + + return Responses._200({ message: 'got a message' }); + } catch (error) { + return Responses._400({ message: 'message could not be received' }); + } + + return Responses._200({ message: 'got a message' }); +}; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 7601972..0000000 --- a/package-lock.json +++ /dev/null @@ -1,259 +0,0 @@ -{ - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@auth0/s3": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@auth0/s3/-/s3-1.0.0.tgz", - "integrity": "sha512-O8PTXJnA7n8ONBSwqlWa+aZ/vlOdZYnSCGQt25h87ALWNItY/Yij79TOnzIkMTJZ8aCpGXQPuIRziLmBliV++Q==", - "requires": { - "aws-sdk": "^2.346.0", - "fd-slicer": "~1.0.0", - "findit2": "~2.2.3", - "graceful-fs": "~4.1.4", - "mime": "^2.3.1", - "mkdirp": "~0.5.0", - "pend": "~1.2.0", - "rimraf": "~2.2.8", - "streamsink": "~1.2.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "aws-sdk": { - "version": "2.522.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.522.0.tgz", - "integrity": "sha512-JNUVaBqXwzDVqR/9dDw4a55aVsdDQYlf/cBM5bSj/g95wbuNWMzrY1TfAxEfSKwH0llp/1/xdXP75AKKp2UoSg==", - "requires": { - "buffer": "4.9.1", - "events": "1.1.1", - "ieee754": "1.1.8", - "jmespath": "0.15.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "uuid": "3.3.2", - "xml2js": "0.4.19" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" - }, - "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", - "requires": { - "pend": "~1.2.0" - } - }, - "findit2": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", - "integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" - }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" - }, - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" - }, - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" - }, - "sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" - }, - "serverless-s3-sync": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/serverless-s3-sync/-/serverless-s3-sync-1.8.0.tgz", - "integrity": "sha512-WD937VPDITWsXpGpvLKHBJf7/4vAwbHPEhK1NJxsAhPq0I5ZA6dNw28qtBJLri9bwM1Zx3zZ7dsrhKHkPjDMgg==", - "requires": { - "@auth0/s3": "^1.0.0", - "bluebird": "^3.5.4", - "chalk": "^2.4.2", - "minimatch": "^3.0.4" - } - }, - "streamsink": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/streamsink/-/streamsink-1.2.0.tgz", - "integrity": "sha1-76/unx4i01ke1949yqlcP1559zw=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "xml2js": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", - "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~9.0.1" - } - }, - "xmlbuilder": { - "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", - "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" - } - } -} diff --git a/package.json b/package.json new file mode 100644 index 0000000..48088ed --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "myserverlessproject", + "version": "1.0.0", + "description": "", + "main": "webpack.config.js", + "dependencies": { + "serverless": "^1.60.4", + "serverless-webpack": "^5.3.1" + }, + "devDependencies": { + "webpack": "^4.41.5" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/SamWSoftware/ServerlessYoutubeSeries.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/SamWSoftware/ServerlessYoutubeSeries/issues" + }, + "homepage": "https://github.com/SamWSoftware/ServerlessYoutubeSeries#readme" +} diff --git a/serverless.yml b/serverless.yml index e3f2187..07a4b7d 100644 --- a/serverless.yml +++ b/serverless.yml @@ -1,25 +1,53 @@ -service: myserverlessproject +service: myserverlessproject2 provider: - name: aws - runtime: nodejs10.x - profile: serverlessUser - -plugins: - - serverless-s3-sync + name: aws + runtime: nodejs12.x + profile: serverlessUser + region: eu-west-1 + environment: + tableName: ${self:custom.tableName} + iamRoleStatements: + - Effect: Allow + Action: + - dynamodb:* + Resource: '*' custom: - s3Sync: - - bucketName: myserverlessprojectuploadbucket-123123 - localDir: UploadData + tableName: WebsocketUsers functions: - hello: - handler: handler.hello + websocket-connect: + handler: lambdas/websockets/connect.handler + events: + - websocket: + route: $connect + websocket-disconnect: + handler: lambdas/websockets/disconnect.handler + events: + - websocket: + route: $disconnect + websocket-default: + handler: lambdas/websockets/default.handler + events: + - websocket: + route: $default + websocket-message: + handler: lambdas/websockets/message.handler + events: + - websocket: + route: message resources: - Resources: - DemoBucketUpload: - Type: AWS::S3::Bucket - Properties: - BucketName: myserverlessprojectuploadbucket-123123 + Resources: + WeboscketUserTable: + Type: AWS::DynamoDB::Table + Properties: + TableName: ${self:custom.tableName} + AttributeDefinitions: + - AttributeName: ID + AttributeType: S + KeySchema: + - AttributeName: ID + KeyType: HASH + BillingMode: PAY_PER_REQUEST