From 0421dd401a5265dd00a40a691d1d5a6197c1565a Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 2 Feb 2024 12:27:17 -0300 Subject: [PATCH] feat: API wrapper for invite members. (#13496) * chore: API * chore: remove * chore: Update --------- Co-authored-by: Keith Williams --- apps/api/pages/api/invites/_post.ts | 63 +++++++++++++++++++++++++++++ apps/api/pages/api/invites/index.ts | 9 +++++ 2 files changed, 72 insertions(+) create mode 100644 apps/api/pages/api/invites/_post.ts create mode 100644 apps/api/pages/api/invites/index.ts diff --git a/apps/api/pages/api/invites/_post.ts b/apps/api/pages/api/invites/_post.ts new file mode 100644 index 0000000000..4b54e369ff --- /dev/null +++ b/apps/api/pages/api/invites/_post.ts @@ -0,0 +1,63 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import { HttpError } from "@calcom/lib/http-error"; +import { defaultResponder } from "@calcom/lib/server"; +import { createContext } from "@calcom/trpc/server/createContext"; +import { viewerTeamsRouter } from "@calcom/trpc/server/routers/viewer/teams/_router"; +import type { TInviteMemberInputSchema } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.schema"; +import { ZInviteMemberInputSchema } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/inviteMember.schema"; + +import { TRPCError } from "@trpc/server"; +import { getHTTPStatusCodeFromError } from "@trpc/server/http"; + +async function postHandler(req: NextApiRequest, res: NextApiResponse) { + const data = ZInviteMemberInputSchema.parse(req.body); + await checkPermissions(req, data); + + async function sessionGetter() { + return { + user: { + id: req.userId, + username: "", + }, + hasValidLicense: true, + expires: "", + }; + } + + const ctx = await createContext({ req, res }, sessionGetter); + try { + const caller = viewerTeamsRouter.createCaller(ctx); + await caller.inviteMember({ + role: data.role, + language: data.language, + teamId: data.teamId, + usernameOrEmail: data.usernameOrEmail, + isOrg: data.isOrg, + }); + + return { success: true, message: `${data.usernameOrEmail} has been invited.` }; + } catch (cause) { + if (cause instanceof TRPCError) { + const statusCode = getHTTPStatusCodeFromError(cause); + throw new HttpError({ statusCode, message: cause.message }); + } + + throw cause; + } +} + +async function checkPermissions(req: NextApiRequest, body: TInviteMemberInputSchema) { + const { userId, isAdmin, prisma } = req; + if (isAdmin) return; + // To prevent auto-accepted invites, limit it to ADMIN users + if (!isAdmin && "accepted" in body) + throw new HttpError({ statusCode: 403, message: "ADMIN needed for `accepted`" }); + // Only team OWNERS and ADMINS can add other members + const membership = await prisma.membership.findFirst({ + where: { userId, teamId: body.teamId, role: { in: ["ADMIN", "OWNER"] } }, + }); + if (!membership) throw new HttpError({ statusCode: 403, message: "You can't add members to this team" }); +} + +export default defaultResponder(postHandler); diff --git a/apps/api/pages/api/invites/index.ts b/apps/api/pages/api/invites/index.ts new file mode 100644 index 0000000000..3ccafb7e55 --- /dev/null +++ b/apps/api/pages/api/invites/index.ts @@ -0,0 +1,9 @@ +import { defaultHandler } from "@calcom/lib/server"; + +import { withMiddleware } from "~/lib/helpers/withMiddleware"; + +export default withMiddleware()( + defaultHandler({ + POST: import("./_post"), + }) +);