From 80467d42ad01cd91ee150953ec8931453317ffdb Mon Sep 17 00:00:00 2001 From: Suresh P Date: Sun, 8 Sep 2019 11:16:17 +0530 Subject: [PATCH 1/6] server auth and license --- server/.env | 9 +++ server/app.js | 8 +++ server/package.json | 7 +- server/src/app/constants/constants.js | 4 ++ server/src/app/createUser.js | 18 ++++++ .../invalid-email-error.exception.js | 8 +++ .../invalid-license-error.exception.js | 8 +++ .../exception/user-exists-error.exception.js | 8 +++ server/src/app/model/license-keys.js | 0 server/src/app/model/user.model.js | 22 +++++++ server/src/app/router/subscription.router.js | 32 ++++++++++ server/src/app/service/email.service.js | 28 ++++++++ server/src/app/service/user.service.js | 64 +++++++++++++++++++ server/src/app/utils/email.utils.js | 8 +++ server/src/app/utils/subscription.utils.js | 39 +++++++++++ server/src/db/db.js | 15 +++++ server/src/http/http.js | 0 17 files changed, 277 insertions(+), 1 deletion(-) create mode 100644 server/.env create mode 100644 server/app.js create mode 100644 server/src/app/constants/constants.js create mode 100644 server/src/app/createUser.js create mode 100644 server/src/app/exception/invalid-email-error.exception.js create mode 100644 server/src/app/exception/invalid-license-error.exception.js create mode 100644 server/src/app/exception/user-exists-error.exception.js create mode 100644 server/src/app/model/license-keys.js create mode 100644 server/src/app/model/user.model.js create mode 100644 server/src/app/router/subscription.router.js create mode 100644 server/src/app/service/email.service.js create mode 100644 server/src/app/service/user.service.js create mode 100644 server/src/app/utils/email.utils.js create mode 100644 server/src/app/utils/subscription.utils.js create mode 100644 server/src/db/db.js create mode 100644 server/src/http/http.js diff --git a/server/.env b/server/.env new file mode 100644 index 00000000..3c86f2e0 --- /dev/null +++ b/server/.env @@ -0,0 +1,9 @@ +SERVER_PORT= + +MONGO_USERNAME = +MONGO_PASSWORD = +MONGO_HOSTNAME = +MONGO_PORT = +MONGO_DB = + +SENDGRID_API_KEY= \ No newline at end of file diff --git a/server/app.js b/server/app.js new file mode 100644 index 00000000..1758828c --- /dev/null +++ b/server/app.js @@ -0,0 +1,8 @@ +var express=require('express') +var db=require('./src/db/db') +var subscriptionRouter=require('./src/app/router/subscription.router') +const app=express() + +app.use('/subscription', subscriptionRouter) + +const server=app.listen(process.env.SERVER_PORT,function() {}); diff --git a/server/package.json b/server/package.json index 1f91c76b..3442c77c 100644 --- a/server/package.json +++ b/server/package.json @@ -10,7 +10,12 @@ "author": "", "license": "ISC", "dependencies": { - "serverless": "^1.51.0" + "@sendgrid/mail": "^6.4.0", + "dotenv": "^8.1.0", + "express": "^4.17.1", + "mongoose": "^5.6.13", + "serverless": "^1.51.0", + "serverless-http": "^2.3.0" }, "devDependencies": { "@babel/core": "^7.6.0", diff --git a/server/src/app/constants/constants.js b/server/src/app/constants/constants.js new file mode 100644 index 00000000..c9495d6a --- /dev/null +++ b/server/src/app/constants/constants.js @@ -0,0 +1,4 @@ +const TRIAL_PLAN='TRIAL' +module.exports={ + TRIAL_PLAN: TRIAL_PLAN +} \ No newline at end of file diff --git a/server/src/app/createUser.js b/server/src/app/createUser.js new file mode 100644 index 00000000..8e8fbf3a --- /dev/null +++ b/server/src/app/createUser.js @@ -0,0 +1,18 @@ +var http = require('http'); +var url= require('url'); + + +http.createServer(function (req, res) { + var queryParams=url.parse(req.url,true).query; + res.write('Hello '+queryParams.email); //write a response to the client + res.end(); //end the response +}).listen(8080); + +function createLicenseKey(userEmail){ + + +} + +checkUserExists(String email){ + +} \ No newline at end of file diff --git a/server/src/app/exception/invalid-email-error.exception.js b/server/src/app/exception/invalid-email-error.exception.js new file mode 100644 index 00000000..a962ddd7 --- /dev/null +++ b/server/src/app/exception/invalid-email-error.exception.js @@ -0,0 +1,8 @@ +class InvalidEmailError extends Error{ + constructor(message){ + super(message) + this.name=this.constructor.name + } +} + +module.exports=InvalidEmailError \ No newline at end of file diff --git a/server/src/app/exception/invalid-license-error.exception.js b/server/src/app/exception/invalid-license-error.exception.js new file mode 100644 index 00000000..4b2439ea --- /dev/null +++ b/server/src/app/exception/invalid-license-error.exception.js @@ -0,0 +1,8 @@ +class InvalidLicenseError extends Error{ + constructor(message){ + super(message) + this.name=this.constructor.name + } +} + +module.exports=InvalidLicenseError \ No newline at end of file diff --git a/server/src/app/exception/user-exists-error.exception.js b/server/src/app/exception/user-exists-error.exception.js new file mode 100644 index 00000000..a0cfebff --- /dev/null +++ b/server/src/app/exception/user-exists-error.exception.js @@ -0,0 +1,8 @@ +class UserExistsError extends Error{ + constructor(message){ + super(message) + this.name=this.constructor.name + } +} + +module.exports=UserExistsError \ No newline at end of file diff --git a/server/src/app/model/license-keys.js b/server/src/app/model/license-keys.js new file mode 100644 index 00000000..e69de29b diff --git a/server/src/app/model/user.model.js b/server/src/app/model/user.model.js new file mode 100644 index 00000000..54bbda62 --- /dev/null +++ b/server/src/app/model/user.model.js @@ -0,0 +1,22 @@ +const mongoose = require('mongoose') +let Schema=mongoose.Schema + +const subscriptionSchema=new Schema({ + type: {type: String}, + valid_till: {type: Date}, + start_date:{type: Date} +},{_id: false}) + +const userSchema=new Schema({ + name: {type: String}, + email: {type: String}, + license_key:{type: String}, + subscription: subscriptionSchema, + c_ts: Date, + u_ts: Date +}) + +let USER_MODEL_NAME='User' +let COLLECTION_NAME='users' +let User=mongoose.model(USER_MODEL_NAME,userSchema,COLLECTION_NAME) +module.exports=User \ No newline at end of file diff --git a/server/src/app/router/subscription.router.js b/server/src/app/router/subscription.router.js new file mode 100644 index 00000000..250e89d4 --- /dev/null +++ b/server/src/app/router/subscription.router.js @@ -0,0 +1,32 @@ +const express = require('express'); +const userService = require('../service/user.service') +const UserExistsError=require('../exception/user-exists-error.exception') +const InvalidEmailError=require('../exception/invalid-email-error.exception') + +const router = express.Router(); +router.get('/activate-trial', async function (req, res) { + const email=req.query.email + let responseBody={} + try{ + await userService.createUserAndEnableTrial(email) + console.log('bbb') + responseBody.status=true + responseBody.message='success' + }catch(err){ + console.log(err) + responseBody.status=false + if(err instanceof UserExistsError){ + responseBody.errorType=409 + responseBody.message='user already activated' + }else if(err instanceof InvalidEmailError){ + responseBody.errorType=400 + responseBody.message='invalid email' + }else{ + responseBody.errorType=500 + responseBody.message='Internal Server Error' + } + } + res.send(responseBody) +}) + +module.exports = router; \ No newline at end of file diff --git a/server/src/app/service/email.service.js b/server/src/app/service/email.service.js new file mode 100644 index 00000000..c7981f52 --- /dev/null +++ b/server/src/app/service/email.service.js @@ -0,0 +1,28 @@ +const sgMail = require('@sendgrid/mail'); +const emailUtils=require('../utils/email.utils') +sgMail.setApiKey(process.env.SENDGRID_API_KEY); + +const SYSTEM_MAIL='noreply@responsively.app' +function sendLicenseKeyMail(email,licenseKey){ + console.log('sending license key to email:'+email) + const msg = { + to: email, + from: SYSTEM_MAIL, + subject: 'Responsively - License Key', + html: 'Please find your license key - '+licenseKey+'', + }; + sgMail.send(msg); +} + +function validateEmailExistence(email){ + + return emailUtils.validateEmail(email) + /** + * to do check email existence, mx lookup. figure out 10min email workarounds + */ +} + +module.exports={ + sendLicenseKeyMail, + validateEmailExistence +} \ No newline at end of file diff --git a/server/src/app/service/user.service.js b/server/src/app/service/user.service.js new file mode 100644 index 00000000..2a4fed57 --- /dev/null +++ b/server/src/app/service/user.service.js @@ -0,0 +1,64 @@ +const User=require('../model/user.model') +const db=require('../../db/db') +const subscriptionUtils=require('../utils/subscription.utils') +const constants=require('../constants/constants') +const crypto=require('crypto') +const emailService=require('../service/email.service') +const InvalidEmailError=require('../exception/invalid-email-error.exception') +const UserExistsError=require('../exception/user-exists-error.exception') +cosnt InvalidLicenseError=require('../exception/invalid-license-error.exception') +async function createUserAndEnableTrial(email){ + + if(!emailService.validateEmailExistence(email)){ + throw new InvalidEmailError('Invalid Email:'+email) + } + let newUser=new User({ + email: email, + license_key:getLicenseKey(), + subscription:subscriptionUtils.getSubscriptionObject(constants.TRIAL_PLAN,new Date()), + }) + + await insertUser(newUser) + emailService.sendLicenseKeyMail(email,newUser.license_key) +} + +async function insertUser(newUser){ + newUser.c_ts=new Date() + newUser.u_ts=new Date() + try{ + await newUser.save() + }catch(err){ + console.log(err) + if(err.name === 'MongoError' && err.code === 11000){ + throw new UserExistsError('user already exists with email:'+newUser.email) + } + throw err + } + return newUser +} + +async function verifyLicenseKey(licenseKey){ + + if(!licenseKey){ + throw new InvalidLicenseError('Invalid License :'+licenseKey) + } + let user=await User.findOne({license_key: licenseKey}).exec() + if(!user){ + throw new InvalidLicenseError('Invalid License:'+licenseKey) + } + + let licenseValid=user.subscription.license_key.getTime()-new Date().getTime() + if(licenseValid<0){ + return false + } + return true +} + +function getLicenseKey(email){ + const key=crypto.randomBytes(20).toString('hex'); + return key; +} + +module.exports={ + createUserAndEnableTrial +} \ No newline at end of file diff --git a/server/src/app/utils/email.utils.js b/server/src/app/utils/email.utils.js new file mode 100644 index 00000000..b2d723d5 --- /dev/null +++ b/server/src/app/utils/email.utils.js @@ -0,0 +1,8 @@ +function validateEmail(email) { + var emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return emailRegex.test(String(email).toLowerCase()); +} + +module.exports={ + validateEmail +} \ No newline at end of file diff --git a/server/src/app/utils/subscription.utils.js b/server/src/app/utils/subscription.utils.js new file mode 100644 index 00000000..174572b3 --- /dev/null +++ b/server/src/app/utils/subscription.utils.js @@ -0,0 +1,39 @@ +const constants=require('../constants/constants') +TRIAL_PERIOD=14 +ONE_DAY=86400000 + + +function getValidTill(type,startDate){ + validate(type,startDate) + if(type==constants.TRIAL_PLAN){ + return computeTrialValidTill(startDate) + } +} + +function computeTrialValidTill(startDate){ + return new Date(startDate.getTime()+TRIAL_PERIOD*ONE_DAY) +} + +function getSubscriptionObject(type,startDate){ + validate(type, startDate) + let subscriptionType=type + let subscriptionStateDate=startDate; + let subscriptionValidTill=getValidTill(subscriptionType,subscriptionStateDate) + let subscriptionObject={ + type: subscriptionType, + start_date: subscriptionStateDate, + valid_till: subscriptionValidTill + } + return subscriptionObject; +} + +function validate(type, startDate){ + if(!type || !startDate){ + throw new Error('Invalid Parameter type:'+type+', startDate:'+startDate) + } +} + +module.exports={ + getSubscriptionObject +} +// to do for other plans \ No newline at end of file diff --git a/server/src/db/db.js b/server/src/db/db.js new file mode 100644 index 00000000..f2c3deb1 --- /dev/null +++ b/server/src/db/db.js @@ -0,0 +1,15 @@ +const mongoose = require('mongoose'); + +const MONGO_USERNAME = process.env.MONGO_USERNAME +const MONGO_PASSWORD = process.env.MONGO_PASSWORD +const MONGO_HOSTNAME = process.env.MONGO_HOSTNAME +const MONGO_PORT = process.env.MONGO_PORT +const MONGO_DB = process.env.MONGO_DB +const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}` + +console.log('connecting to db'); +mongoose.connect(url,{ useNewUrlParser: true }, function(error){ + if(error) console.log(error); + console.log("connection successful"); +}) +console.log('connected'); diff --git a/server/src/http/http.js b/server/src/http/http.js new file mode 100644 index 00000000..e69de29b From 4e0af59f65101c1b216b0766be6bccd3613d6100 Mon Sep 17 00:00:00 2001 From: Suresh P Date: Sun, 8 Sep 2019 12:47:50 +0530 Subject: [PATCH 2/6] license cerification --- server/.env | 14 ++++++------ server/src/app/router/subscription.router.js | 23 +++++++++++++++++++- server/src/app/service/user.service.js | 9 ++++---- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/server/.env b/server/.env index 3c86f2e0..0b547a62 100644 --- a/server/.env +++ b/server/.env @@ -1,9 +1,9 @@ -SERVER_PORT= +SERVER_PORT=8080 -MONGO_USERNAME = -MONGO_PASSWORD = -MONGO_HOSTNAME = -MONGO_PORT = -MONGO_DB = +MONGO_USERNAME = app +MONGO_PASSWORD = app +MONGO_HOSTNAME = 127.0.0.1 +MONGO_PORT = 27017 +MONGO_DB = responsively -SENDGRID_API_KEY= \ No newline at end of file +SENDGRID_API_KEY=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 \ No newline at end of file diff --git a/server/src/app/router/subscription.router.js b/server/src/app/router/subscription.router.js index 250e89d4..05ac8979 100644 --- a/server/src/app/router/subscription.router.js +++ b/server/src/app/router/subscription.router.js @@ -2,6 +2,7 @@ const express = require('express'); const userService = require('../service/user.service') const UserExistsError=require('../exception/user-exists-error.exception') const InvalidEmailError=require('../exception/invalid-email-error.exception') +const InvalidLicenseError=require('../exception/invalid-license-error.exception') const router = express.Router(); router.get('/activate-trial', async function (req, res) { @@ -9,7 +10,6 @@ router.get('/activate-trial', async function (req, res) { let responseBody={} try{ await userService.createUserAndEnableTrial(email) - console.log('bbb') responseBody.status=true responseBody.message='success' }catch(err){ @@ -29,4 +29,25 @@ router.get('/activate-trial', async function (req, res) { res.send(responseBody) }) +router.get('/verify-license', async function (req, res) { + const licenseKey=req.query.licenseKey + let responseBody={} + try{ + await userService.verifyLicenseKey(licenseKey) + responseBody.status=true + responseBody.message='success' + }catch(err){ + console.log(err) + responseBody.status=false + if(err instanceof InvalidLicenseError){ + responseBody.errorType=403 + responseBody.message='Invalid License' + }else{ + responseBody.errorType=500 + responseBody.message='Internal Server Error' + } + } + res.send(responseBody) +}) + module.exports = router; \ No newline at end of file diff --git a/server/src/app/service/user.service.js b/server/src/app/service/user.service.js index 2a4fed57..138cd519 100644 --- a/server/src/app/service/user.service.js +++ b/server/src/app/service/user.service.js @@ -6,7 +6,7 @@ const crypto=require('crypto') const emailService=require('../service/email.service') const InvalidEmailError=require('../exception/invalid-email-error.exception') const UserExistsError=require('../exception/user-exists-error.exception') -cosnt InvalidLicenseError=require('../exception/invalid-license-error.exception') +const InvalidLicenseError=require('../exception/invalid-license-error.exception') async function createUserAndEnableTrial(email){ if(!emailService.validateEmailExistence(email)){ @@ -46,8 +46,8 @@ async function verifyLicenseKey(licenseKey){ if(!user){ throw new InvalidLicenseError('Invalid License:'+licenseKey) } - - let licenseValid=user.subscription.license_key.getTime()-new Date().getTime() + console.log(user) + let licenseValid=user.subscription.valid_till.getTime()-new Date().getTime() if(licenseValid<0){ return false } @@ -60,5 +60,6 @@ function getLicenseKey(email){ } module.exports={ - createUserAndEnableTrial + createUserAndEnableTrial, + verifyLicenseKey } \ No newline at end of file From bc9e1878d68db87146e132651b34226655e21026 Mon Sep 17 00:00:00 2001 From: Suresh P Date: Sun, 8 Sep 2019 14:09:25 +0530 Subject: [PATCH 3/6] remove properties --- server/.env | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/.env b/server/.env index 0b547a62..3c86f2e0 100644 --- a/server/.env +++ b/server/.env @@ -1,9 +1,9 @@ -SERVER_PORT=8080 +SERVER_PORT= -MONGO_USERNAME = app -MONGO_PASSWORD = app -MONGO_HOSTNAME = 127.0.0.1 -MONGO_PORT = 27017 -MONGO_DB = responsively +MONGO_USERNAME = +MONGO_PASSWORD = +MONGO_HOSTNAME = +MONGO_PORT = +MONGO_DB = -SENDGRID_API_KEY=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 \ No newline at end of file +SENDGRID_API_KEY= \ No newline at end of file From 0d6e21c417772c372677d763adb1c4f04b7c526d Mon Sep 17 00:00:00 2001 From: Suresh Date: Mon, 9 Sep 2019 22:40:04 +0530 Subject: [PATCH 4/6] lambda updates --- server/.env | 9 --- server/app.js | 8 --- server/environment.yml | 6 ++ server/package.json | 4 +- server/serverless.yaml | 29 +++++++- server/sls-webpack.config.js | 2 +- server/src/app/createUser.js | 18 ----- server/src/app/model/license-keys.js | 0 server/src/app/router/subscription.router.js | 53 -------------- server/src/app/service/email.service.js | 4 +- server/src/app/service/user.service.js | 34 +++++---- server/src/db/db.js | 9 +-- server/src/http/http.js | 0 server/src/http/lambda.js | 76 ++++++++++++++++++++ server/src/websocket/lambda.js | 41 +++++++++++ 15 files changed, 176 insertions(+), 117 deletions(-) delete mode 100644 server/.env delete mode 100644 server/app.js create mode 100644 server/environment.yml delete mode 100644 server/src/app/createUser.js delete mode 100644 server/src/app/model/license-keys.js delete mode 100644 server/src/app/router/subscription.router.js delete mode 100644 server/src/http/http.js create mode 100644 server/src/http/lambda.js diff --git a/server/.env b/server/.env deleted file mode 100644 index 3c86f2e0..00000000 --- a/server/.env +++ /dev/null @@ -1,9 +0,0 @@ -SERVER_PORT= - -MONGO_USERNAME = -MONGO_PASSWORD = -MONGO_HOSTNAME = -MONGO_PORT = -MONGO_DB = - -SENDGRID_API_KEY= \ No newline at end of file diff --git a/server/app.js b/server/app.js deleted file mode 100644 index 1758828c..00000000 --- a/server/app.js +++ /dev/null @@ -1,8 +0,0 @@ -var express=require('express') -var db=require('./src/db/db') -var subscriptionRouter=require('./src/app/router/subscription.router') -const app=express() - -app.use('/subscription', subscriptionRouter) - -const server=app.listen(process.env.SERVER_PORT,function() {}); diff --git a/server/environment.yml b/server/environment.yml new file mode 100644 index 00000000..b91cf0ab --- /dev/null +++ b/server/environment.yml @@ -0,0 +1,6 @@ +MONGO_USERNAME: +MONGO_PASSWORD: +MONGO_HOSTNAME: +MONGO_PORT: +MONGO_DB: +SENDGRID_API_KEY: \ No newline at end of file diff --git a/server/package.json b/server/package.json index 3442c77c..6d6677b1 100644 --- a/server/package.json +++ b/server/package.json @@ -11,8 +11,6 @@ "license": "ISC", "dependencies": { "@sendgrid/mail": "^6.4.0", - "dotenv": "^8.1.0", - "express": "^4.17.1", "mongoose": "^5.6.13", "serverless": "^1.51.0", "serverless-http": "^2.3.0" @@ -24,4 +22,4 @@ "serverless-webpack": "^5.3.1", "webpack": "^4.39.3" } -} +} \ No newline at end of file diff --git a/server/serverless.yaml b/server/serverless.yaml index fa17c5b4..8c706aca 100644 --- a/server/serverless.yaml +++ b/server/serverless.yaml @@ -1,5 +1,3 @@ -org: manojvivek -app: responsively service: responsively-server custom: @@ -10,11 +8,28 @@ provider: name: aws runtime: nodejs10.x websocketsApiName: responsively-websocket + apiName: dev-responsively-server websocketsApiRouteSelectionExpression: $request.body.action # custom routes are selected by the value of the action property in the body + environment: ${file(environment.yml)} functions: + activateTrial: + handler: src/http/lambda.activateTrial + timeout: 15 + events: + - http: + path: activate-trial + method: get + validateLicenseHttp: + handler: src/http/lambda.validateLicense + timeout: 15 + events: + - http: + path: validate-license + method: get websocket-connection-manager: handler: src/websocket/lambda.connectionHandler + timeout: 15 events: - websocket: route: $connect @@ -22,13 +37,21 @@ functions: route: $disconnect defaultHandler: handler: src/websocket/lambda.defaultHandler + timeout: 15 events: - websocket: $default #simple event definition without extra route property pingHandler: handler: src/websocket/lambda.pingHandler + timeout: 15 events: - websocket: route: ping # will trigger if $request.body.action === "ping" + validateLicense: + handler: src/websocket/lambda.validateLicense + timeout: 15 + events: + - websocket: + route: validate # will trigger if $request.body.action === "validate" plugins: - - serverless-webpack + - serverless-webpack \ No newline at end of file diff --git a/server/sls-webpack.config.js b/server/sls-webpack.config.js index b5bd6a5d..f7707c29 100644 --- a/server/sls-webpack.config.js +++ b/server/sls-webpack.config.js @@ -3,7 +3,7 @@ const webpack = require('webpack'); process.env.NODE_ENV = slsw.lib.options.stage || 'development'; const mode = - process.env.NODE_ENV === 'development' ? 'development' : 'production'; +process.env.NODE_ENV === 'development' ? 'development' : 'production'; module.exports = { entry: slsw.lib.entries, diff --git a/server/src/app/createUser.js b/server/src/app/createUser.js deleted file mode 100644 index 8e8fbf3a..00000000 --- a/server/src/app/createUser.js +++ /dev/null @@ -1,18 +0,0 @@ -var http = require('http'); -var url= require('url'); - - -http.createServer(function (req, res) { - var queryParams=url.parse(req.url,true).query; - res.write('Hello '+queryParams.email); //write a response to the client - res.end(); //end the response -}).listen(8080); - -function createLicenseKey(userEmail){ - - -} - -checkUserExists(String email){ - -} \ No newline at end of file diff --git a/server/src/app/model/license-keys.js b/server/src/app/model/license-keys.js deleted file mode 100644 index e69de29b..00000000 diff --git a/server/src/app/router/subscription.router.js b/server/src/app/router/subscription.router.js deleted file mode 100644 index 05ac8979..00000000 --- a/server/src/app/router/subscription.router.js +++ /dev/null @@ -1,53 +0,0 @@ -const express = require('express'); -const userService = require('../service/user.service') -const UserExistsError=require('../exception/user-exists-error.exception') -const InvalidEmailError=require('../exception/invalid-email-error.exception') -const InvalidLicenseError=require('../exception/invalid-license-error.exception') - -const router = express.Router(); -router.get('/activate-trial', async function (req, res) { - const email=req.query.email - let responseBody={} - try{ - await userService.createUserAndEnableTrial(email) - responseBody.status=true - responseBody.message='success' - }catch(err){ - console.log(err) - responseBody.status=false - if(err instanceof UserExistsError){ - responseBody.errorType=409 - responseBody.message='user already activated' - }else if(err instanceof InvalidEmailError){ - responseBody.errorType=400 - responseBody.message='invalid email' - }else{ - responseBody.errorType=500 - responseBody.message='Internal Server Error' - } - } - res.send(responseBody) -}) - -router.get('/verify-license', async function (req, res) { - const licenseKey=req.query.licenseKey - let responseBody={} - try{ - await userService.verifyLicenseKey(licenseKey) - responseBody.status=true - responseBody.message='success' - }catch(err){ - console.log(err) - responseBody.status=false - if(err instanceof InvalidLicenseError){ - responseBody.errorType=403 - responseBody.message='Invalid License' - }else{ - responseBody.errorType=500 - responseBody.message='Internal Server Error' - } - } - res.send(responseBody) -}) - -module.exports = router; \ No newline at end of file diff --git a/server/src/app/service/email.service.js b/server/src/app/service/email.service.js index c7981f52..fc10bb4b 100644 --- a/server/src/app/service/email.service.js +++ b/server/src/app/service/email.service.js @@ -3,7 +3,7 @@ const emailUtils=require('../utils/email.utils') sgMail.setApiKey(process.env.SENDGRID_API_KEY); const SYSTEM_MAIL='noreply@responsively.app' -function sendLicenseKeyMail(email,licenseKey){ +async function sendLicenseKeyMail(email,licenseKey){ console.log('sending license key to email:'+email) const msg = { to: email, @@ -11,7 +11,7 @@ function sendLicenseKeyMail(email,licenseKey){ subject: 'Responsively - License Key', html: 'Please find your license key - '+licenseKey+'', }; - sgMail.send(msg); + await sgMail.send(msg); } function validateEmailExistence(email){ diff --git a/server/src/app/service/user.service.js b/server/src/app/service/user.service.js index 138cd519..710af8eb 100644 --- a/server/src/app/service/user.service.js +++ b/server/src/app/service/user.service.js @@ -1,5 +1,4 @@ const User=require('../model/user.model') -const db=require('../../db/db') const subscriptionUtils=require('../utils/subscription.utils') const constants=require('../constants/constants') const crypto=require('crypto') @@ -7,6 +6,7 @@ const emailService=require('../service/email.service') const InvalidEmailError=require('../exception/invalid-email-error.exception') const UserExistsError=require('../exception/user-exists-error.exception') const InvalidLicenseError=require('../exception/invalid-license-error.exception') + async function createUserAndEnableTrial(email){ if(!emailService.validateEmailExistence(email)){ @@ -19,7 +19,7 @@ async function createUserAndEnableTrial(email){ }) await insertUser(newUser) - emailService.sendLicenseKeyMail(email,newUser.license_key) + await emailService.sendLicenseKeyMail(email,newUser.license_key) } async function insertUser(newUser){ @@ -34,25 +34,31 @@ async function insertUser(newUser){ } throw err } + console.log('userInserted') return newUser } -async function verifyLicenseKey(licenseKey){ +async function validateLicenseKey(licenseKey){ if(!licenseKey){ throw new InvalidLicenseError('Invalid License :'+licenseKey) } - let user=await User.findOne({license_key: licenseKey}).exec() - if(!user){ - throw new InvalidLicenseError('Invalid License:'+licenseKey) + try{ + let user=await User.findOne({license_key: licenseKey}).exec() + if(!user){ + throw new InvalidLicenseError('Invalid License:'+licenseKey) + } + let licenseValid=user.subscription.valid_till.getTime()-new Date().getTime() + if(licenseValid<0){ + console.log('license expired!'); + return false + } + console.log('license active!'); + return true + }catch(err){ + throw err } - console.log(user) - let licenseValid=user.subscription.valid_till.getTime()-new Date().getTime() - if(licenseValid<0){ - return false - } - return true -} +} function getLicenseKey(email){ const key=crypto.randomBytes(20).toString('hex'); @@ -61,5 +67,5 @@ function getLicenseKey(email){ module.exports={ createUserAndEnableTrial, - verifyLicenseKey + validateLicenseKey } \ No newline at end of file diff --git a/server/src/db/db.js b/server/src/db/db.js index f2c3deb1..36357f6d 100644 --- a/server/src/db/db.js +++ b/server/src/db/db.js @@ -1,15 +1,12 @@ const mongoose = require('mongoose'); - const MONGO_USERNAME = process.env.MONGO_USERNAME const MONGO_PASSWORD = process.env.MONGO_PASSWORD const MONGO_HOSTNAME = process.env.MONGO_HOSTNAME const MONGO_PORT = process.env.MONGO_PORT const MONGO_DB = process.env.MONGO_DB -const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}` - +const url = `mongodb+srv://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}/${MONGO_DB}` console.log('connecting to db'); mongoose.connect(url,{ useNewUrlParser: true }, function(error){ - if(error) console.log(error); - console.log("connection successful"); + if(error){console.log(error)} + else{console.log("connection successful")} }) -console.log('connected'); diff --git a/server/src/http/http.js b/server/src/http/http.js deleted file mode 100644 index e69de29b..00000000 diff --git a/server/src/http/lambda.js b/server/src/http/lambda.js new file mode 100644 index 00000000..67024fee --- /dev/null +++ b/server/src/http/lambda.js @@ -0,0 +1,76 @@ +const db=require('../db/db') +const userService = require('../app/service/user.service') +const UserExistsError=require('../app/exception/user-exists-error.exception') +const InvalidLicenseError=require('../app/exception/invalid-license-error.exception') +const InvalidEmailError=require('../app/exception/invalid-email-error.exception') + +export async function activateTrial(event, context, callback) { + let responseBody={} + let statusCode=0 + context.callbackWaitsForEmptyEventLoop = false; + try{ + const email=event["queryStringParameters"]['email'] + if(!email){ + throw new InvalidEmailError('email is empty') + } + await userService.createUserAndEnableTrial(email) + responseBody.status=true + responseBody.statusCode=200; + responseBody.message='success' + statusCode=200 + }catch(err){ + console.log(err) + responseBody.status=false + if(err instanceof UserExistsError){ + responseBody.statusCode=409 + responseBody.message='user already activated' + }else if(err instanceof InvalidEmailError){ + responseBody.statusCode=400 + responseBody.message='invalid email' + }else{ + responseBody.statusCode=500 + responseBody.message='Internal Server Error' + } + } + let response= { + statusCode: 200, + body: JSON.stringify(responseBody) + } + callback(null, response) +} + +export async function validateLicense(event, context, callback) { + let responseBody={} + let statusCode=0 + context.callbackWaitsForEmptyEventLoop = false; + console.log(event) + try{ + const licenseKey=event["queryStringParameters"]['licenseKey'] + if(!licenseKey){ + throw new InvalidLicenseError('licenseKey is empty') + } + responseBody.status=await userService.validateLicenseKey(licenseKey) + responseBody.statusCode=200 + if(responseBody.status){ + responseBody.message='success' + }else{ + responseBody.message='license expired' + } + statusCode=200 + }catch(err){ + console.log(err) + responseBody.status=false + if(err instanceof InvalidLicenseError){ + responseBody.statusCode=403 + responseBody.message='Invalid License' + }else{ + responseBody.statusCode=500 + responseBody.message='Internal Server Error' + } + } + let response= { + statusCode: 200, + body: JSON.stringify(responseBody) + } + callback(null, response) + } \ No newline at end of file diff --git a/server/src/websocket/lambda.js b/server/src/websocket/lambda.js index 8299fd94..8ffd007d 100644 --- a/server/src/websocket/lambda.js +++ b/server/src/websocket/lambda.js @@ -1,3 +1,7 @@ +var db=require('../db/db') +const userService = require('../app/service/user.service') +const InvalidLicenseError=require('../app/exception/invalid-license-error.exception') + export async function connectionHandler(event, context, callback) { return { statusCode: 200, @@ -18,3 +22,40 @@ export async function pingHandler(event, context, callback) { body: 'Message recieved', }; } + +export async function validateLicense(event, context, callback) { + let responseBody={} + let statusCode=0 + context.callbackWaitsForEmptyEventLoop = false; + console.log(event) + try{ + const body=JSON.parse(event.body) + if(!body['data'] || !body['data']['licenseKey']){ + throw new InvalidLicenseError('licenseKey is empty') + } + const licenseKey=body['data']['licenseKey'] + responseBody.status=await userService.validateLicenseKey(licenseKey) + responseBody.statusCode=200 + if(responseBody.status){ + responseBody.message='success' + }else{ + responseBody.message='license expired' + } + statusCode=200 + }catch(err){ + console.log(err) + responseBody.status=false + if(err instanceof InvalidLicenseError){ + responseBody.statusCode=403 + responseBody.message='Invalid License' + }else{ + responseBody.statusCode=500 + responseBody.message='Internal Server Error' + } + } + let response= { + statusCode: 200, + body: JSON.stringify(responseBody) + } + callback(null, response) +} \ No newline at end of file From 5c0b1c7d70f2b030dd058fa7bb2029a4e73caddd Mon Sep 17 00:00:00 2001 From: Suresh Date: Fri, 13 Sep 2019 20:48:17 +0530 Subject: [PATCH 5/6] update --- server/src/http/lambda.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/server/src/http/lambda.js b/server/src/http/lambda.js index 67024fee..ae6afd8f 100644 --- a/server/src/http/lambda.js +++ b/server/src/http/lambda.js @@ -9,7 +9,7 @@ export async function activateTrial(event, context, callback) { let statusCode=0 context.callbackWaitsForEmptyEventLoop = false; try{ - const email=event["queryStringParameters"]['email'] + const email=event['queryStringParameters']['email'] if(!email){ throw new InvalidEmailError('email is empty') } @@ -40,12 +40,11 @@ export async function activateTrial(event, context, callback) { } export async function validateLicense(event, context, callback) { - let responseBody={} - let statusCode=0 - context.callbackWaitsForEmptyEventLoop = false; - console.log(event) - try{ - const licenseKey=event["queryStringParameters"]['licenseKey'] + let responseBody={} + let statusCode=0 + context.callbackWaitsForEmptyEventLoop = false; + try{ + const licenseKey=event['queryStringParameters']['licenseKey'] if(!licenseKey){ throw new InvalidLicenseError('licenseKey is empty') } From 64e02b2576a6bcc7c07415b19546c24bbbbc7b44 Mon Sep 17 00:00:00 2001 From: Suresh Date: Sun, 15 Sep 2019 11:03:53 +0530 Subject: [PATCH 6/6] websockets updated --- server/package.json | 3 +- server/serverless.yaml | 2 +- server/src/app/constants/constants.js | 22 +++- .../invalid-subscription.exception.js | 8 ++ .../src/app/model/active-connections.model.js | 19 +++ server/src/app/model/plan.model.js | 16 +++ server/src/app/model/subscription.model.js | 16 +++ server/src/app/model/user.model.js | 13 +- .../app/service/active-connections.service.js | 73 +++++++++++ server/src/app/service/email.service.js | 3 + server/src/app/service/plan.service.js | 51 ++++++++ .../src/app/service/subscription.service.js | 114 ++++++++++++++++++ server/src/app/service/user.service.js | 46 +++---- server/src/app/utils/subscription.utils.js | 39 ------ server/src/http/lambda.js | 44 ++----- server/src/websocket/lambda.js | 67 +++++++--- 16 files changed, 410 insertions(+), 126 deletions(-) create mode 100644 server/src/app/exception/invalid-subscription.exception.js create mode 100644 server/src/app/model/active-connections.model.js create mode 100644 server/src/app/model/plan.model.js create mode 100644 server/src/app/model/subscription.model.js create mode 100644 server/src/app/service/active-connections.service.js create mode 100644 server/src/app/service/plan.service.js create mode 100644 server/src/app/service/subscription.service.js delete mode 100644 server/src/app/utils/subscription.utils.js diff --git a/server/package.json b/server/package.json index 6d6677b1..afbcadd3 100644 --- a/server/package.json +++ b/server/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "@sendgrid/mail": "^6.4.0", + "aws-sdk": "^2.528.0", "mongoose": "^5.6.13", "serverless": "^1.51.0", "serverless-http": "^2.3.0" @@ -22,4 +23,4 @@ "serverless-webpack": "^5.3.1", "webpack": "^4.39.3" } -} \ No newline at end of file +} diff --git a/server/serverless.yaml b/server/serverless.yaml index 8c706aca..0d2c3b6b 100644 --- a/server/serverless.yaml +++ b/server/serverless.yaml @@ -8,7 +8,7 @@ provider: name: aws runtime: nodejs10.x websocketsApiName: responsively-websocket - apiName: dev-responsively-server + apiName: responsively-server websocketsApiRouteSelectionExpression: $request.body.action # custom routes are selected by the value of the action property in the body environment: ${file(environment.yml)} diff --git a/server/src/app/constants/constants.js b/server/src/app/constants/constants.js index c9495d6a..89168bf1 100644 --- a/server/src/app/constants/constants.js +++ b/server/src/app/constants/constants.js @@ -1,4 +1,24 @@ const TRIAL_PLAN='TRIAL' + +const SUBSCRIPTION_STATUS=Object.freeze({ + ACTIVE:{ + id: 'ACTIVE' + }, + EXPIRED:{ + id:'EXPIRED' + } +}) + +const SUBSCRIPTION_DURATION=Object.freeze({ + MONTH:{ + id: 'ACTIVE' + }, + YEAR:{ + id:'EXPIRED' + } +}) + module.exports={ - TRIAL_PLAN: TRIAL_PLAN + TRIAL_PLAN: TRIAL_PLAN, + SUBSCRIPTION_STATUS: SUBSCRIPTION_STATUS } \ No newline at end of file diff --git a/server/src/app/exception/invalid-subscription.exception.js b/server/src/app/exception/invalid-subscription.exception.js new file mode 100644 index 00000000..8a4635ea --- /dev/null +++ b/server/src/app/exception/invalid-subscription.exception.js @@ -0,0 +1,8 @@ +class InvalidSubscriptionError extends Error{ + constructor(message){ + super(message) + this.name=this.constructor.name + } +} + +module.exports=InvalidSubscriptionError \ No newline at end of file diff --git a/server/src/app/model/active-connections.model.js b/server/src/app/model/active-connections.model.js new file mode 100644 index 00000000..110b4d29 --- /dev/null +++ b/server/src/app/model/active-connections.model.js @@ -0,0 +1,19 @@ +const mongoose = require('mongoose') +let Schema=mongoose.Schema + +const connectionsSchema=new Schema({ + connection_id: {type: String}, + start_time: Date +},{_id: false}) + +const activeConnectionsSchema=new Schema({ + _id: {type: String}, + connections:[connectionsSchema], + c_ts: Date, + u_ts: Date +}) + +const ACTIVE_CONNECTIONS_MODEL_NAME='ActiveConnection' +const COLLECTION_NAME='active_connections' +const ActiveConnection=mongoose.model(ACTIVE_CONNECTIONS_MODEL_NAME,activeConnectionsSchema,COLLECTION_NAME) +module.exports=ActiveConnection \ No newline at end of file diff --git a/server/src/app/model/plan.model.js b/server/src/app/model/plan.model.js new file mode 100644 index 00000000..ceddafdf --- /dev/null +++ b/server/src/app/model/plan.model.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose') +let Schema=mongoose.Schema + +const planSchema=new Schema({ + name: {type: String}, + user_limit: {type: Number}, + duration:{type: String}, + c_ts: {type: Date}, + u_ts: {type: Date}, + price: {type: String} +}) + +const PLAN_MODEL_NAME='Plan' +const COLLECTION_NAME='plans' +const Plan=mongoose.model(PLAN_MODEL_NAME,planSchema,COLLECTION_NAME) +module.exports=Plan \ No newline at end of file diff --git a/server/src/app/model/subscription.model.js b/server/src/app/model/subscription.model.js new file mode 100644 index 00000000..cac2177e --- /dev/null +++ b/server/src/app/model/subscription.model.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose') +let Schema=mongoose.Schema + +const subscriptionSchema=new Schema({ + user_id: {type: String}, + plan_id: {type: String}, + start_date:Date, + status: {type: String}, + c_ts: Date, + u_ts: Date +}) + +const SUBSCRIPTION_MODEL_NAME='Subscription' +const COLLECTION_NAME='subscriptions' +const Subscription=mongoose.model(SUBSCRIPTION_MODEL_NAME,subscriptionSchema,COLLECTION_NAME) +module.exports=Subscription \ No newline at end of file diff --git a/server/src/app/model/user.model.js b/server/src/app/model/user.model.js index 54bbda62..fda06dd3 100644 --- a/server/src/app/model/user.model.js +++ b/server/src/app/model/user.model.js @@ -1,22 +1,15 @@ const mongoose = require('mongoose') let Schema=mongoose.Schema -const subscriptionSchema=new Schema({ - type: {type: String}, - valid_till: {type: Date}, - start_date:{type: Date} -},{_id: false}) - const userSchema=new Schema({ name: {type: String}, email: {type: String}, license_key:{type: String}, - subscription: subscriptionSchema, c_ts: Date, u_ts: Date }) -let USER_MODEL_NAME='User' -let COLLECTION_NAME='users' -let User=mongoose.model(USER_MODEL_NAME,userSchema,COLLECTION_NAME) +const USER_MODEL_NAME='User' +const COLLECTION_NAME='users' +const User=mongoose.model(USER_MODEL_NAME,userSchema,COLLECTION_NAME) module.exports=User \ No newline at end of file diff --git a/server/src/app/service/active-connections.service.js b/server/src/app/service/active-connections.service.js new file mode 100644 index 00000000..94d74625 --- /dev/null +++ b/server/src/app/service/active-connections.service.js @@ -0,0 +1,73 @@ +const ActiveConnection=require('../model/active-connections.model') +const subscriptionService=require('../service/subscription.service') +const planService=require('../service/plan.service') +const InvalidSubscriptionError=require('../exception/invalid-subscription.exception') +const InvalidLicenseError=require('../exception/invalid-license-error.exception') +const AWS=require('aws-sdk') + +async function addNewConnection(licenseKey,connectionId){ + + let responseBody={} + try{ + let subscription=await subscriptionService.getSubscriptionByLicenseKey(licenseKey) + if(!subscription){ + throw new Error('no active subscriptions found') + } + const allowedUsers=await planService.getCurrentUserLimitForPlan(subscription.start_date,subscription.plan_id) + if(allowedUsers && allowedUsers>0){ + const connectionObj={ + connection_id: connectionId, + start_time: new Date() + } + const updateObj={ + $push:{connections:connectionObj}, + $set:{u_ts:new Date()}, + $setOnInsert:{c_ts: new Date()} + } + let existingConnection=await ActiveConnection.findOneAndUpdate({_id: licenseKey},updateObj,{upsert:true}) + await removeOldConnection(allowedUsers,existingConnection) + + }else{ + throw new InvalidSubscriptionError('0 user limit found for plan') + } + }catch(err){ + console.log(err) + throw err + } +} + +async function removeOldConnection(userLimit,existingConnection){ + if(existingConnection && existingConnection.connections){ + let connections=existingConnection.connections + if(connections.length>=userLimit){ + await ActiveConnection.updateOne({_id:existingConnection._id},{$pop:{connections: -1},$set:{u_ts:new Date()}}) + } + } +} + +async function closeConnection(connectionId){ + console.log('disconnecting:'+connectionId) + await ActiveConnection.updateOne({'connections.connection_id': connectionId},{$pull:{connections:{connection_id:connectionId}}}) + console.log('disconnecting:'+connectionId+' done') +} + +async function publish(event,connectionId,data){ + console.log('publishing data to client connection:'+connectionId+',data:'+JSON.stringify(data)) + const apigwManagementApi = new AWS.ApiGatewayManagementApi({ + endpoint: event.requestContext.domainName + '/' + event.requestContext.stage + }); + await apigwManagementApi.postToConnection({ + ConnectionId: connectionId, + Data: JSON.stringify(data) + }).promise() + console.log('publishing data to client connection:'+connectionId+',data:'+data+' ..done') +} + +module.exports={ + addNewConnection, + closeConnection, + publish +} +// addNewConnection("asd","aasd").then(res=>{ +// console.log('done') +// }) \ No newline at end of file diff --git a/server/src/app/service/email.service.js b/server/src/app/service/email.service.js index fc10bb4b..80cc091e 100644 --- a/server/src/app/service/email.service.js +++ b/server/src/app/service/email.service.js @@ -4,6 +4,9 @@ sgMail.setApiKey(process.env.SENDGRID_API_KEY); const SYSTEM_MAIL='noreply@responsively.app' async function sendLicenseKeyMail(email,licenseKey){ + if(process.env.SEND_MAIL==='false'){ + return + } console.log('sending license key to email:'+email) const msg = { to: email, diff --git a/server/src/app/service/plan.service.js b/server/src/app/service/plan.service.js new file mode 100644 index 00000000..dbe842bc --- /dev/null +++ b/server/src/app/service/plan.service.js @@ -0,0 +1,51 @@ +const Plan=require('../model/plan.model') +const mongoose=require('mongoose') + +async function getPlan(planId){ + console.log('fetching from db - planId:'+planId) + const plan=await Plan.findOne({_id: mongoose.Types.ObjectId(planId)}).exec() + console.log('plan:'+plan) + return plan +} + +async function getPlanByName(planName){ + console.log('fetching from db - planName:'+planName) + const plan=await Plan.findOne({name: planName}).exec() + console.log('plan:'+plan) + return plan +} + +async function checkIfPlanStillValidForDate(startDate, planId){ + + let plan=await getPlan(planId) + return validator(startDate,plan) + return false +} + +function validator(startDate, plan){ + if(plan && plan.duration==='MONTH'){ + console.log('calculating validity of month plan:'+plan+',startTime:'+startDate) + let endDate=new Date(startDate) + endDate.setMonth(endDate.getMonth()+1) + if(endDate.getTime()>=new Date().getTime()){ + return true + } + } + return false +} + +async function getCurrentUserLimitForPlan(startDate,planId){ + let plan=await getPlan(planId) + let valid=validator(startDate,plan) + if(valid){ + return plan.user_limit + } + return 0 + +} +module.exports={ + getPlan, + getPlanByName, + checkIfPlanStillValidForDate, + getCurrentUserLimitForPlan +} \ No newline at end of file diff --git a/server/src/app/service/subscription.service.js b/server/src/app/service/subscription.service.js new file mode 100644 index 00000000..d562eba7 --- /dev/null +++ b/server/src/app/service/subscription.service.js @@ -0,0 +1,114 @@ +const User=require('../model/user.model') +const Subscription=require('../model/subscription.model') +const userService=require('../service/user.service') +const planService=require('../service/plan.service') +const constants=require('../constants/constants') +const emailService=require('../service/email.service') +const InvalidLicenseError=require('../exception/invalid-license-error.exception') +const UserExistsError=require('../exception/user-exists-error.exception') +const mongoose=require('mongoose') + +async function createUserAndActivateTrial(email){ + + try{ + let trialPlan=await planService.getPlanByName(constants.TRIAL_PLAN) + + if(await userService.checkIfUserExists(email)){ + throw new UserExistsError('User exists') + } + let user=await userService.createUser(email) + + let subscription=new Subscription({ + user_id: user._id, + plan_id: trialPlan._id, + start_date: new Date(), + status: constants.SUBSCRIPTION_STATUS.ACTIVE.id, + }) + await insertSubscription(subscription) + await emailService.sendLicenseKeyMail(email,user.license_key) + }catch(err){ + console.log(err) + throw err + } +} + +async function insertSubscription(subscription){ + + subscription.c_ts=new Date() + subscription.u_ts=new Date() + try{ + await subscription.save() + }catch(err){ + console.log('error saving subscription'+subscription); + throw err + } +} + +async function getSubscriptionByLicenseKey(licenseKey){ + try{ + if(!licenseKey){ + throw new InvalidLicenseError('Invalid License :'+licenseKey) + } + let user=await User.findOne({license_key: licenseKey}).exec() + if(!user){ + throw new InvalidLicenseError('Invalid License:'+licenseKey) + } + let subscription= await Subscription.findOne({user_id: user._id,status: constants.SUBSCRIPTION_STATUS.ACTIVE.id}).exec() + return subscription + }catch(err){ + console.log(err) + throw err + } +} + +async function validateLicenseKey(licenseKey){ + + try{ + let subscription= await getSubscriptionByLicenseKey(licenseKey) + let licenseValid=await planService.checkIfPlanStillValidForDate(subscription.start_date,subscription.plan_id) + if(licenseValid){ + console.log('license active!'); + return true + } + console.log('license expired!'); + return false + }catch(err){ + console.log(err) + throw err + } +} + +async function constructLicenseValidationResponse(licenseKey){ + let responseBody={} + try{ + if(!licenseKey){ + throw new InvalidLicenseError('licenseKey is empty') + } + responseBody.status=await validateLicenseKey(licenseKey) + if(responseBody.status){ + responseBody.statusCode=200 + responseBody.message='success' + }else{ + responseBody.statusCode=403 + responseBody.message='license expired' + } + }catch(err){ + console.log(err) + responseBody.status=false + if(err instanceof InvalidLicenseError){ + responseBody.statusCode=403 + responseBody.message='Invalid License' + }else{ + responseBody.statusCode=500 + responseBody.message='Internal Server Error' + } + } + return responseBody +} + +module.exports={ + createUserAndActivateTrial, + validateLicenseKey, + constructLicenseValidationResponse, + getSubscriptionByLicenseKey +} \ No newline at end of file diff --git a/server/src/app/service/user.service.js b/server/src/app/service/user.service.js index 710af8eb..37c1dbd3 100644 --- a/server/src/app/service/user.service.js +++ b/server/src/app/service/user.service.js @@ -1,25 +1,25 @@ const User=require('../model/user.model') -const subscriptionUtils=require('../utils/subscription.utils') +const Plan=require('../model/plan.model') +const planService=require('../service/plan.service') const constants=require('../constants/constants') const crypto=require('crypto') const emailService=require('../service/email.service') const InvalidEmailError=require('../exception/invalid-email-error.exception') const UserExistsError=require('../exception/user-exists-error.exception') -const InvalidLicenseError=require('../exception/invalid-license-error.exception') +const mongoose=require('mongoose') -async function createUserAndEnableTrial(email){ +async function createUser(email){ if(!emailService.validateEmailExistence(email)){ throw new InvalidEmailError('Invalid Email:'+email) } let newUser=new User({ email: email, - license_key:getLicenseKey(), - subscription:subscriptionUtils.getSubscriptionObject(constants.TRIAL_PLAN,new Date()), + license_key:getLicenseKey() }) await insertUser(newUser) - await emailService.sendLicenseKeyMail(email,newUser.license_key) + return newUser } async function insertUser(newUser){ @@ -38,27 +38,20 @@ async function insertUser(newUser){ return newUser } -async function validateLicenseKey(licenseKey){ +async function getUserByEmail(email){ - if(!licenseKey){ - throw new InvalidLicenseError('Invalid License :'+licenseKey) - } - try{ - let user=await User.findOne({license_key: licenseKey}).exec() - if(!user){ - throw new InvalidLicenseError('Invalid License:'+licenseKey) - } - let licenseValid=user.subscription.valid_till.getTime()-new Date().getTime() - if(licenseValid<0){ - console.log('license expired!'); - return false - } - console.log('license active!'); + return await User.findOne({email: email}) +} + +async function checkIfUserExists(email){ + + let user=await getUserByEmail(email) + console.log(user) + if(user){ return true - }catch(err){ - throw err } -} + return false +} function getLicenseKey(email){ const key=crypto.randomBytes(20).toString('hex'); @@ -66,6 +59,7 @@ function getLicenseKey(email){ } module.exports={ - createUserAndEnableTrial, - validateLicenseKey + createUser, + getUserByEmail, + checkIfUserExists } \ No newline at end of file diff --git a/server/src/app/utils/subscription.utils.js b/server/src/app/utils/subscription.utils.js deleted file mode 100644 index 174572b3..00000000 --- a/server/src/app/utils/subscription.utils.js +++ /dev/null @@ -1,39 +0,0 @@ -const constants=require('../constants/constants') -TRIAL_PERIOD=14 -ONE_DAY=86400000 - - -function getValidTill(type,startDate){ - validate(type,startDate) - if(type==constants.TRIAL_PLAN){ - return computeTrialValidTill(startDate) - } -} - -function computeTrialValidTill(startDate){ - return new Date(startDate.getTime()+TRIAL_PERIOD*ONE_DAY) -} - -function getSubscriptionObject(type,startDate){ - validate(type, startDate) - let subscriptionType=type - let subscriptionStateDate=startDate; - let subscriptionValidTill=getValidTill(subscriptionType,subscriptionStateDate) - let subscriptionObject={ - type: subscriptionType, - start_date: subscriptionStateDate, - valid_till: subscriptionValidTill - } - return subscriptionObject; -} - -function validate(type, startDate){ - if(!type || !startDate){ - throw new Error('Invalid Parameter type:'+type+', startDate:'+startDate) - } -} - -module.exports={ - getSubscriptionObject -} -// to do for other plans \ No newline at end of file diff --git a/server/src/http/lambda.js b/server/src/http/lambda.js index ae6afd8f..d2cb4e10 100644 --- a/server/src/http/lambda.js +++ b/server/src/http/lambda.js @@ -1,5 +1,5 @@ const db=require('../db/db') -const userService = require('../app/service/user.service') +const subscriptionService = require('../app/service/subscription.service') const UserExistsError=require('../app/exception/user-exists-error.exception') const InvalidLicenseError=require('../app/exception/invalid-license-error.exception') const InvalidEmailError=require('../app/exception/invalid-email-error.exception') @@ -9,11 +9,11 @@ export async function activateTrial(event, context, callback) { let statusCode=0 context.callbackWaitsForEmptyEventLoop = false; try{ - const email=event['queryStringParameters']['email'] + const {email}=event.queryStringParameters if(!email){ throw new InvalidEmailError('email is empty') } - await userService.createUserAndEnableTrial(email) + await subscriptionService.createUserAndActivateTrial(email) responseBody.status=true responseBody.statusCode=200; responseBody.message='success' @@ -44,32 +44,14 @@ export async function validateLicense(event, context, callback) { let statusCode=0 context.callbackWaitsForEmptyEventLoop = false; try{ - const licenseKey=event['queryStringParameters']['licenseKey'] - if(!licenseKey){ - throw new InvalidLicenseError('licenseKey is empty') - } - responseBody.status=await userService.validateLicenseKey(licenseKey) - responseBody.statusCode=200 - if(responseBody.status){ - responseBody.message='success' - }else{ - responseBody.message='license expired' - } - statusCode=200 - }catch(err){ - console.log(err) - responseBody.status=false - if(err instanceof InvalidLicenseError){ - responseBody.statusCode=403 - responseBody.message='Invalid License' - }else{ - responseBody.statusCode=500 - responseBody.message='Internal Server Error' - } - } - let response= { - statusCode: 200, - body: JSON.stringify(responseBody) - } - callback(null, response) + const {licenseKey}=event.queryStringParameters + responseBody=await subscriptionService.constructLicenseValidationResponse(licenseKey) + }catch(err){ + console.log(err) + } + let response= { + statusCode: 200, + body: JSON.stringify(responseBody) + } + callback(null, response) } \ No newline at end of file diff --git a/server/src/websocket/lambda.js b/server/src/websocket/lambda.js index 8ffd007d..cfb646b1 100644 --- a/server/src/websocket/lambda.js +++ b/server/src/websocket/lambda.js @@ -1,18 +1,61 @@ var db=require('../db/db') const userService = require('../app/service/user.service') +const activeConnectionService = require('../app/service/active-connections.service') +const subscriptionService = require('../app/service/subscription.service') const InvalidLicenseError=require('../app/exception/invalid-license-error.exception') +const InvalidSubscriptionError=require('../app/exception/invalid-subscription.exception') export async function connectionHandler(event, context, callback) { + + let responseBody={} + const connectionId=event.requestContext.connectionId + try{ + console.log(event) + if(event.requestContext.eventType==='CONNECT'){ + const {licenseKey}=event.queryStringParameters + await activeConnectionService.addNewConnection(licenseKey, connectionId) + responseBody.status=true + responseBody.statusCode=200 + responseBody.message='connection established' + }else if(event.requestContext.eventType==='DISCONNECT'){ + await activeConnectionService.closeConnection(connectionId) + } + }catch(err){ + console.log(err) + responseBody.status=false + if(err instanceof InvalidSubscriptionError){ + responseBody.statusCode=403 + responseBody.message='invalid subscription' + }else if(err instanceof InvalidLicenseError){ + responseBody.statusCode=403 + responseBody.message='invalid license' + }else{ + responseBody.statusCode=500 + responseBody.message='server error' + } + } return { statusCode: 200, - body: 'Message recieved', + body: JSON.stringify(responseBody), }; } export async function defaultHandler(event, context, callback) { + try{ + const body=JSON.parse(event.body) + const connectionId=event.requestContext.connectionId + + if(!body.data || !body.data.licenseKey){ + throw new InvalidLicenseError('license Key is empty') + } + const licenseKey=body.data.licenseKey + activeConnectionService.closeConnection(licenseKey, connectionId) + }catch(err){ + console.log(err) + } return { statusCode: 200, - body: 'Message recieved', + body: 'Connection closed', }; } @@ -27,6 +70,7 @@ export async function validateLicense(event, context, callback) { let responseBody={} let statusCode=0 context.callbackWaitsForEmptyEventLoop = false; + const connectionId=event.requestContext.connectionId console.log(event) try{ const body=JSON.parse(event.body) @@ -34,25 +78,14 @@ export async function validateLicense(event, context, callback) { throw new InvalidLicenseError('licenseKey is empty') } const licenseKey=body['data']['licenseKey'] - responseBody.status=await userService.validateLicenseKey(licenseKey) - responseBody.statusCode=200 - if(responseBody.status){ - responseBody.message='success' - }else{ - responseBody.message='license expired' - } - statusCode=200 + responseBody=await subscriptionService.constructLicenseValidationResponse(licenseKey) }catch(err){ console.log(err) responseBody.status=false - if(err instanceof InvalidLicenseError){ - responseBody.statusCode=403 - responseBody.message='Invalid License' - }else{ - responseBody.statusCode=500 - responseBody.message='Internal Server Error' - } + responseBody.statusCode=500 + responseBody.message='internal server error' } + await activeConnectionService.publish(event,connectionId,responseBody) let response= { statusCode: 200, body: JSON.stringify(responseBody)