From 3c0addf28b2ab1a799cf0ac6ecfae0b54d4031f0 Mon Sep 17 00:00:00 2001 From: Barry Low Date: Tue, 11 Dec 2018 23:17:42 -0800 Subject: [PATCH] Sendgrid resolvers --- docs/README.md | 11 +- server/.env.dev | 17 -- server/.env.sample | 11 +- server/.gitignore | 1 + ...est.yml => docker-compose.build-image.yml} | 0 server/src/email/sendgrid.js | 153 ------------ .../src/graphql/resolvers/Mutation/Member.js | 17 -- server/src/graphql/resolvers/Mutation/Team.js | 0 .../graphql/resolvers/Mutation/TeamMember.js | 0 .../src/graphql/resolvers/Mutation/index.js | 195 +--------------- .../resolvers/Mutation/sendgrid/corporate.js | 221 ++++++++++++++++++ .../resolvers/Mutation/sendgrid/master.js | 219 +++++++++++++++++ .../resolvers/Mutation/sendgrid/teams.js | 134 +++++++++++ server/src/graphql/resolvers/Query/index.js | 14 -- .../graphql/resolvers/gateways/User/index.js | 57 ----- server/src/graphql/resolvers/index.js | 5 +- server/src/graphql/schema.graphql | 54 +---- server/src/middleware/getMember.js | 23 -- 18 files changed, 616 insertions(+), 516 deletions(-) delete mode 100644 server/.env.dev rename server/{docker-compose.imagetest.yml => docker-compose.build-image.yml} (100%) delete mode 100644 server/src/email/sendgrid.js delete mode 100644 server/src/graphql/resolvers/Mutation/Member.js delete mode 100644 server/src/graphql/resolvers/Mutation/Team.js delete mode 100644 server/src/graphql/resolvers/Mutation/TeamMember.js create mode 100644 server/src/graphql/resolvers/Mutation/sendgrid/corporate.js create mode 100644 server/src/graphql/resolvers/Mutation/sendgrid/teams.js delete mode 100644 server/src/graphql/resolvers/gateways/User/index.js delete mode 100644 server/src/middleware/getMember.js diff --git a/docs/README.md b/docs/README.md index 3a6dc38fb..ba6858377 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,8 +11,13 @@ The Notifications Module graphql schema: ``` type Mutation { - master - teamsSendMemberInvite(email: String!, teamId: String!): String - teamsVerifyMemberInvite(token: String!): String + Corporate_NewsletterSignup(email: String!): String + Corporate_NewsletterSignupVerify(token: String!): String + Corporate_Contact(email: String!, subject: String!, Message: String!, recaptcha: String!): String + Master_NewsletterSignup(email: String!): String + Master_NewsletterSignupVerify(token: String!): String + Master_Contact(email: String!, subject: String!, Message: String!, recaptcha: String!): String + Teams_SendTeamMemberInvite(email: String!, teamName: String!): String + Teams_SendTeamCreateConfirmation(email: String!, teamName: String!, botName: String!): String } ``` diff --git a/server/.env.dev b/server/.env.dev deleted file mode 100644 index a8386085e..000000000 --- a/server/.env.dev +++ /dev/null @@ -1,17 +0,0 @@ -PRESHARED_GATEWAY_KEY=zk8pkrs050fa1pCl - -NODE_ENV=development -GENERATE_SOURCEMAP=true -PORT=4101 - -# SendGrid Api Keys -SG_APIKEY=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 -SG_APIKEY_CONTACTS=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 - -SG_MASTER_SIGNUP_EMAILID=d-b4c1424b7b444fdab69e2c8d19f17497 -SG_MASTER_FEEDBACK_EMAILID=d-c613efbcb0484124a1d49cf9c11de07b - -SG_CORPORATE_SIGNUP_EMAILID=d-b4c1424b7b444fdab69e2c8d19f17497 -SG_CORPORATE_CONTACT_EMAILID=sg-master-contact-template-id - -SG_TEAMS_MEMBER_INVITE_EMAILID=sg-teams-member-invite-template-id diff --git a/server/.env.sample b/server/.env.sample index c360a703f..35a7da7b4 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -5,9 +5,12 @@ PRESHARED_GATEWAY_KEY=SECRET_STUFF NODE_ENV=development GENERATE_SOURCEMAP=false +# APIs +PLATFORM_API_URL=https://platform-api-dev.advancedalgos.net + # SendGrid Api Keys SG_APIKEY=sg-apikey -SG_APIKEY_CONTACTS=sg-apikey-contacts +SG_APIKEY_CAMPAIGN=sg-apikey-contacts # SendGrid Template IDs SG_MASTER_SIGNUP_EMAILID=sg-master-signup-template-id @@ -17,3 +20,9 @@ SG_CORPORATE_SIGNUP_EMAILID=sg-master-signup-template-id SG_CORPORATE_CONTACT_EMAILID=sg-master-contact-template-id SG_TEAMS_MEMBER_INVITE_EMAILID=sg-teams-member-invite-template-id +SG_TEAMS_TEAMCREATE_EMAILID=sg-teams-teamcreate-template-id + +#SendGrid Origins +SG_PLATFORM_ORIGIN=https://platform-dev.advancedalgos.net +# SG_PLATFORM_ORIGIN=https://platform.advancedalgos.net +SG_CORPORATE_ORIGIN=https://advancedalgos.net diff --git a/server/.gitignore b/server/.gitignore index d5985783d..5410e8c43 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,5 +1,6 @@ /.env /.env.prod +.env.dev /database/.env /database/docker-compose.prisma.prod.yml /docker-compose.prisma.prod.yml diff --git a/server/docker-compose.imagetest.yml b/server/docker-compose.build-image.yml similarity index 100% rename from server/docker-compose.imagetest.yml rename to server/docker-compose.build-image.yml diff --git a/server/src/email/sendgrid.js b/server/src/email/sendgrid.js deleted file mode 100644 index e5243a357..000000000 --- a/server/src/email/sendgrid.js +++ /dev/null @@ -1,153 +0,0 @@ -var jwt = require('jsonwebtoken') -var axios = require('axios') - -import { logger } from '../logger' - -const sendTeamMemberInvite = function(email, team) { - const dev = process.env.NODE_ENV === 'development' ? true : false - logger.info(`sendTeamMemberInvite email|team: ${email} | ${team}`) - const API_KEY = process.env.SG_APIKEY - const token = jwt.sign({ email: email, team: team.slug }, API_KEY, { expiresIn: '7d' }) - let origin = 'https://teams.advancedalgos.net' - const params = '/activate-team-membership/' - if (dev){ - origin = 'http://localhost:3001' - } - - const headers = { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin' : origin, - 'Access-Control-Allow-Credentials': 'true' - }; - - const data = JSON.stringify({ - "personalizations": [ - { - "to": [ - { - "email": email - } - ], - dynamic_template_data: { - "aateaminvitelink": origin + params + token, - "aateamname": team.name, - "subject": "INVITATION: Join team " + team.name - }, - "subject": "INVITATION: Join team " + team.name - } - ], - "from": { - "email": "noreply@advancedalgos.net", - "name": "Advanced Algos Teams" - }, - "reply_to": { - "email": "feedback@advancedalgos.net", - "name": "Advanced Algos Teams" - }, - "template_id": process.env.SG_INVITE_EMAILID - }) - - let sendVerify = axios({ - method: 'post', - url: 'https://api.sendgrid.com/v3/mail/send', - data: data, - headers:{ - 'content-type': 'application/json', - 'authorization': 'Bearer ' + API_KEY - } - }) - .then(function (response) { - if (response.status >= 200 && response.status < 300) { - return 'Success' - } else { - throw response.data.errors[0].message - } - }) - .catch(function (error) { - return `${error.status}-${error.response.data.errors[0].message}` - }) - return sendVerify -} - -const sendTeamCreateConfirmation = function(email, teamName, botName) { - const dev = process.env.NODE_ENV === 'development' ? true : false - logger.info(`sendTeamCreateConfirmation email|teamName|botName: ${email} | ${teamName} | ${botName}`) - const API_KEY = process.env.SG_APIKEY - let origin = 'https://teams.advancedalgos.net' - if (dev){ - origin = 'http://localhost:3001' - } - - const headers = { - 'Content-Type': 'application/json', - 'Access-Control-Allow-Origin' : origin, - 'Access-Control-Allow-Credentials': 'true' - }; - - const data = JSON.stringify({ - "personalizations": [ - { - "to": [ - { - "email": email - } - ], - dynamic_template_data: { - "aadeveloplink": "https://develop.advancedalgos.net/index.html", - "aateamname": teamName, - "aabotname": botName, - "subject": "Team " + teamName + " has been created!" - }, - "subject": "Team " + teamName + " has been created!" - } - ], - "from": { - "email": "noreply@advancedalgos.net", - "name": "Advanced Algos Teams" - }, - "reply_to": { - "email": "feedback@advancedalgos.net", - "name": "Advanced Algos Teams" - }, - "template_id": process.env.SG_TEAMCREATE_EMAILID - }) - - let sendTeamCreation = axios({ - method: 'post', - url: 'https://api.sendgrid.com/v3/mail/send', - data: data, - headers:{ - 'content-type': 'application/json', - 'authorization': 'Bearer ' + API_KEY - } - }) - .then(function (response) { - if (response.status >= 200 && response.status < 300) { - return 'Success' - } else { - throw response.data.errors[0].message - } - }) - .catch(function (error) { - return `${error.status}-${error.response.data.errors[0].message}` - }) - return sendTeamCreation -} - -const verifyInviteToken = async function(token) { - const API_KEY = process.env.SG_APIKEY - let verifiedToken - try { - verifiedToken = await jwt.verify(token, API_KEY, {maxAge: '7d'}) - } catch(err) { - if (err.name === "TokenExpiredError"){ - throw new Error ("Error: Token Expired. Please request new invitation or apply to the team again") - } else { - throw new Error (`Error: Token error - ${err.message}`) - } - } - logger.info(`verifyInviteToken: ${verifiedToken}`) - return verifiedToken -} - -module.exports = { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } diff --git a/server/src/graphql/resolvers/Mutation/Member.js b/server/src/graphql/resolvers/Mutation/Member.js deleted file mode 100644 index 653764d4c..000000000 --- a/server/src/graphql/resolvers/Mutation/Member.js +++ /dev/null @@ -1,17 +0,0 @@ -export const createMember = async function (ctx, info, authId) { - logger.info('createMember', authId) - const member = await ctx.db.mutation.upsertMember({ - where: { - authId: authId, - }, - create: { - authId: authId, - alias: idToken.nickname - }, - update: { - authId: authId, - alias: idToken.nickname - } - }, info) - return member -} diff --git a/server/src/graphql/resolvers/Mutation/Team.js b/server/src/graphql/resolvers/Mutation/Team.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/graphql/resolvers/Mutation/TeamMember.js b/server/src/graphql/resolvers/Mutation/TeamMember.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/server/src/graphql/resolvers/Mutation/index.js b/server/src/graphql/resolvers/Mutation/index.js index 7d94f1a61..9f47efcca 100644 --- a/server/src/graphql/resolvers/Mutation/index.js +++ b/server/src/graphql/resolvers/Mutation/index.js @@ -1,188 +1,17 @@ -import axios from 'axios' -import { ApolloError } from 'apollo-server-express' - -import { getUser } from '../../../middleware/getMember' - -import { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } from '../../../email/sendgrid' - -import TEAMS_CONNECTIONS_FRAGMENT from '../../fragments/TeamsConnectionsFragment' -import TEAMS_FRAGMENT from '../../fragments/TeamsFragment' -import TEAM_FB_FRAGMENT from '../../fragments/TeamFBFragment' - -import { logger, AuthenticationError, ServiceUnavailableError, DatabaseError, ConflictError } from '../../../logger' +import { Corporate_NewsletterSignup, Corporate_NewsletterSignupVerify, Corporate_Contact } from './sendgrid/corporate' +import { Master_NewsletterSignup, Master_NewsletterSignupVerify, Master_Feedback } from './sendgrid/master' +import { Teams_SendTeamMemberInvite, Teams_VerifyTeamMemberInviteToken, Teams_SendTeamCreateConfirmation } from './sendgrid/teams' export const resolvers = { Mutation: { - async sendMemberInviteSG(parent, { email, teamId }, ctx, info) { - const team = await ctx.db.query.team({ where: { id: teamId } }, TEAMS_FRAGMENT) - const addMember = await ctx.db.mutation.upsertTeamMembers({ - where:{email: email}, - create:{role:'MEMBER', email:email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invited by ${team.members[0].member.alias}`}}}, - update:{role: 'MEMBER', email: email, team: {connect: {id: teamId}}, status:{create:{status: 'INVITED', reason: `Invite resent by ${team.members[0].member.alias}`}}}}, - `{ id }`) - const sendInvite = await sendTeamMemberInvite(email, team) - return sendInvite - }, - async verifyTeamInvite(parent, { token }, ctx, info) { - let verifiedToken = null - let team = null - try { - verifiedToken = await verifyInviteToken(token) - team = { - email: verifiedToken.email, - team: { - slug: verifiedToken.team - } - } - logger.info('verifyTeamInvite.token: ', await verifiedToken) - } catch (err) { - logger.debug('verifyTeamInvite. err: ', err) - throw new DatabaseError(err.message) - } - return team - }, - async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) { - logger.info('createTeam ctx.request.user:') - logger.info(ctx.request.headers.userid) - const authId = ctx.request.headers.userid - if (!authId) { - throw new AuthenticationError() - } - const userData = await getUser(authId) - logger.info('userData:') - logger.info(JSON.stringify(await userData)) - const alias = await userData.data.users_User.alias - const email = await userData.data.users_User.email - const avatar = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-avatar-default.png' - const banner = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-banner-default.png' - - const createTeamUrl = encodeURI(`${slug}/${name}/${alias}/${botSlug}/${botName}`) - logger.info('createTeamUrl:') - logger.info(JSON.stringify(await createTeamUrl)) - - const platformUrl = 'https://charts-dev.advancedalgos.net/AABrowserAPI/newTeam/' - // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' - - logger.info(`${platformUrl}${createTeamUrl}/${authId}`) - const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}/${process.env.AAWEB_TEAM_SHARED_SECRET}`) - .then((result) => { - console.log('createPlatformTeam result:', result.data) - if(result.data.message === 'Team Name already taken'){ - return { error: 'Team already exist on AA Cloud.', code: 409 } - } - if(result.data.result === 'Fail'){ - return { error: `${result.data.message} - Creating team on AA Cloud has failed`, code: 404 } - } - return result - }) - - logger.info('createPlatformTeam') - logger.info(await createPlatformTeam) - logger.info('createPlatformTeam Error') - logger.error(await createPlatformTeam.error) - logger.info('createPlatformTeam Code') - logger.error(await createPlatformTeam.code) - - // if (await createPlatformTeam.error && await createPlatformTeam.code === 404) throw new ApolloError(await createPlatformTeam.error, 404) - // if (await createPlatformTeam.error && await createPlatformTeam.code === 409) throw new ApolloError(await createPlatformTeam.error, 409) - - const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`) - .catch((err) => { - logger.debug(err, 'existingMember error: ') - throw new DatabaseError(err) - }) - - logger.debug('existingMember:') - logger.debug(await existingMember) - - let createTeam - if(existingMember !== null && existingMember.id !== null){ - logger.info('createTeam with existingMember:') - createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {connect: {authId: authId}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Connected to Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug(err, 'createTeamMutation error: ') - return new DatabaseError(err) - }) - } else { - logger.info('createTeam without existingMember:') - createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {create: {authId: authId, alias: alias, visible:'true', status: { create: { status: 'ACTIVE', reason: `Created team ${name}`}}}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Created with Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug(err, 'createTeamMutation error: ') - return new DatabaseError(err) - }) - } - - sendTeamCreateConfirmation(email, name, botName) - - return createTeam - }, - async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) { - return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug('createTeam error: ', err) - throw new DatabaseError(`Team Profile Update Error: ${err}`) - }) - }, - async updateFB(parent, { fbId, avatar}, ctx, info) { - return ctx.db.mutation.updateFinancialBeings({data:{avatar:avatar}, where:{id:fbId}}, info) - .catch((err) => { - logger.debug('createTeam error: ', err) - throw new DatabaseError(`Update Financial Being Error: ${err}`) - }) - }, - async deleteTeam(parent, { slug, botSlug }, ctx, info) { - logger.info('deleteTeam ctx.request.user:') - logger.info(ctx.request.headers.userid) - const authId = ctx.request.headers.userid - if (!authId) { - throw new AuthenticationError() - } - logger.info(authId) - logger.info('getUser') - logger.info(getUser) - - return getUser(authId) - .then(async result => { - logger.info('deleteTeam axios:') - logger.info(await result) - - const alias = await result.data.users_User.alias - // const email = result.data.users_User.email - const deleteTeamUrl = encodeURI(`${slug}/${alias}/${botSlug}`) - logger.info('deleteTeamUrl:') - logger.info(JSON.stringify(await deleteTeamUrl)) - - const platformUrl = 'https://charts-dev.advancedalgos.net/AABrowserAPI/deleteTeam/' - // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' - - logger.info(`${platformUrl}${deleteTeamUrl}/${authId}`) - const deletePlatformTeam = await axios.get(`${platformUrl}${deleteTeamUrl}/${authId}/${process.env.AAWEB_DELETE_TEAM_SHARED_SECRET}`) - .then((result) => { - console.log('deletePlatformTeam result:', result.data) - if(result.data.result === 'Fail'){ - throw new DatabaseError(result.data.message) - } - return result - }) - .catch(err =>{ - logger.debug('deletePlatformTeam err:') - logger.debug(err) - throw new DatabaseError(err) - }) - console.log('deletePlatformTeam returned: ', await deletePlatformTeam) - - return ctx.db.mutation.deleteTeam({ where: { slug } }, info) - .catch((res) => { - logger.info('deleteTeam error: ', res) - const errors = res.graphQLErrors.map((error) => { - throw new DatabaseError(error.message) - }) - }) - }) - .catch((err) => { - logger.debug('deleteTeam error: ') - throw new DatabaseError(err) - }) - } + Corporate_NewsletterSignup: (parent, arg, ctx, info) => Corporate_NewsletterSignup(parent, arg, ctx, info), + Corporate_NewsletterSignupVerify: (parent, arg, ctx, info) => Corporate_NewsletterSignupVerify(parent, arg, ctx, info), + Corporate_Contact: (parent, arg, ctx, info) => Corporate_Contact(parent, arg, ctx, info), + Master_NewsletterSignup: (parent, arg, ctx, info) => Master_NewsletterSignup(parent, arg, ctx, info), + Master_NewsletterSignupVerify: (parent, arg, ctx, info) => Master_NewsletterSignupVerify(parent, arg, ctx, info), + Master_Feedback: (parent, arg, ctx, info) => Master_Feedback(parent, arg, ctx, info), + Teams_SendTeamMemberInvite: (parent, arg, ctx, info) => Teams_SendTeamMemberInvite(parent, arg, ctx, info), + Teams_VerifyTeamMemberInviteToken: (parent, arg, ctx, info) => Teams_VerifyTeamMemberInviteToken(parent, arg, ctx, info), + Teams_SendTeamCreateConfirmation: (parent, arg, ctx, info) => Teams_SendTeamCreateConfirmation(parent, arg, ctx, info) } } diff --git a/server/src/graphql/resolvers/Mutation/sendgrid/corporate.js b/server/src/graphql/resolvers/Mutation/sendgrid/corporate.js new file mode 100644 index 000000000..3f042e8ec --- /dev/null +++ b/server/src/graphql/resolvers/Mutation/sendgrid/corporate.js @@ -0,0 +1,221 @@ +import sgMail from '@sendgrid/mail' +import axios from 'axios' +import jwt from 'jsonwebtoken' +import { ApolloError } from 'apollo-server-express' + +import logger from '../../logger' + +const API_KEY = process.env.SG_APIKEY +const API_KEY_CAMPAIGN = process.env.SG_APIKEY_CAMPAIGN + +const origin = process.env.SG_CORPORATE_ORIGIN + +export default { + async Corporate_NewsletterSignup(parent, { email }, ctx, info) { + logger.debug('Corporate_NewsletterSignup params email:') + logger.debug(email) + const token = jwt.sign({ email: email }, API_KEY, { expiresIn: '1d' }) + const params = '/email-verification?token=' + + const data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": email + } + ], + dynamic_template_data: { + "aaverifylink": origin + params + token, + "subject": 'VERIFY YOUR INTEREST in Advanced Algos' + }, + "subject": 'VERIFY YOUR INTEREST in Advanced Algos' + } + ], + "from": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Team" + }, + "reply_to": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Team" + }, + "template_id": process.env.SG_CORPORATE_SIGNUP_EMAILID + }) + + const sendVerify = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/mail/send', + data: data, + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY + } + }) + .then(function (response) { + logger.debug('Sendgrid sendVerify response:') + logger.debug(response) + if (response.status >= 200 && response.status < 300) { + return 'SUCCESS' + } else { + throw response.data.errors[0].message + } + }) + .catch(function (error) { + logger.error('Sendgrid sendVerify Error:') + logger.error(error) + throw new ApolloError(`Sendgrid sendVerify Error: ${error.response.data.errors[0].message}`, 404) + }) + return sendVerify + }, + + async Corporate_NewsletterSignupVerify(parent, { token }, ctx, info){ + let verifiedToken + try { + verifiedToken = jwt.verify(token, API_KEY, {maxAge: '1d'}) + } catch(err) { + logger.error('Sendgrid verifiedToken Error:') + logger.error(err) + if (err.name === 'TokenExpiredError'){ + throw new ApolloError('Error: Token Expired. Please resubmit email address.', 400) + } else { + throw new ApolloError(`Error: ${err.message}`, 400) + } + } + logger.debug('Sendgrid verifySignup verifiedToken: ') + logger.debug(verifiedToken) + + const email = verifiedToken.email + + const subscribe = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/contactdb/recipients', + data: [{"email": email}], + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY_CAMPAIGN + } + }) + .then(response => { + logger.debug('Sendgrid verifySignup subscribe: ') + logger.debug(response) + const recipients = response.data.persisted_recipients + + if (recipients.length > 0 ){ + const addToList = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/contactdb/lists/4068018/recipients/' + recipients[0], + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY_CAMPAIGN + } + }) + .then(response => { + logger.debug('Sendgrid verifySignup addToList: ') + logger.debug(response) + if (response.status >= 200 && response.status < 300) { + return 'SUCCESS' + } else { + throw response.data.errors[0].message + } + }) + .catch(error => { + logger.error('Sendgrid verifySignUp subscribe Error:') + logger.error(error) + throw `Email SignUp Error: ${error}` + }) + return addToList + } else{ + return response.data.errors[0].message + } + }) + .then(response => { + return response + }) + .catch(error => { + logger.error('Sendgrid verifySubscribe Error:') + logger.error(error) + throw new ApolloError(`Sendgrid verifySubscribe Error: ${error.response.data.errors[0].message}`, 404) + }) + return subscribe + }, + + async Corporate_Contact(parent, { email, subject, message, recaptcha }, ctx, info) { + const toEmail = 'feedback@advancedalgos.net' + + const data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": email + } + ], + dynamic_template_data: { + "aacontactname": name, + "aacontactemail": email, + "aacontactbody": message + }, + "subject": `AA Corporate Site Contact - Message from ${name}` + } + ], + "from": { + "email": email, + "name": name + }, + "reply_to": { + "email": email, + "name": name + }, + "template_id": process.env.SG_CORPORATE_CONTACT_EMAILID + }) + + const checkCaptcha = axios({ + method: 'post', + url: 'https://www.google.com/recaptcha/api/siteverify', + params: { + secret: process.env.RECAPTCHA, + response: recaptcha + } + }) + .then(function (response) { + logger.info(response.data) + if (response.status >= 200 && response.status < 300 && response.data.success) { + return axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/mail/send', + data: data, + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY + } + }) + .then(function (response) { + logger.debug('Sendgrid Contact response: ') + logger.debug(response) + if (response.status >= 200 && response.status < 300) { + return `Corporate Contact email sent` + } else { + throw response.data.errors[0].message + } + }) + .catch(function (error) { + logger.error(`sendgrid Error: ${JSON.stringify(error)}`) + throw `Corporate Contact email send error: ${error.response.data.errors[0].message}` + }) + }else{ + throw response.data + } + }) + .then(function(response){ + return response + }) + .catch(function (error) { + logger.error('Sendgrid Corporate Contact RECAPTCHA Error:') + logger.error(error) + throw new ApolloError(`Sendgrid Corporate Contact RECAPTCHA Error: ${error.response.data.errors[0].message}`, 404) + }) + + return checkCaptcha + } +} diff --git a/server/src/graphql/resolvers/Mutation/sendgrid/master.js b/server/src/graphql/resolvers/Mutation/sendgrid/master.js index e69de29bb..6dd3d3e12 100644 --- a/server/src/graphql/resolvers/Mutation/sendgrid/master.js +++ b/server/src/graphql/resolvers/Mutation/sendgrid/master.js @@ -0,0 +1,219 @@ +import sgMail from '@sendgrid/mail' +import axios from 'axios' +import jwt from 'jsonwebtoken' +import { ApolloError } from 'apollo-server-express' + +import logger from '../../logger' + +const API_KEY = process.env.SG_APIKEY +const API_KEY_CAMPAIGN = process.env.SG_APIKEY_CAMPAIGN + +const origin = process.env.SG_PLATFORM_ORIGIN + +export default { + async Master_NewsletterSignup(parent, { email }, ctx, info) { + logger.debug('Master_NewsletterSignup params email:') + logger.debug(email) + const token = jwt.sign({ email: email }, API_KEY, { expiresIn: '1d' }) + const params = '/email-verification?token=' + + const data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": email + } + ], + dynamic_template_data: { + "aaverifylink": origin + params + token, + "subject": 'VERIFY YOUR INTEREST in Advanced Algos' + }, + "subject": 'VERIFY YOUR INTEREST in Advanced Algos' + } + ], + "from": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Team" + }, + "reply_to": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Team" + }, + "template_id": process.env.SG_MASTER_SIGNUP_EMAILID + }) + + const sendVerify = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/mail/send', + data: data, + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY + } + }) + .then(function (response) { + logger.debug('Sendgrid Master SendVerify response:') + logger.debug (response) + if (response.status >= 200 && response.status < 300) { + return 'SUCCESS' + } else { + throw response.data.errors[0].message + } + }) + .catch(function (error) { + logger.error('Sendgrid Master SendVerify Error:') + logger.error(error) + throw new ApolloError(`Sendgrid Master SendVerify Error: ${error.response.data.errors[0].message}`, 404) + }) + return sendVerify + }, + async Master_NewsletterSignupVerify(parent, { token }, ctx, info){ + let verifiedToken + try { + verifiedToken = jwt.verify(token, API_KEY, {maxAge: '1d'}) + } catch(err) { + logger.error('Sendgrid Master verifiedToken Error:') + logger.error(err) + if (err.name === 'TokenExpiredError'){ + throw new ApolloError('Error: Token Expired. Please resubmit email address.', 400) + } else { + throw new ApolloError(`Error: ${err.message}`, 400) + } + } + logger.debug('Sendgrid Master verifySignup verifiedToken: ') + logger.debug(verifiedToken) + + const email = verifiedToken.email + + const subscribe = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/contactdb/recipients', + data: [{"email": email}], + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY_CAMPAIGN + } + }) + .then(response => { + logger.info('Sendgrid Master verifySignup subscribe: ') + logger.info(response) + const recipients = response.data.persisted_recipients + + if (recipients.length > 0 ){ + const addToList = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/contactdb/lists/4068018/recipients/' + recipients[0], + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY_CAMPAIGN + } + }) + .then(response => { + logger.info('Sendgrid Master verifySignup addToList: ') + logger.info(response) + if (response.status >= 200 && response.status < 300) { + return 'SUCCESS' + } else { + throw response.data.errors[0].message + } + }) + .catch(error => { + logger.error('Sendgrid Master verifySignUp subscribe Error:') + logger.error(error) + throw `Email SignUp Error: ${error}` + }) + return addToList + } else{ + return response.data.errors[0].message + } + }) + .then(response => { + return response + }) + .catch(error => { + logger.error('Sendgrid Master verifySubscribe Error:') + logger.error(error) + throw new ApolloError(`Sendgrid Master verifySubscribe Error: ${error.response.data.errors[0].message}`, 404) + }) + return subscribe + }, + async Master_Feedback(parent, { email, subject, message, recaptcha }, ctx, info) { + const toEmail = 'feedback@advancedalgos.net' + + const data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": toEmail + } + ], + dynamic_template_data: { + "aacontactname": name, + "aacontactemail": email, + "aacontactbody": message + }, + "subject": `AA Platform App Feedback - Message from ${name}` + } + ], + "from": { + "email": email, + "name": name + }, + "reply_to": { + "email": email, + "name": name + }, + "template_id": process.env.SG_MASTER_FEEDBACK_EMAILID + }) + + const checkCaptcha = axios({ + method: 'post', + url: 'https://www.google.com/recaptcha/api/siteverify', + params: { + secret: process.env.RECAPTCHA, + response: recaptcha + } + }) + .then(function (response) { + logger.info(response.data) + if (response.status >= 200 && response.status < 300 && response.data.success) { + return axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/mail/send', + data: data, + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY + } + }) + .then(function (response) { + logger.debug('Platform Feedback response: ') + logger.debug(response) + if (response.status >= 200 && response.status < 300) { + return `Platform Feedback email sent` + } else { + throw response.data.errors[0].message + } + }) + .catch(function (error) { + logger.error(`sendgrid Error: ${JSON.stringify(error)}`) + throw `Platform Feedback email send error: ${error.response.data.errors[0].message}` + }) + }else{ + throw response.data + } + }) + .then(function(response){ + return response + }) + .catch(function (error) { + logger.error('Platform Feedback RECAPTCHA Error:') + logger.error(error) + throw new ApolloError(`Platform Feedback RECAPTCHA Error: ${error.response.data.errors[0].message}`, 404) + }) + + return checkCaptcha + } +} diff --git a/server/src/graphql/resolvers/Mutation/sendgrid/teams.js b/server/src/graphql/resolvers/Mutation/sendgrid/teams.js new file mode 100644 index 000000000..5f0ad1503 --- /dev/null +++ b/server/src/graphql/resolvers/Mutation/sendgrid/teams.js @@ -0,0 +1,134 @@ +import sgMail from '@sendgrid/mail' +import axios from 'axios' +import jwt from 'jsonwebtoken' +import { ApolloError } from 'apollo-server-express' + +import logger from '../../logger' + +const API_KEY = process.env.SG_APIKEY +const API_KEY_CAMPAIGN = process.env.SG_APIKEY_CAMPAIGN + +const origin = process.env.SG_PLATFORM_ORIGIN + +export default { + async Teams_SendTeamMemberInvite(parent, { email, teamName }, ctx, info) { + logger.debug('Teams_NewsletterSignup params email:') + logger.debug(email) + const token = jwt.sign({ email: email }, API_KEY, { expiresIn: '1d' }) + const params = '/email-verification?token=' + + const data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": email + } + ], + dynamic_template_data: { + "aateaminvitelink": origin + params + token, + "aateamname": teamName, + "subject": "INVITATION: Join team " + teamName + }, + "subject": "INVITATION: Join team " + teamName + } + ], + "from": { + "email": "noreply@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "reply_to": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "template_id": process.env.SG_TEAMS_MEMBER_INVITE_EMAILID + }) + + let sendVerify = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/mail/send', + data: data, + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY + } + }) + .then(function (response) { + if (response.status >= 200 && response.status < 300) { + return 'Success' + } else { + throw response.data.errors[0].message + } + }) + .catch(function (error) { + throw new ApolloError (`Send Team Invite Verification Error: - ${error.status}-${error.response.data.errors[0].message}`, 404) + }) + return sendVerify + }, + + async Teams_VerifyTeamMemberInviteToken(parent, { email, teamName }, ctx, info) { + let verifiedToken + try { + verifiedToken = await jwt.verify(token, API_KEY, {maxAge: '7d'}) + } catch(err) { + if (err.name === "TokenExpiredError"){ + throw new ApolloError('Team Invite Token Error: Token Expired. Please request new invitation or apply to the team again', 404) + } else { + throw new ApolloError (`Team Invite Token Error: - ${err.message}`, 404) + } + } + logger.info(`verifyInviteToken: ${verifiedToken}`) + return verifiedToken + }, + + async Teams_SendTeamCreateConfirmation(parent, { email, teamName, botName }, ctx, info){ + const data = JSON.stringify({ + "personalizations": [ + { + "to": [ + { + "email": email + } + ], + dynamic_template_data: { + "aadeveloplink": "https://develop.advancedalgos.net/index.html", + "aateamname": teamName, + "aabotname": botName, + "subject": "Team " + teamName + " has been created!" + }, + "subject": "Team " + teamName + " has been created!" + } + ], + "from": { + "email": "noreply@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "reply_to": { + "email": "feedback@advancedalgos.net", + "name": "Advanced Algos Teams" + }, + "template_id": process.env.SG_TEAMS_TEAMCREATE_EMAILID + }) + + let sendTeamCreation = axios({ + method: 'post', + url: 'https://api.sendgrid.com/v3/mail/send', + data: data, + headers:{ + 'content-type': 'application/json', + 'authorization': 'Bearer ' + API_KEY + } + }) + .then(function (response) { + if (response.status >= 200 && response.status < 300) { + return 'Success' + } else { + throw response.data.errors[0].message + } + }) + .catch(function (error) { + throw new ApolloError(`Team Creation Confirmation Error: ${error.response.data.errors[0].message}`, ${error.status}) + }) + return sendTeamCreation + } +} diff --git a/server/src/graphql/resolvers/Query/index.js b/server/src/graphql/resolvers/Query/index.js index c88d132c1..5861773ce 100644 --- a/server/src/graphql/resolvers/Query/index.js +++ b/server/src/graphql/resolvers/Query/index.js @@ -1,20 +1,6 @@ import { logger } from '../../../logger' -import { member, owner } from './Member' -import { teams, teamById, teamByName, teamBySlug, teamWithRole, teamsByOwner, teamsByRole } from './Team' -import{ fbByTeamMember } from './TeamMember' - export const resolvers = { Query: { - member: (parent, arg, ctx, info) => member(parent, arg, ctx, info), - teams: (parent, arg, ctx, info) => teams(parent, arg, ctx, info), - teamById: (parent, arg, ctx, info) => teamById(parent, arg, ctx, info), - teamByName: (parent, arg, ctx, info) => teamByName(parent, arg, ctx, info), - teamBySlug: (parent, arg, ctx, info) => teamBySlug(parent, arg, ctx, info), - teamWithRole: (parent, arg, ctx, info) => teamWithRole(parent, arg, ctx, info), - teamsByOwner: (parent, arg, ctx, info) => teamsByOwner(parent, arg, ctx, info), - teamsByRole: (parent, arg, ctx, info) => teamsByRole(parent, arg, ctx, info), - owner: (parent, arg, ctx, info) => owner(parent, arg, ctx, info), - fbByTeamMember: (parent, arg, ctx, info) => fbByTeamMember(parent, arg, ctx, info) } } diff --git a/server/src/graphql/resolvers/gateways/User/index.js b/server/src/graphql/resolvers/gateways/User/index.js deleted file mode 100644 index d88eaba8f..000000000 --- a/server/src/graphql/resolvers/gateways/User/index.js +++ /dev/null @@ -1,57 +0,0 @@ -import axios from 'axios' - -import { logger, DatabaseError } from '../logger' - -export const getMember = async (req, res, next, db) => { - if (!req.headers.userId) return next() - logger.info('getMember ') - logger.info(req.headers.userId) - try { - const member = await getUser(req.headers.userId) - req.user = await member - next() - } catch (err) { - next() - } - -} - -export const getUser = authId => { - logger.info('getUser MiddleWare') - logger.info(authId) - const API_URL = 'https://app-api.advancedalgos.net/graphql' - return new Promise((resolve, reject) => { - try { - return axios.post(API_URL, { - query: `query users_User($userId: ID!) { - users_User(id: $userId){ - id - alias - email - } - }`, - variables: { - userId: authId - } - }, { - headers: { - 'Content-Type': 'application/json' - } - }) - .then(res => { - logger.info('getUser result') - logger.info(res.data) - resolve(res.data) - }) - .catch(err => { - logger.info('getUser err') - logger.info(err) - reject(err) - }) - } catch (error) { - reject(error) - } - }) -} - -export default getUser diff --git a/server/src/graphql/resolvers/index.js b/server/src/graphql/resolvers/index.js index d8cbffce2..af4037629 100644 --- a/server/src/graphql/resolvers/index.js +++ b/server/src/graphql/resolvers/index.js @@ -1,6 +1,7 @@ -import merge from 'lodash.merge' +// import merge from 'lodash.merge' // import { resolvers as queryResolvers } from './Query' import { resolvers as mutationResolvers } from './Mutation' -export const resolvers = merge(mutationResolvers) +// export const resolvers = merge(mutationResolvers) +export const resolvers = mutationResolvers diff --git a/server/src/graphql/schema.graphql b/server/src/graphql/schema.graphql index 54559651e..1c2d434fe 100644 --- a/server/src/graphql/schema.graphql +++ b/server/src/graphql/schema.graphql @@ -1,48 +1,10 @@ -# import Query.TeamConnection, Team from "../generated/prisma.graphql" - -directive @isAuthenticated on QUERY | FIELD | MUTATION -directive @hasRole(roles: [String]) on QUERY | FIELD | MUTATION -directive @isOwner(type: String) on QUERY | MUTATION -directive @isOwnerOrHasRole(type: String, roles: [String]) on QUERY | MUTATION - -type Query { - teams: TeamConnection - teamDetails: [Team!]! @isAuthenticated - teamByName(name: String!): Team - teamBySlug(slug: String!): Team - teamById(teamId: String!): Team - teamWithRole(teamId: String!, role: String!): Team - teamsByOwner: [Team!]! @isAuthenticated - teamsByRole: [Team!]! @isAuthenticated - fbByTeamMember: Team @isAuthenticated - owner: Member @isAuthenticated - member: Member @isAuthenticated -} - type Mutation { - authenticate(idToken: String!): Member - verifyTeamInvite(token: String!): TeamMembers - createTeam(name: String!, slug: String!, botName: String!, botSlug: String!): Team @isAuthenticated - updateTeamProfile(slug: String!, description: String, motto: String, avatar: String, banner: String): Team @isAuthenticated - updateFB(fbId: String!, avatar: String): FinancialBeings @isAuthenticated - deleteTeam(slug: String!, botSlug: String!): Team @isAuthenticated @isOwner(type: "Team") - sendMemberInviteSG(email: String!, teamId: String!): String - confirmMember(token: String!): String - removeTeamMember(teamId: String!, memberId: String!): String -} - -fragment MemberInfo on Member { - id - authId - alias - visible - status { - ...MemberStatusInfo - } -} - -fragment MemberStatusInfo on MemberStatus { - status - reason - createdAt + Corporate_NewsletterSignup(email: String!): String + Corporate_NewsletterSignupVerify(token: String!): String + Corporate_Contact(email: String!, subject: String!, Message: String!, recaptcha: String!): String + Master_NewsletterSignup(email: String!): String + Master_NewsletterSignupVerify(token: String!): String + Master_Contact(email: String!, subject: String!, Message: String!, recaptcha: String!): String + Teams_SendTeamMemberInvite(email: String!, teamName: String!): String + Teams_SendTeamCreateConfirmation(email: String!, teamName: String!, botName: String!): String } diff --git a/server/src/middleware/getMember.js b/server/src/middleware/getMember.js deleted file mode 100644 index dfa7511bf..000000000 --- a/server/src/middleware/getMember.js +++ /dev/null @@ -1,23 +0,0 @@ -import axios from 'axios' - -import { logger, DatabaseError } from '../logger' - -export const getMember = async (req, res, next, db) => { - if (!req.headers.userId) return next() - logger.debug('getMember ') - logger.debug(req.headers.userId) - try { - const member = await getUser(req.headers.userId) - req.user = await member - logger.debug('getMember - getUser:') - logger.debug(req.user) - next() - } catch (err) { - logger.debug('getMember - error:') - logger.debug(err) - next() - } - -} - -export default getMember