Files
speckle-server/packages/server/modules/serverinvites/graph/resolvers/serverInvites.ts
T
Kristaps Fabians Geikins ee5ae8af62 fix(fe2): accept invite before onboarding after sign up (#2491)
* explicitly ordering global middlewares

* various subscription fixes & WIP project invite middleware

* SSR invite accept & toast notifs seem to work

* backend support for mixpanel

* mixpanel be logic -> shared

* minor fix

* finissh

* lint fix

* minor comment adjustments

* better adblock handling
2024-07-11 11:45:11 +03:00

254 lines
8.0 KiB
TypeScript

import { Roles } from '@/modules/core/helpers/mainConstants'
import { removePrivateFields } from '@/modules/core/helpers/userHelper'
import { InviteCreateValidationError } from '@/modules/serverinvites/errors'
import {
buildUserTarget,
ResourceTargets
} from '@/modules/serverinvites/helpers/inviteHelper'
import {
createAndSendInviteFactory,
resendInviteEmailFactory
} from '@/modules/serverinvites/services/inviteCreationService'
import {
createStreamInviteAndNotifyFactory,
useStreamInviteAndNotifyFactory
} from '@/modules/serverinvites/services/management'
import {
cancelStreamInviteFactory,
resendInviteFactory,
deleteInviteFactory,
finalizeStreamInviteFactory
} from '@/modules/serverinvites/services/inviteProcessingService'
import {
getServerInviteForTokenFactory,
getUserPendingStreamInviteFactory,
getUserPendingStreamInvitesFactory
} from '@/modules/serverinvites/services/inviteRetrievalService'
import { authorizeResolver } from '@/modules/shared'
import { chunk } from 'lodash'
import { Resolvers } from '@/modules/core/graph/generated/graphql'
import db from '@/db/knex'
import { ServerRoles, StreamRoles } from '@speckle/shared'
import {
deleteInvitesByTargetFactory,
deleteStreamInviteFactory,
findInviteFactory,
findResourceFactory,
findServerInviteFactory,
findStreamInviteFactory,
findUserByTargetFactory,
insertInviteAndDeleteOldFactory,
queryAllUserStreamInvitesFactory,
deleteInviteFactory as deleteInviteFromDbFactory
} from '@/modules/serverinvites/repositories/serverInvites'
export = {
Query: {
async streamInvite(_parent, args, context) {
const { streamId, token } = args
return getUserPendingStreamInviteFactory({
findStreamInvite: findStreamInviteFactory({ db })
})(streamId, context.userId, token)
},
async projectInvite(_parent, args, context) {
const { projectId, token } = args
return await getUserPendingStreamInviteFactory({
findStreamInvite: findStreamInviteFactory({ db })
})(projectId, context.userId, token)
},
async streamInvites(_parent, _args, context) {
const { userId } = context
return getUserPendingStreamInvitesFactory({
queryAllUserStreamInvites: queryAllUserStreamInvitesFactory({
db
})
})(userId!)
},
async serverInviteByToken(_parent, args) {
const { token } = args
return getServerInviteForTokenFactory({
findServerInvite: findServerInviteFactory({ db })
})(token)
}
},
ServerInvite: {
async invitedBy(parent, _args, ctx) {
const { invitedById } = parent
if (!invitedById) return null
const user = await ctx.loaders.users.getUser.load(invitedById)
return user ? removePrivateFields(user) : null
}
},
Mutation: {
async serverInviteCreate(_parent, args, context) {
await createAndSendInviteFactory({
findResource: findResourceFactory(),
findUserByTarget: findUserByTargetFactory(),
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db })
})(
{
target: args.input.email,
inviterId: context.userId!,
message: args.input.message,
serverRole: args.input.serverRole as null | undefined | ServerRoles
},
context.resourceAccessRules
)
return true
},
async streamInviteCreate(_parent, args, context) {
await authorizeResolver(
context.userId,
args.input.streamId,
Roles.Stream.Owner,
context.resourceAccessRules
)
await createStreamInviteAndNotifyFactory({
createAndSendInvite: createAndSendInviteFactory({
findResource: findResourceFactory(),
findUserByTarget: findUserByTargetFactory(),
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({
db
})
})
})(args.input, context.userId!, context.resourceAccessRules)
return true
},
async serverInviteBatchCreate(_parent, args, context) {
const { input: paramsArray } = args
const inviteCount = paramsArray.length
if (inviteCount > 10 && context.role !== Roles.Server.Admin) {
throw new InviteCreateValidationError(
'Maximum 10 invites can be sent at once by non admins'
)
}
// Batch calls so that we don't kill the server
const batches = chunk(paramsArray, 50)
for (const paramsBatchArray of batches) {
await Promise.all(
paramsBatchArray.map((params) =>
createAndSendInviteFactory({
findResource: findResourceFactory(),
findUserByTarget: findUserByTargetFactory(),
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({
db
})
})(
{
target: params.email,
inviterId: context.userId!,
message: params.message,
serverRole: params.serverRole as ServerRoles | null | undefined
},
context.resourceAccessRules
)
)
)
}
return true
},
async streamInviteBatchCreate(_parent, args, context) {
const { input: paramsArray } = args
// Validate params
for (const params of paramsArray) {
const { email, userId } = params
if (!email && !userId) {
throw new InviteCreateValidationError(
'Either email or userId must be specified'
)
}
}
// Batch calls so that we don't kill the server
const batches = chunk(paramsArray, 50)
for (const paramsBatchArray of batches) {
await Promise.all(
paramsBatchArray.map((params) => {
const { email, userId, message, streamId, role, serverRole } = params
const target = (userId ? buildUserTarget(userId) : email)!
return createAndSendInviteFactory({
findResource: findResourceFactory(),
findUserByTarget: findUserByTargetFactory(),
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({
db
})
})(
{
target,
inviterId: context.userId!,
message,
resourceTarget: ResourceTargets.Streams,
resourceId: streamId,
role: (role as unknown as StreamRoles) || Roles.Stream.Contributor,
serverRole: serverRole as ServerRoles | null | undefined
},
context.resourceAccessRules
)
})
)
}
return true
},
async streamInviteUse(_parent, args, ctx) {
await useStreamInviteAndNotifyFactory({
finalizeStreamInvite: finalizeStreamInviteFactory({
findStreamInvite: findStreamInviteFactory({ db }),
deleteInvitesByTarget: deleteInvitesByTargetFactory({ db }),
findResource: findResourceFactory()
})
})(args, ctx.userId!, ctx.resourceAccessRules)
return true
},
async streamInviteCancel(_parent, args, ctx) {
const { streamId, inviteId } = args
const { userId, resourceAccessRules } = ctx
await authorizeResolver(userId, streamId, Roles.Stream.Owner, resourceAccessRules)
await cancelStreamInviteFactory({
findStreamInvite: findStreamInviteFactory({ db }),
deleteStreamInvite: deleteStreamInviteFactory({ db })
})(streamId, inviteId)
return true
},
async inviteResend(_parent, args) {
const { inviteId } = args
await resendInviteFactory({
findInvite: findInviteFactory({ db }),
resendInviteEmail: resendInviteEmailFactory({
findResource: findResourceFactory(),
findUserByTarget: findUserByTargetFactory()
})
})(inviteId)
return true
},
async inviteDelete(_parent, args) {
const { inviteId } = args
await deleteInviteFactory({
findInvite: findInviteFactory({ db }),
deleteInvite: deleteInviteFromDbFactory({ db })
})(inviteId)
return true
}
}
} as Resolvers