Refactors teams
parent
c03144c343
commit
88332fb2ab
|
@ -2,10 +2,14 @@ import { z } from "zod";
|
|||
|
||||
import { _TeamModel as Team } from "@calcom/prisma/zod";
|
||||
|
||||
export const schemaTeamBaseBodyParams = Team.omit({ id: true }).partial();
|
||||
export const schemaTeamBaseBodyParams = Team.omit({ id: true }).partial({ hideBranding: true });
|
||||
|
||||
const schemaTeamRequiredParams = z.object({});
|
||||
|
||||
export const schemaTeamBodyParams = schemaTeamBaseBodyParams.merge(schemaTeamRequiredParams);
|
||||
export const schemaTeamBodyParams = schemaTeamBaseBodyParams.merge(schemaTeamRequiredParams).strict();
|
||||
|
||||
export const schemaTeamUpdateBodyParams = schemaTeamBodyParams.partial();
|
||||
|
||||
export const schemaTeamReadPublic = Team.omit({});
|
||||
|
||||
export const schemaTeamsReadPublic = z.array(schemaTeamReadPublic);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { schemaQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
|
||||
async function authMiddleware(req: NextApiRequest) {
|
||||
const { userId, prisma, isAdmin } = req;
|
||||
const { teamId } = schemaQueryTeamId.parse(req.query);
|
||||
/** Admins can skip the ownership verification */
|
||||
if (isAdmin) return;
|
||||
/** Non-members will see a 404 error which may or not be the desired behavior. */
|
||||
await prisma.team.findFirstOrThrow({
|
||||
where: { id: teamId, members: { some: { userId } } },
|
||||
});
|
||||
}
|
||||
|
||||
export default authMiddleware;
|
|
@ -1,4 +1,4 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
@ -22,36 +22,22 @@ import { schemaQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
|||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team removed successfuly
|
||||
* description: OK, team removed successfully
|
||||
* 400:
|
||||
* description: Bad request. Team id is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
export async function deleteHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { prisma, isAdmin, userId } = req;
|
||||
|
||||
const query = schemaQueryTeamId.parse(req.query);
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
export async function deleteHandler(req: NextApiRequest) {
|
||||
const { prisma, query, userId } = req;
|
||||
const { teamId } = schemaQueryTeamId.parse(query);
|
||||
/** Only OWNERS can delete teams */
|
||||
const _team = await prisma.team.findFirst({
|
||||
where: { id: teamId, members: { some: { userId, role: "OWNER" } } },
|
||||
});
|
||||
const userTeamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
// Here we only check for ownership of the user if the user is not admin, otherwise we let ADMIN's edit any user
|
||||
if (!isAdmin && !userTeamIds.includes(query.teamId))
|
||||
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
|
||||
await prisma.team
|
||||
.delete({ where: { id: query.teamId } })
|
||||
.then(() =>
|
||||
res.status(200).json({
|
||||
message: `Team with id: ${query.teamId} deleted successfully`,
|
||||
})
|
||||
)
|
||||
.catch((error: Error) =>
|
||||
res.status(404).json({
|
||||
message: `Team with id: ${query.teamId} not found`,
|
||||
error,
|
||||
})
|
||||
);
|
||||
if (!_team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER required" });
|
||||
await prisma.team.delete({ where: { id: teamId } });
|
||||
return { message: `Team with id: ${teamId} deleted successfully` };
|
||||
}
|
||||
|
||||
export default defaultResponder(deleteHandler);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { Prisma } from "@prisma/client";
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
|
@ -31,18 +31,12 @@ import { schemaTeamReadPublic } from "@lib/validations/team";
|
|||
*/
|
||||
export async function getHandler(req: NextApiRequest) {
|
||||
const { prisma, isAdmin, userId } = req;
|
||||
|
||||
const query = schemaQueryTeamId.parse(req.query);
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
});
|
||||
const userTeamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
// Here we only check for ownership of the user if the user is not admin, otherwise we let ADMIN's edit any user
|
||||
if (!isAdmin && !userTeamIds.includes(query.teamId))
|
||||
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
|
||||
const data = await prisma.team.findUnique({ where: { id: query.teamId } });
|
||||
const team = schemaTeamReadPublic.parse(data);
|
||||
return { team };
|
||||
const { teamId } = schemaQueryTeamId.parse(req.query);
|
||||
const where: Prisma.TeamWhereInput = { id: teamId };
|
||||
// Non-admins can only query the teams they're part of
|
||||
if (!isAdmin) where.members = { some: { userId } };
|
||||
const data = await prisma.team.findFirstOrThrow({ where });
|
||||
return { team: schemaTeamReadPublic.parse(data) };
|
||||
}
|
||||
|
||||
export default defaultResponder(getHandler);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
import { schemaTeamBodyParams, schemaTeamReadPublic } from "@lib/validations/team";
|
||||
import { schemaTeamReadPublic, schemaTeamUpdateBodyParams } from "@lib/validations/team";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
|
@ -23,35 +23,23 @@ import { schemaTeamBodyParams, schemaTeamReadPublic } from "@lib/validations/tea
|
|||
* - teams
|
||||
* responses:
|
||||
* 201:
|
||||
* description: OK, team edited successfuly
|
||||
* description: OK, team edited successfully
|
||||
* 400:
|
||||
* description: Bad request. Team body is invalid.
|
||||
* 401:
|
||||
* description: Authorization information is missing or invalid.
|
||||
*/
|
||||
export async function patchHandler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { prisma, isAdmin, userId, body } = req;
|
||||
const safeBody = schemaTeamBodyParams.safeParse(body);
|
||||
|
||||
const query = schemaQueryTeamId.parse(req.query);
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: { userId: userId },
|
||||
export async function patchHandler(req: NextApiRequest) {
|
||||
const { prisma, body, userId } = req;
|
||||
const data = schemaTeamUpdateBodyParams.parse(body);
|
||||
const { teamId } = schemaQueryTeamId.parse(req.query);
|
||||
/** Only OWNERS and ADMINS can edit teams */
|
||||
const _team = await prisma.team.findFirst({
|
||||
where: { id: teamId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } },
|
||||
});
|
||||
const userTeamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
// Here we only check for ownership of the user if the user is not admin, otherwise we let ADMIN's edit any user
|
||||
if (!isAdmin || !userTeamIds.includes(query.teamId))
|
||||
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
|
||||
if (!safeBody.success) {
|
||||
{
|
||||
res.status(400).json({ message: "Invalid request body" });
|
||||
return;
|
||||
}
|
||||
}
|
||||
const data = await prisma.team.update({ where: { id: query.teamId }, data: safeBody.data });
|
||||
if (!data) throw new HttpError({ statusCode: 404, message: `Team with id: ${query.teamId} not found` });
|
||||
const team = schemaTeamReadPublic.parse(data);
|
||||
if (!team) throw new HttpError({ statusCode: 401, message: `Your request body wasn't valid` });
|
||||
return { team };
|
||||
if (!_team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER or ADMIN required" });
|
||||
const team = await prisma.team.update({ where: { id: teamId }, data });
|
||||
return { team: schemaTeamReadPublic.parse(team) };
|
||||
}
|
||||
|
||||
export default defaultResponder(patchHandler);
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import { defaultHandler } from "@calcom/lib/server";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { withMiddleware } from "@lib/helpers/withMiddleware";
|
||||
import { withValidQueryTeamId } from "@lib/validations/shared/queryTeamId";
|
||||
|
||||
export default withMiddleware()(
|
||||
withValidQueryTeamId(
|
||||
defaultHandler({
|
||||
import authMiddleware from "./_auth-middleware";
|
||||
|
||||
export default withMiddleware("HTTP_GET_DELETE_PATCH")(
|
||||
defaultResponder(async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
await authMiddleware(req);
|
||||
return defaultHandler({
|
||||
GET: import("./_get"),
|
||||
PATCH: import("./_patch"),
|
||||
DELETE: import("./_delete"),
|
||||
})
|
||||
)
|
||||
})(req, res);
|
||||
})
|
||||
);
|
||||
|
|
|
@ -3,7 +3,7 @@ import type { NextApiRequest } from "next";
|
|||
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaTeamReadPublic } from "@lib/validations/team";
|
||||
import { schemaTeamsReadPublic } from "@lib/validations/team";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
|
@ -23,20 +23,11 @@ import { schemaTeamReadPublic } from "@lib/validations/team";
|
|||
*/
|
||||
async function getHandler(req: NextApiRequest) {
|
||||
const { userId, prisma, isAdmin } = req;
|
||||
const membershipWhere: Prisma.MembershipWhereInput = {};
|
||||
const where: Prisma.TeamWhereInput = {};
|
||||
// If user is not ADMIN, return only his data.
|
||||
if (!isAdmin) membershipWhere.userId = userId;
|
||||
const userWithMemberships = await prisma.membership.findMany({
|
||||
where: membershipWhere,
|
||||
});
|
||||
const teamIds = userWithMemberships.map((membership) => membership.teamId);
|
||||
const teamWhere: Prisma.TeamWhereInput = {};
|
||||
|
||||
if (!isAdmin) teamWhere.id = { in: teamIds };
|
||||
|
||||
const data = await prisma.team.findMany({ where: teamWhere });
|
||||
const teams = schemaTeamReadPublic.parse(data);
|
||||
return { teams };
|
||||
if (!isAdmin) where.members = { some: { userId } };
|
||||
const data = await prisma.team.findMany({ where });
|
||||
return { teams: schemaTeamsReadPublic.parse(data) };
|
||||
}
|
||||
|
||||
export default defaultResponder(getHandler);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import type { NextApiRequest } from "next";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import { schemaMembershipPublic } from "@lib/validations/membership";
|
||||
|
@ -24,22 +23,22 @@ import { schemaTeamBodyParams, schemaTeamReadPublic } from "@lib/validations/tea
|
|||
*/
|
||||
async function postHandler(req: NextApiRequest) {
|
||||
const { prisma, body, userId } = req;
|
||||
const safe = schemaTeamBodyParams.safeParse(body);
|
||||
if (!safe.success) throw new HttpError({ statusCode: 400, message: "Invalid request body" });
|
||||
const data = await prisma.team.create({ data: safe.data });
|
||||
// We're also creating the relation membership of team ownership in this call.
|
||||
const owner = await prisma.membership
|
||||
.create({
|
||||
data: { userId, teamId: data.id, role: "OWNER", accepted: true },
|
||||
})
|
||||
.then((owner) => schemaMembershipPublic.parse(owner));
|
||||
const team = schemaTeamReadPublic.parse(data);
|
||||
if (!team) throw new HttpError({ statusCode: 400, message: "We were not able to create your team" });
|
||||
const data = schemaTeamBodyParams.parse(body);
|
||||
const team = await prisma.team.create({
|
||||
data: {
|
||||
...data,
|
||||
members: {
|
||||
// We're also creating the relation membership of team ownership in this call.
|
||||
create: { userId, role: "OWNER", accepted: true },
|
||||
},
|
||||
},
|
||||
include: { members: true },
|
||||
});
|
||||
req.statusCode = 201;
|
||||
// We are also returning the new ownership relation as owner besides team.
|
||||
return {
|
||||
team,
|
||||
owner,
|
||||
team: schemaTeamReadPublic.parse(team),
|
||||
owner: schemaMembershipPublic.parse(team.members[0]),
|
||||
message: "Team created successfully, we also made you the owner of this team",
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue