mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 16:23:06 +10:00
feat: add organisations (#1820)
This commit is contained in:
@ -1,16 +1,14 @@
|
||||
import { TeamMemberRole } from '@prisma/client';
|
||||
import type { Team, TeamGlobalSettings } from '@prisma/client';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import type { TFolderType } from '../../types/folder-type';
|
||||
import { FolderType } from '../../types/folder-type';
|
||||
import { determineDocumentVisibility } from '../../utils/document-visibility';
|
||||
import { getTeamById } from '../team/get-team';
|
||||
import { getTeamSettings } from '../team/get-team-settings';
|
||||
|
||||
export interface CreateFolderOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
teamId: number;
|
||||
name: string;
|
||||
parentId?: string | null;
|
||||
type?: TFolderType;
|
||||
@ -23,52 +21,9 @@ export const createFolder = async ({
|
||||
parentId,
|
||||
type = FolderType.DOCUMENT,
|
||||
}: CreateFolderOptions) => {
|
||||
const user = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: userId,
|
||||
},
|
||||
include: {
|
||||
teamMembers: {
|
||||
select: {
|
||||
teamId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const team = await getTeamById({ userId, teamId });
|
||||
|
||||
if (
|
||||
teamId !== undefined &&
|
||||
!user.teamMembers.some((teamMember) => teamMember.teamId === teamId)
|
||||
) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Team not found',
|
||||
});
|
||||
}
|
||||
|
||||
let team: (Team & { teamGlobalSettings: TeamGlobalSettings | null }) | null = null;
|
||||
let userTeamRole: TeamMemberRole | undefined;
|
||||
|
||||
if (teamId) {
|
||||
const teamWithUserRole = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: teamId,
|
||||
},
|
||||
include: {
|
||||
teamGlobalSettings: true,
|
||||
members: {
|
||||
where: {
|
||||
userId: userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
team = teamWithUserRole;
|
||||
userTeamRole = teamWithUserRole.members[0]?.role;
|
||||
}
|
||||
const settings = await getTeamSettings({ userId, teamId });
|
||||
|
||||
return await prisma.folder.create({
|
||||
data: {
|
||||
@ -77,10 +32,7 @@ export const createFolder = async ({
|
||||
teamId,
|
||||
parentId,
|
||||
type,
|
||||
visibility: determineDocumentVisibility(
|
||||
team?.teamGlobalSettings?.documentVisibility,
|
||||
userTeamRole ?? TeamMemberRole.MEMBER,
|
||||
),
|
||||
visibility: determineDocumentVisibility(settings.documentVisibility, team.currentTeamRole),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -4,45 +4,16 @@ import { match } from 'ts-pattern';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { getTeamById } from '../team/get-team';
|
||||
|
||||
export interface DeleteFolderOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
teamId: number;
|
||||
folderId: string;
|
||||
}
|
||||
|
||||
export const deleteFolder = async ({ userId, teamId, folderId }: DeleteFolderOptions) => {
|
||||
let teamMemberRole: TeamMemberRole | null = null;
|
||||
|
||||
if (teamId) {
|
||||
const team = await prisma.team.findFirst({
|
||||
where: {
|
||||
id: teamId,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!team) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Team not found',
|
||||
});
|
||||
}
|
||||
|
||||
teamMemberRole = team.members[0]?.role ?? null;
|
||||
}
|
||||
const team = await getTeamById({ userId, teamId });
|
||||
|
||||
const folder = await prisma.folder.findFirst({
|
||||
where: {
|
||||
@ -63,18 +34,16 @@ export const deleteFolder = async ({ userId, teamId, folderId }: DeleteFolderOpt
|
||||
});
|
||||
}
|
||||
|
||||
if (teamId && teamMemberRole) {
|
||||
const hasPermission = match(teamMemberRole)
|
||||
.with(TeamMemberRole.ADMIN, () => true)
|
||||
.with(TeamMemberRole.MANAGER, () => folder.visibility !== DocumentVisibility.ADMIN)
|
||||
.with(TeamMemberRole.MEMBER, () => folder.visibility === DocumentVisibility.EVERYONE)
|
||||
.otherwise(() => false);
|
||||
const hasPermission = match(team.currentTeamRole)
|
||||
.with(TeamMemberRole.ADMIN, () => true)
|
||||
.with(TeamMemberRole.MANAGER, () => folder.visibility !== DocumentVisibility.ADMIN)
|
||||
.with(TeamMemberRole.MEMBER, () => folder.visibility === DocumentVisibility.EVERYONE)
|
||||
.otherwise(() => false);
|
||||
|
||||
if (!hasPermission) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You do not have permission to delete this folder',
|
||||
});
|
||||
}
|
||||
if (!hasPermission) {
|
||||
throw new AppError(AppErrorCode.UNAUTHORIZED, {
|
||||
message: 'You do not have permission to delete this folder',
|
||||
});
|
||||
}
|
||||
|
||||
return await prisma.folder.delete({
|
||||
|
||||
@ -5,49 +5,19 @@ import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { DocumentVisibility } from '../../types/document-visibility';
|
||||
import type { TFolderType } from '../../types/folder-type';
|
||||
import { getTeamById } from '../team/get-team';
|
||||
|
||||
export interface FindFoldersOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
teamId: number;
|
||||
parentId?: string | null;
|
||||
type?: TFolderType;
|
||||
}
|
||||
|
||||
export const findFolders = async ({ userId, teamId, parentId, type }: FindFoldersOptions) => {
|
||||
let team = null;
|
||||
let teamMemberRole = null;
|
||||
const team = await getTeamById({ userId, teamId });
|
||||
|
||||
if (teamId !== undefined) {
|
||||
try {
|
||||
team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: teamId,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
teamMemberRole = team.members[0].role;
|
||||
} catch (error) {
|
||||
console.error('Error finding team:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const visibilityFilters = match(teamMemberRole)
|
||||
const visibilityFilters = match(team.currentTeamRole)
|
||||
.with(TeamMemberRole.ADMIN, () => ({
|
||||
visibility: {
|
||||
in: [
|
||||
@ -67,14 +37,12 @@ export const findFolders = async ({ userId, teamId, parentId, type }: FindFolder
|
||||
const whereClause = {
|
||||
AND: [
|
||||
{ parentId },
|
||||
teamId
|
||||
? {
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
}
|
||||
: { userId, teamId: null },
|
||||
{
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -94,7 +62,8 @@ export const findFolders = async ({ userId, teamId, parentId, type }: FindFolder
|
||||
prisma.folder.findMany({
|
||||
where: {
|
||||
parentId: folder.id,
|
||||
...(teamId ? { teamId, ...visibilityFilters } : { userId, teamId: null }),
|
||||
teamId,
|
||||
...visibilityFilters,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
@ -113,7 +82,8 @@ export const findFolders = async ({ userId, teamId, parentId, type }: FindFolder
|
||||
prisma.folder.count({
|
||||
where: {
|
||||
parentId: folder.id,
|
||||
...(teamId ? { teamId, ...visibilityFilters } : { userId, teamId: null }),
|
||||
teamId,
|
||||
...visibilityFilters,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@ -5,10 +5,11 @@ import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { DocumentVisibility } from '../../types/document-visibility';
|
||||
import type { TFolderType } from '../../types/folder-type';
|
||||
import { getTeamById } from '../team/get-team';
|
||||
|
||||
export interface GetFolderBreadcrumbsOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
teamId: number;
|
||||
folderId: string;
|
||||
type?: TFolderType;
|
||||
}
|
||||
@ -19,39 +20,9 @@ export const getFolderBreadcrumbs = async ({
|
||||
folderId,
|
||||
type,
|
||||
}: GetFolderBreadcrumbsOptions) => {
|
||||
let teamMemberRole = null;
|
||||
const team = await getTeamById({ userId, teamId });
|
||||
|
||||
if (teamId !== undefined) {
|
||||
try {
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: teamId,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
teamMemberRole = team.members[0].role;
|
||||
} catch (error) {
|
||||
console.error('Error finding team:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
const visibilityFilters = match(teamMemberRole)
|
||||
const visibilityFilters = match(team.currentTeamRole)
|
||||
.with(TeamMemberRole.ADMIN, () => ({
|
||||
visibility: {
|
||||
in: [
|
||||
@ -71,14 +42,10 @@ export const getFolderBreadcrumbs = async ({
|
||||
const whereClause = (folderId: string) => ({
|
||||
id: folderId,
|
||||
...(type ? { type } : {}),
|
||||
...(teamId
|
||||
? {
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
}
|
||||
: { userId, teamId: null }),
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
});
|
||||
|
||||
const breadcrumbs = [];
|
||||
|
||||
@ -6,49 +6,19 @@ import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { DocumentVisibility } from '../../types/document-visibility';
|
||||
import type { TFolderType } from '../../types/folder-type';
|
||||
import { getTeamById } from '../team/get-team';
|
||||
|
||||
export interface GetFolderByIdOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
teamId: number;
|
||||
folderId?: string;
|
||||
type?: TFolderType;
|
||||
}
|
||||
|
||||
export const getFolderById = async ({ userId, teamId, folderId, type }: GetFolderByIdOptions) => {
|
||||
let teamMemberRole = null;
|
||||
const team = await getTeamById({ userId, teamId });
|
||||
|
||||
if (teamId !== undefined) {
|
||||
try {
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: teamId,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
teamMemberRole = team.members[0].role;
|
||||
} catch (error) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Team not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const visibilityFilters = match(teamMemberRole)
|
||||
const visibilityFilters = match(team.currentTeamRole)
|
||||
.with(TeamMemberRole.ADMIN, () => ({
|
||||
visibility: {
|
||||
in: [
|
||||
@ -68,14 +38,10 @@ export const getFolderById = async ({ userId, teamId, folderId, type }: GetFolde
|
||||
const whereClause = {
|
||||
id: folderId,
|
||||
...(type ? { type } : {}),
|
||||
...(teamId
|
||||
? {
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
}
|
||||
: { userId, teamId: null }),
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
};
|
||||
|
||||
const folder = await prisma.folder.findFirst({
|
||||
|
||||
@ -7,9 +7,11 @@ import { FolderType } from '@documenso/lib/types/folder-type';
|
||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { getTeamById } from '../team/get-team';
|
||||
|
||||
export interface MoveDocumentToFolderOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
teamId: number;
|
||||
documentId: number;
|
||||
folderId?: string | null;
|
||||
requestMetadata?: ApiRequestMetadata;
|
||||
@ -21,41 +23,9 @@ export const moveDocumentToFolder = async ({
|
||||
documentId,
|
||||
folderId,
|
||||
}: MoveDocumentToFolderOptions) => {
|
||||
let teamMemberRole = null;
|
||||
const team = await getTeamById({ userId, teamId });
|
||||
|
||||
if (teamId !== undefined) {
|
||||
try {
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: teamId,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
members: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
teamMemberRole = team.members[0].role;
|
||||
} catch (error) {
|
||||
console.error('Error finding team:', error);
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Team not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const visibilityFilters = match(teamMemberRole)
|
||||
const visibilityFilters = match(team.currentTeamRole)
|
||||
.with(TeamMemberRole.ADMIN, () => ({
|
||||
visibility: {
|
||||
in: [
|
||||
@ -74,14 +44,10 @@ export const moveDocumentToFolder = async ({
|
||||
|
||||
const documentWhereClause = {
|
||||
id: documentId,
|
||||
...(teamId
|
||||
? {
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
}
|
||||
: { userId, teamId: null }),
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
};
|
||||
|
||||
const document = await prisma.document.findFirst({
|
||||
@ -98,14 +64,10 @@ export const moveDocumentToFolder = async ({
|
||||
const folderWhereClause = {
|
||||
id: folderId,
|
||||
type: FolderType.DOCUMENT,
|
||||
...(teamId
|
||||
? {
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
}
|
||||
: { userId, teamId: null }),
|
||||
OR: [
|
||||
{ teamId, ...visibilityFilters },
|
||||
{ userId, teamId },
|
||||
],
|
||||
};
|
||||
|
||||
const folder = await prisma.folder.findFirst({
|
||||
|
||||
Reference in New Issue
Block a user