feat: add organisation template type (#2611)

This commit is contained in:
Ephraim Duncan
2026-03-16 14:29:34 +00:00
committed by GitHub
parent 943a0b50e3
commit 36bbd97514
41 changed files with 1702 additions and 279 deletions
+69 -1
View File
@@ -1,9 +1,17 @@
import { type DocumentDataType, DocumentStatus } from '@prisma/client';
import {
type DocumentDataType,
DocumentStatus,
type EnvelopeType,
type TemplateType,
} from '@prisma/client';
import { EnvelopeType as EnvelopeTypeEnum, TemplateType as TemplateTypeEnum } from '@prisma/client';
import contentDisposition from 'content-disposition';
import { type Context } from 'hono';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { sha256 } from '@documenso/lib/universal/crypto';
import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server';
import { prisma } from '@documenso/prisma';
import type { HonoEnv } from '../../router';
@@ -79,3 +87,63 @@ export const handleEnvelopeItemFileRequest = async ({
return c.body(file);
};
type CheckEnvelopeFileAccessOptions = {
userId: number;
teamId: number;
envelopeType: EnvelopeType;
templateType: TemplateType;
};
/**
* Check whether a user has access to an envelope's file.
*
* First checks team membership. If that fails and the envelope is an
* ORGANISATION template (not a document), falls back to checking whether
* the user belongs to any team in the same organisation.
*/
export const checkEnvelopeFileAccess = async ({
userId,
teamId,
envelopeType,
templateType,
}: CheckEnvelopeFileAccessOptions): Promise<boolean> => {
const team = await getTeamById({ userId, teamId }).catch(() => null);
if (team) {
return true;
}
if (
envelopeType === EnvelopeTypeEnum.TEMPLATE &&
templateType === TemplateTypeEnum.ORGANISATION
) {
const orgAccess = await prisma.team.findFirst({
where: {
id: teamId,
organisation: {
teams: {
some: {
teamGroups: {
some: {
organisationGroup: {
organisationGroupMembers: {
some: {
organisationMember: { userId },
},
},
},
},
},
},
},
},
},
select: { id: true },
});
return orgAccess !== null;
}
return false;
};
+10 -15
View File
@@ -6,13 +6,12 @@ import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session
import { APP_DOCUMENT_UPLOAD_SIZE_LIMIT } from '@documenso/lib/constants/app';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import { putNormalizedPdfFileServerSide } from '@documenso/lib/universal/upload/put-file.server';
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
import { prisma } from '@documenso/prisma';
import type { HonoEnv } from '../../router';
import { handleEnvelopeItemFileRequest } from './files.helpers';
import { checkEnvelopeFileAccess, handleEnvelopeItemFileRequest } from './files.helpers';
import {
type TGetPresignedPostUrlResponse,
ZGetEnvelopeItemFileDownloadRequestParamsSchema,
@@ -119,16 +118,14 @@ export const filesRoute = new Hono<HonoEnv>()
return c.json({ error: 'Envelope item not found' }, 404);
}
const team = await getTeamById({
userId: userId,
const hasAccess = await checkEnvelopeFileAccess({
userId,
teamId: envelope.teamId,
}).catch((error) => {
console.error(error);
return null;
envelopeType: envelope.type,
templateType: envelope.templateType,
});
if (!team) {
if (!hasAccess) {
return c.json(
{ error: 'User does not have access to the team that this envelope is associated with' },
403,
@@ -187,16 +184,14 @@ export const filesRoute = new Hono<HonoEnv>()
return c.json({ error: 'Envelope item not found' }, 404);
}
const team = await getTeamById({
const hasDownloadAccess = await checkEnvelopeFileAccess({
userId: session.user.id,
teamId: envelope.teamId,
}).catch((error) => {
console.error(error);
return null;
envelopeType: envelope.type,
templateType: envelope.templateType,
});
if (!team) {
if (!hasDownloadAccess) {
return c.json(
{ error: 'User does not have access to the team that this envelope is associated with' },
403,
@@ -5,13 +5,13 @@ import { z } from 'zod';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import { verifyEmbeddingPresignToken } from '@documenso/lib/server-only/embedding-presign/verify-embedding-presign-token';
import { getTeamById } from '@documenso/lib/server-only/team/get-team';
import type { DocumentDataVersion } from '@documenso/lib/types/document';
import { sha256 } from '@documenso/lib/universal/crypto';
import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server';
import { prisma } from '@documenso/prisma';
import type { HonoEnv } from '../../../router';
import { checkEnvelopeFileAccess } from '../files.helpers';
const route = new Hono<HonoEnv>();
@@ -67,7 +67,9 @@ route.get(
envelope: {
select: {
id: true,
type: true,
teamId: true,
templateType: true,
},
},
},
@@ -78,12 +80,14 @@ route.get(
}
// Check whether the user has access to the document.
const team = await getTeamById({
const hasAccess = await checkEnvelopeFileAccess({
userId,
teamId: envelopeItem.envelope.teamId,
}).catch(() => null);
envelopeType: envelopeItem.envelope.type,
templateType: envelopeItem.envelope.templateType,
});
if (!team) {
if (!hasAccess) {
return c.json({ error: 'Not found' }, 404);
}