mirror of
https://github.com/documenso/documenso.git
synced 2025-11-11 21:12:48 +10:00
feat: add team user management endpoints to api (#1416)
## Description Adds user management capabilities to our current API. Allows for adding, removing, listing and updating members of a given team using a valid API token. ## Related Issue N/A ## Changes Made - Added an endpoint for inviting a team member - Added an endpoint for removing a team member - Added an endpoint for updating a team member - Added an endpoint for listing team members ## Testing Performed Tests were written for this feature request
This commit is contained in:
@ -26,6 +26,8 @@ import { getRecipientById } from '@documenso/lib/server-only/recipient/get-recip
|
||||
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
||||
import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document';
|
||||
import { updateRecipient } from '@documenso/lib/server-only/recipient/update-recipient';
|
||||
import { createTeamMemberInvites } from '@documenso/lib/server-only/team/create-team-member-invites';
|
||||
import { deleteTeamMembers } from '@documenso/lib/server-only/team/delete-team-members';
|
||||
import type { CreateDocumentFromTemplateResponse } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||
import { createDocumentFromTemplateLegacy } from '@documenso/lib/server-only/template/create-document-from-template-legacy';
|
||||
@ -49,7 +51,12 @@ import {
|
||||
} from '@documenso/lib/universal/upload/server-actions';
|
||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentDataType, DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
||||
import {
|
||||
DocumentDataType,
|
||||
DocumentStatus,
|
||||
SigningStatus,
|
||||
TeamMemberRole,
|
||||
} from '@documenso/prisma/client';
|
||||
|
||||
import { ApiContractV1 } from './contract';
|
||||
import { authenticatedMiddleware } from './middleware/authenticated';
|
||||
@ -1279,4 +1286,270 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
findTeamMembers: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: teamId } = args.params;
|
||||
|
||||
if (team?.id !== Number(teamId)) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const self = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const members = await prisma.teamMember.findMany({
|
||||
where: {
|
||||
teamId: team.id,
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
members: members.map((member) => ({
|
||||
id: member.id,
|
||||
email: member.user.email,
|
||||
role: member.role,
|
||||
})),
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
inviteTeamMember: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: teamId } = args.params;
|
||||
|
||||
const { email, role } = args.body;
|
||||
|
||||
if (team?.id !== Number(teamId)) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const self = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const hasAlreadyBeenInvited = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
teamId: team.id,
|
||||
user: {
|
||||
email,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (hasAlreadyBeenInvited) {
|
||||
return {
|
||||
status: 400,
|
||||
body: {
|
||||
message: 'This user has already been invited to the team',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
await createTeamMemberInvites({
|
||||
userId: user.id,
|
||||
userName: user.name ?? '',
|
||||
teamId: team.id,
|
||||
invitations: [
|
||||
{
|
||||
email,
|
||||
role,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
message: 'An invite has been sent to the member',
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
updateTeamMember: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: teamId, memberId } = args.params;
|
||||
|
||||
const { role } = args.body;
|
||||
|
||||
if (team?.id !== Number(teamId)) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const self = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const member = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
id: Number(memberId),
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!member) {
|
||||
return {
|
||||
status: 404,
|
||||
body: {
|
||||
message: 'The provided member id does not exist.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const updatedMember = await prisma.teamMember.update({
|
||||
where: {
|
||||
id: member.id,
|
||||
},
|
||||
data: {
|
||||
role,
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
id: updatedMember.id,
|
||||
email: updatedMember.user.email,
|
||||
role: updatedMember.role,
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
removeTeamMember: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: teamId, memberId } = args.params;
|
||||
|
||||
if (team?.id !== Number(teamId)) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const self = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (self?.role !== TeamMemberRole.ADMIN) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You are not authorized to perform actions against this team.',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const member = await prisma.teamMember.findFirst({
|
||||
where: {
|
||||
id: Number(memberId),
|
||||
teamId: Number(teamId),
|
||||
},
|
||||
include: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!member) {
|
||||
return {
|
||||
status: 404,
|
||||
body: {
|
||||
message: 'Member not found',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (team.ownerUserId === member.userId) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You cannot remove the owner of the team',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (member.userId === user.id) {
|
||||
return {
|
||||
status: 403,
|
||||
body: {
|
||||
message: 'You cannot remove yourself from the team',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
await deleteTeamMembers({
|
||||
userId: user.id,
|
||||
teamId: team.id,
|
||||
teamMemberIds: [member.id],
|
||||
});
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
id: member.id,
|
||||
email: member.user.email,
|
||||
role: member.role,
|
||||
},
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user