diff --git a/api/.babelrc b/api/.babelrc index 1320b9a32..81bfbad3f 100644 --- a/api/.babelrc +++ b/api/.babelrc @@ -1,3 +1,7 @@ { - "presets": ["@babel/preset-env"] + "presets": ["@babel/preset-env"], + "plugins": [ + "@babel/plugin-proposal-throw-expressions", + ["@babel/plugin-proposal-class-properties", { "loose": true }] + ] } diff --git a/api/package-lock.json b/api/package-lock.json index d5e74e85c..8292250ea 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -575,6 +575,20 @@ "@babel/plugin-syntax-async-generators": "^7.0.0" } }, + "@babel/plugin-proposal-class-properties": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.1.0.tgz", + "integrity": "sha512-/PCJWN+CKt5v1xcGn4vnuu13QDoV+P7NcICP44BoonAJoPSGwVkgrXihFIQGiEjjPlUDBIw1cM7wYFLARS2/hw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-member-expression-to-functions": "^7.0.0", + "@babel/helper-optimise-call-expression": "^7.0.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-replace-supers": "^7.1.0", + "@babel/plugin-syntax-class-properties": "^7.0.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz", @@ -602,6 +616,16 @@ "@babel/plugin-syntax-optional-catch-binding": "^7.0.0" } }, + "@babel/plugin-proposal-throw-expressions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-throw-expressions/-/plugin-proposal-throw-expressions-7.0.0.tgz", + "integrity": "sha512-CA2EUiwnbXrsdV4hy3jYghm91WaL7zL7xYfu628dyItRr6gylbRxshghGEK/Hhm//rR58N3PBmEeuYqSW57IUQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-throw-expressions": "^7.0.0" + } + }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz", @@ -620,6 +644,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-class-properties": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0.tgz", + "integrity": "sha512-cR12g0Qzn4sgkjrbrzWy2GE7m9vMl/sFkqZ3gIpAQdrvPDnLM8180i+ANDFIXfjHo9aqp0ccJlQ0QNZcFUbf9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz", @@ -644,6 +677,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-throw-expressions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.0.0.tgz", + "integrity": "sha512-/5uORdWlPta/ALhI5zKtm0Y9vAYOa7HJMML0OnCGk9XZA4hpGjb0Xjt/OVDCJVawC/4FrlAGCHOaj9BtWeVDvg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz", diff --git a/api/package.json b/api/package.json index 057991f3d..f50720958 100644 --- a/api/package.json +++ b/api/package.json @@ -37,7 +37,10 @@ "winston": "^3.1.0" }, "devDependencies": { + "@babel/plugin-proposal-class-properties": "^7.1.0", + "@babel/plugin-proposal-throw-expressions": "^7.0.0", "graphql-cli": "^2.16.5", + "lodash.merge": "^4.6.1", "npm-run-all": "4.1.3", "prisma": "^1.18.1" } diff --git a/api/src/auth/validateIdToken.js b/api/src/auth/validateIdToken.js deleted file mode 100644 index dd0b794b3..000000000 --- a/api/src/auth/validateIdToken.js +++ /dev/null @@ -1,28 +0,0 @@ -const jwksClient = require('jwks-rsa') -const jwt = require('jsonwebtoken') -const jwks = jwksClient({ - cache: true, - rateLimit: true, - jwksRequestsPerMinute: 1, - jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json` -}) - -const validateIdToken = idToken => - new Promise((resolve, reject) => { - const { header, payload } = jwt.decode(idToken, { complete: true }) - if (!header || !header.kid || !payload) reject(new Error('Invalid Token')) - jwks.getSigningKey(header.kid, (err, key) => { - if (err) reject({ error: `Error getting signing key: ${err.message}` }) - jwt.verify( - idToken, - key.publicKey, - { algorithms: ['RS256'] }, - (err, decoded) => { - if (err) reject('jwt verify error: ' + err.message) - resolve(decoded) - } - ) - }) - }) - -module.exports = { validateIdToken } diff --git a/api/src/directives/index.js b/api/src/directives/index.js index b686e7bae..b775edb7d 100644 --- a/api/src/directives/index.js +++ b/api/src/directives/index.js @@ -1,9 +1,6 @@ import { SchemaDirectiveVisitor } from 'apollo-server-express' import _get from 'lodash.get' -import { logger } from '../logger' - -import { AuthenticationError, ForbiddenError } from '../errors' -import { validateIdToken } from '../auth/validateIdToken' +import { logger, AuthenticationError } from '../logger' const memberLocationOnContext = 'request.user' const userIdOnContext = 'req.userid' @@ -70,7 +67,7 @@ export const directiveResolvers = { if (roles.includes(role)) { return next() } - throw new ForbiddenError('Insufficient permissions') + throw new AuthenticationError('Insufficient permissions') }, isOwner: async (next, source, { type }, ctx) => { logger.info('directive isOwner: ', source, type, ctx) @@ -86,7 +83,7 @@ export const directiveResolvers = { if (isOwner) { return next() } - throw new ForbiddenError('Insufficient permissions') + throw new AuthenticationError('Insufficient permissions') }, isOwnerOrHasRole: async (next, source, { roles, type }, ctx, ...p) => { const userId = await isUserId(ctx) @@ -117,7 +114,7 @@ export const directiveResolvers = { if(hasRole){ return next() } - throw new ForbiddenError('Insufficient permissions') + throw new AuthenticationError('Insufficient permissions') } } diff --git a/api/src/graphql/TeamFBFragment.js b/api/src/graphql/fragments/TeamFBFragment.js similarity index 100% rename from api/src/graphql/TeamFBFragment.js rename to api/src/graphql/fragments/TeamFBFragment.js diff --git a/api/src/graphql/TeamsConnectionsFragment.js b/api/src/graphql/fragments/TeamsConnectionsFragment.js similarity index 100% rename from api/src/graphql/TeamsConnectionsFragment.js rename to api/src/graphql/fragments/TeamsConnectionsFragment.js diff --git a/api/src/graphql/TeamsFragment.js b/api/src/graphql/fragments/TeamsFragment.js similarity index 100% rename from api/src/graphql/TeamsFragment.js rename to api/src/graphql/fragments/TeamsFragment.js diff --git a/api/src/graphql/resolvers/Mutation/Member.js b/api/src/graphql/resolvers/Mutation/Member.js new file mode 100644 index 000000000..653764d4c --- /dev/null +++ b/api/src/graphql/resolvers/Mutation/Member.js @@ -0,0 +1,17 @@ +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/api/src/graphql/resolvers/Mutation/Team.js b/api/src/graphql/resolvers/Mutation/Team.js new file mode 100644 index 000000000..e69de29bb diff --git a/api/src/graphql/resolvers/Mutation/TeamMember.js b/api/src/graphql/resolvers/Mutation/TeamMember.js new file mode 100644 index 000000000..e69de29bb diff --git a/api/src/graphql/resolvers/Mutation/index.js b/api/src/graphql/resolvers/Mutation/index.js new file mode 100644 index 000000000..9f33592c6 --- /dev/null +++ b/api/src/graphql/resolvers/Mutation/index.js @@ -0,0 +1,143 @@ +import { getUser } from '../../../middleware/getMember' + +import { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } from '../../..//email/sendgrid' + +import TEAM_FB_FRAGMENT from '../../fragments/TeamFBFragment' + +import { logger } from '../../../logger' + +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 Error(err.message) + } + return team + }, + async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) { + logger.info('createTeam ctx.request.user:') + logger.info(ctx.request.user) + + const authId = ctx.userId + + return getUser(authId) + .then(async result => { + logger.info('axios:') + logger.info(result) + + const alias = result.data.users_User.alias + const email = result.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://develop.advancedalgos.net/AABrowserAPI/teamSetup/' + // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' + + logger.info(`${platformUrl}${createTeamUrl}/${authId}`) + const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}`) + .then((result) => { + console.log('createPlatformTeam result:', result.data) + if(result.data.message === 'Team Name already taken'){ + return result.data.message + } + if(result.data.result === 'Fail'){ + return result.data.message + } + return result + }) + .catch(err =>{ + logger.debug('createPlatformTeam err:') + logger.debug(err) + throw new Error(err) + }) + console.log('createPlatformTeam returned: ', await createPlatformTeam) + if(await createPlatformTeam === 'Team Name already taken'){ + return createPlatformTeam + } + + const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`) + .catch((err) => { + logger.debug(err, 'existingMember error: ') + return err + }) + + logger.info('existingMember:') + logger.info(await existingMember) + + let createTeam + if(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 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 err + }) + } + + // sendTeamCreateConfirmation(email, name, botName) + + return createTeam + }) + .catch((err) => { + logger.debug('createTeam error: ') + return err + }) + }, + 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) + return 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) + return err + }) + }, + async deleteTeam(parent, { slug }, ctx, info) { + return ctx.db.mutation.deleteTeam({ where: { slug } }, info) + .catch((res) => { + logger.info('deleteTeam error: ', res) + const errors = res.graphQLErrors.map((error) => { + return error.message + }) + }) + } + } +} diff --git a/api/src/graphql/resolvers/Query/Member.js b/api/src/graphql/resolvers/Query/Member.js new file mode 100644 index 000000000..2d1a02060 --- /dev/null +++ b/api/src/graphql/resolvers/Query/Member.js @@ -0,0 +1,29 @@ +import { logger, AuthentificationError, DatabaseError } from '../../../logger' + +export const member = async (parent, arg, ctx, info) => { + logger.info(ctx.request.userId, ' --> resolver.member') + const authId = ctx.request.userId + if (!authId) { + throw new AuthentificationError() + } + return ctx.db.query.member({ where: { authId: arg.authId } }, info) + .catch(err => { + logger.debug('resolver.member error: ') + logger.debug(err) + throw new DatabaseError(`Member Data Error: ${err.message}`) + }) +} + +export const owner = async (parent, args, ctx, info) => { + logger.info('resolver.query.owner ctx: ') + const authId = ctx.request.userId + if (!authId) { + return throw new AuthentificationError() + } + return ctx.db.query.member({ where: { authId: authId } }, info) + .catch(err => { + logger.debug('resolver.owner error: ') + logger.debug(err) + throw new DatabaseError(`Member Data Error: ${err.message}`) + }) +} diff --git a/api/src/graphql/resolvers/Query/Team.js b/api/src/graphql/resolvers/Query/Team.js new file mode 100644 index 000000000..6e92c83dd --- /dev/null +++ b/api/src/graphql/resolvers/Query/Team.js @@ -0,0 +1,45 @@ +import { logger } from '../../../logger' + +import TEAMS_CONNECTIONS_FRAGMENT from '../../fragments/TeamsConnectionsFragment' +import TEAMS_FRAGMENT from '../../fragments/TeamsFragment' + +export const teams = async (parent, args, ctx, info) => { + logger.info('teams: ', ctx.request.res) + return ctx.db.query.teamsConnection({}, TEAMS_CONNECTIONS_FRAGMENT) +} + +export const teamById = async (parent, { id }, ctx, info) => { + return ctx.db.query.team({ where: { id } }, info) +} + +export const teamByName = async (parent, { name }, ctx, info) => { + return ctx.db.query.team({ where: { name: name } }, `{ name }`) +} + +export const teamWithRole = async (parent, { teamId, role }, ctx, info) => { + return ctx.db.query.teamsConnection({where: {AND: [{id: teamId},{members_some:{role: role}}]}, first:1}, info) +} + +export const teamsByOwner = async (parent, { ownerId }, ctx, info) => { + logger.info('teamsByOwner') + logger.info(ctx.req.headers) + logger.info(ctx.req.token) + return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) +} + +export const teamsByRole = async (parent, args, ctx, info) => { + const authId = ctx.request.userId + let teamAdmin + try { + teamAdmin= await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}, role_not:'MEMBER'}]}}}, TEAMS_FRAGMENT) + } catch (err) { + logger.debug('teamsByRole error: ') + logger.debug(err) + const errors = res.graphQLErrors.map((error) => { + return error.message + }) + return errors + } + logger.info('teamsByRole teamAdmin: ', teamAdmin) + return teamAdmin +} diff --git a/api/src/graphql/resolvers/Query/TeamMember.js b/api/src/graphql/resolvers/Query/TeamMember.js new file mode 100644 index 000000000..76ef6c9c7 --- /dev/null +++ b/api/src/graphql/resolvers/Query/TeamMember.js @@ -0,0 +1,9 @@ +import { logger } from '../../../logger' + +export const fbByTeamMember = async (parent, args, ctx, info) => { + logger.info('fbByTeamMember: ', args, ctx.request, ctx.request.res.req.user) + const authId = ctx.request.userId + let teamMemberFB = await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}}]}}, orderBy: 'updatedAt_DESC'}, TEAM_FB_FRAGMENT) + logger.info('fbByTeamMember response: ', await teamMemberFB[0]) + return teamMemberFB[0] +} diff --git a/api/src/graphql/resolvers/Query/index.js b/api/src/graphql/resolvers/Query/index.js new file mode 100644 index 000000000..5616f8cbb --- /dev/null +++ b/api/src/graphql/resolvers/Query/index.js @@ -0,0 +1,19 @@ +import { logger } from '../../../logger' + +import { member, owner } from './Member' +import { teams, teamById, teamByName, 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), + 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/api/src/graphql/resolvers/index.js b/api/src/graphql/resolvers/index.js new file mode 100644 index 000000000..70fae750a --- /dev/null +++ b/api/src/graphql/resolvers/index.js @@ -0,0 +1,6 @@ +import merge from 'lodash.merge' + +import { resolvers as queryResolvers } from './Query' +import { resolvers as mutationResolvers } from './Mutation' + +const resolvers = merge(queryResolvers, mutationResolvers) diff --git a/api/src/schema.graphql b/api/src/graphql/schema.graphql similarity index 80% rename from api/src/schema.graphql rename to api/src/graphql/schema.graphql index 2be3053e3..6f889a058 100644 --- a/api/src/schema.graphql +++ b/api/src/graphql/schema.graphql @@ -1,4 +1,4 @@ -# import Query.TeamConnection, Team from "./generated/prisma.graphql" +# import Query.TeamConnection, Team from "../generated/prisma.graphql" directive @isAuthenticated on QUERY | FIELD | MUTATION directive @hasRole(roles: [String]) on QUERY | FIELD | MUTATION @@ -15,8 +15,7 @@ type Query { teamsByRole: [Team!]! @isAuthenticated fbByTeamMember: Team @isAuthenticated owner: Member @isAuthenticated - currentMember: Member @isAuthenticated - member(authId: String!): Member @isAuthenticated + member: Member @isAuthenticated } type Mutation { @@ -35,18 +34,14 @@ fragment MemberInfo on Member { id authId alias - isActive - profile { - ...MemberProfileInfo + visible + status { + ...MemberStatusInfo } } -fragment MemberProfileInfo on MemberProfile { - firstName - lastName - email - avatar - role { - name - } +fragment MemberStatusInfo on MemberStatus { + status + reason + createdAt } diff --git a/api/src/index.js b/api/src/index.js index d7e90c76a..ce59f2e96 100644 --- a/api/src/index.js +++ b/api/src/index.js @@ -13,17 +13,10 @@ import { importSchema } from 'graphql-import' import parser from 'fast-xml-parser' import axios from 'axios' -import checkJwt from './auth/middleware/jwt' -import { getMember, getUser } from './auth/middleware/getMember' -import { validateIdToken } from './auth/validateIdToken' +import { resolvers } from './graphql/resolvers' + import { schemaDirectives } from './directives' -import { sendTeamMemberInvite, sendTeamCreateConfirmation, verifyInviteToken } from './email/sendgrid' - -import TEAMS_FRAGMENT from './graphql/TeamsFragment' -import TEAMS_CONNECTIONS_FRAGMENT from './graphql/TeamsConnectionsFragment' -import TEAM_FB_FRAGMENT from './graphql/TeamFBFragment' - import pubsub from './pubsub' import { logger } from './logger' @@ -32,249 +25,6 @@ const GRAPHQL_SUBSCRIPTIONS = '/graphql' const PORT = 4001 const NODE_ENV = 'development' -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 -} - -const ctxMember = ctx => ctx.user - -const resolvers = { - Query: { - async member(parent, arg, ctx, info) { - logger.info('member: ', parent, arg) - return ctx.db.query.member({ where: { authId: arg.authId } }, info) - }, - async currentMember(parent, args, ctx, info) { - logger.info('currentMember: ', ctx.request.res.req.user, ctx.token) - const authId = ctx.request.res.req.user.sub - - return ctx.db.query.member({ where: { authId } }, info) - }, - teams(parent, args, ctx, info) { - logger.info('teams: ', ctx.request.res) - return ctx.db.query.teamsConnection({}, TEAMS_CONNECTIONS_FRAGMENT) - }, - async teamById(parent, { id }, ctx, info) { - return ctx.db.query.team({ where: { id } }, info) - }, - async teamByName(parent, { name }, ctx, info) { - return ctx.db.query.team({ where: { name: name } }, `{ name }`) - }, - async teamWithRole(parent, { teamId, role }, ctx, info) { - return ctx.db.query.teamsConnection({where: {AND: [{id: teamId},{members_some:{role: role}}]}, first:1}, info) - }, - async teamsByOwner(parent, { ownerId }, ctx, info) { - logger.info('teamsByOwner') - logger.info(ctx.req.headers) - logger.info(ctx.req.token) - return ctx.db.query.teams({where: { owner: ownerId }, orderBy:'updatedAt_DESC'}, TEAMS_FRAGMENT) - }, - async teamsByRole(parent, args, ctx, info) { - const authId = ctx.request.userId - let teamAdmin - try { - teamAdmin= await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}, role_not:'MEMBER'}]}}}, TEAMS_FRAGMENT) - } catch (err) { - logger.debug('teamsByRole error: ') - logger.debug(err) - const errors = res.graphQLErrors.map((error) => { - return error.message - }) - return errors - } - logger.info('teamsByRole teamAdmin: ', teamAdmin) - return teamAdmin - }, - async fbByTeamMember(parent, args, ctx, info) { - logger.info('fbByTeamMember: ', args, ctx.request, ctx.request.res.req.user) - const authId = ctx.request.userId - let teamMemberFB = await ctx.db.query.teams({where: {members_some: {AND: [{member: {authId: authId}}]}}, orderBy: 'updatedAt_DESC'}, TEAM_FB_FRAGMENT) - logger.info('fbByTeamMember response: ', await teamMemberFB[0]) - return teamMemberFB[0] - }, - async owner(parent, args, ctx, info) { - logger.info('resolver.query.owner ctx: ', ctxMember(ctx)) - return ctx.db.query.member({ where: { id: ctxMember(ctx).authId } }, info) - .catch(err => { - logger.debug('createTeam error: ', err) - const errors = res.graphQLErrors.map((error) => { - return error.message - }) - }) - } - }, - Mutation: { - async authenticate(parent, { idToken }, ctx, info) { - let memberToken = null - try { - memberToken = await validateIdToken(idToken) - logger.info('authenticate.memberToken: ', await memberToken) - } catch (err) { - logger.debug('authenticat.validateIdToken err: ', err) - throw new Error(err.message) - } - const authId = ctx.userId - let member = await ctx.db.query.member({ where: { authId } }, info) - - if (!member) { - try { - return createMember(ctx, info, authId) - } catch (error) { - throw new Error(error.message) - } - } - return member - }, - 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 Error(err.message) - } - return team - }, - async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) { - logger.info('createTeam ctx.request.user:') - logger.info(ctx.request.user) - - const authId = ctx.userId - - return getUser(authId) - .then(async result => { - logger.info('axios:') - logger.info(result) - - const alias = result.data.users_User.alias - const email = result.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://develop.advancedalgos.net/AABrowserAPI/teamSetup/' - // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' - - logger.info(`${platformUrl}${createTeamUrl}/${authId}`) - const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}`) - .then((result) => { - console.log('createPlatformTeam result:', result.data) - if(result.data.message === 'Team Name already taken'){ - return result.data.message - } - if(result.data.result === 'Fail'){ - return result.data.message - } - return result - }) - .catch(err =>{ - logger.debug('createPlatformTeam err:') - logger.debug(err) - throw new Error(err) - }) - console.log('createPlatformTeam returned: ', await createPlatformTeam) - if(await createPlatformTeam === 'Team Name already taken'){ - return createPlatformTeam - } - - const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`) - .catch((err) => { - logger.debug(err, 'existingMember error: ') - return err - }) - - logger.info('existingMember:') - logger.info(await existingMember) - - let createTeam - if(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 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 err - }) - } - - // sendTeamCreateConfirmation(email, name, botName) - - return createTeam - }) - .catch((err) => { - logger.debug('createTeam error: ') - return err - }) - }, - 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) - return 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) - return err - }) - }, - async deleteTeam(parent, { slug }, ctx, info) { - return ctx.db.mutation.deleteTeam({ where: { slug } }, info) - .catch((res) => { - logger.info('deleteTeam error: ', res) - const errors = res.graphQLErrors.map((error) => { - return error.message - }) - }) - } - } -} - - - const db = new Prisma({ fragmentReplacements: extractFragmentReplacements(resolvers), typeDefs: 'src/generated/prisma.graphql', @@ -285,24 +35,7 @@ const db = new Prisma({ const app = express() -const connectionMiddlewares = [ - checkJwt, - function (err, req, res, next) { - logger.info('checkJwt: ') - logger.error(err) - logger.info(req.headers) - logger.info(req.user) - if (err) { - const error = err.message ? err.message : err - return res.status(201).send(error) - } else { - next() - } - }, - (req, res, done) => getMember(req, res, done, db) -] - -app.post(GRAPHQL_ENDPOINT, ...connectionMiddlewares) +app.post(GRAPHQL_ENDPOINT) const corsOptions = { "origin": "*", @@ -322,9 +55,8 @@ const options = { const server = createApolloServer(app, { graphqlEndpoint: GRAPHQL_ENDPOINT, subscriptionsEndpoint: GRAPHQL_SUBSCRIPTIONS, - wsMiddlewares: connectionMiddlewares, apolloServerOptions: { formatError }, - typeDefs: importSchema('src/schema.graphql'), + typeDefs: importSchema('src/graphql/schema.graphql'), resolvers, resolverValidationOptions: { requireResolversForResolveType: false }, schemaDirectives, @@ -343,5 +75,3 @@ server.listen({ port: PORT }, () => { `🚀 GraphQL Server is running on http://localhost:${PORT}${GRAPHQL_ENDPOINT} in "${NODE_ENV}" mode\n` ) }) - -module.exports= { createMember } diff --git a/api/src/auth/middleware/getMember.js b/api/src/middleware/getMember.js similarity index 96% rename from api/src/auth/middleware/getMember.js rename to api/src/middleware/getMember.js index 8d212e5c9..871a7e2eb 100644 --- a/api/src/auth/middleware/getMember.js +++ b/api/src/middleware/getMember.js @@ -1,4 +1,4 @@ -import { logger } from '../../logger' +import { logger } from '../logger' export const getMember = async (req, res, next, db) => { if (!req.headers.userId) return next() diff --git a/api/src/auth/middleware/jwt.js b/api/src/middleware/jwt.js similarity index 100% rename from api/src/auth/middleware/jwt.js rename to api/src/middleware/jwt.js