feat: add create document beta endpoint (#1584)

This commit is contained in:
David Nguyen
2025-01-16 13:36:00 +11:00
committed by GitHub
parent 5750f2b477
commit 9e03747e43
47 changed files with 2624 additions and 1934 deletions

View File

@@ -4,6 +4,19 @@ import { DEFAULT_DOCUMENT_TIME_ZONE } from './time-zones';
export const DEFAULT_DOCUMENT_DATE_FORMAT = 'yyyy-MM-dd hh:mm a';
export const VALID_DATE_FORMAT_VALUES = [
DEFAULT_DOCUMENT_DATE_FORMAT,
'yyyy-MM-dd',
'dd/MM/yyyy hh:mm a',
'MM/dd/yyyy hh:mm a',
'yyyy-MM-dd HH:mm',
'yy-MM-dd hh:mm a',
'yyyy-MM-dd HH:mm:ss',
'MMMM dd, yyyy hh:mm a',
'EEEE, MMMM dd, yyyy hh:mm a',
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
] as const;
export const DATE_FORMATS = [
{
key: 'yyyy-MM-dd_hh:mm_a',
@@ -55,7 +68,11 @@ export const DATE_FORMATS = [
label: 'ISO 8601',
value: "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
},
];
] satisfies {
key: string;
label: string;
value: (typeof VALID_DATE_FORMAT_VALUES)[number];
}[];
export const convertToLocalSystemFormat = (
customText: string,

View File

@@ -0,0 +1,248 @@
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { normalizePdf as makeNormalizedPdf } from '@documenso/lib/server-only/pdf/normalize-pdf';
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { nanoid } from '@documenso/lib/universal/id';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import type { DocumentVisibility, TemplateMeta } from '@documenso/prisma/client';
import {
DocumentSource,
RecipientRole,
SendStatus,
SigningStatus,
WebhookTriggerEvents,
} from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
import type { TCreateDocumentV2Request } from '@documenso/trpc/server/document-router/schema';
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';
import type { TDocumentFormValues } from '../../types/document-form-values';
import {
ZWebhookDocumentSchema,
mapDocumentToWebhookDocumentPayload,
} from '../../types/webhook-payload';
import { getFile } from '../../universal/upload/get-file';
import { putPdfFile } from '../../universal/upload/put-file';
import { createDocumentAuthOptions, createRecipientAuthOptions } from '../../utils/document-auth';
import { determineDocumentVisibility } from '../../utils/document-visibility';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type CreateDocumentOptions = {
userId: number;
teamId?: number;
documentDataId: string;
normalizePdf?: boolean;
data: {
title: string;
externalId?: string;
visibility?: DocumentVisibility;
globalAccessAuth?: TDocumentAccessAuthTypes;
globalActionAuth?: TDocumentActionAuthTypes;
formValues?: TDocumentFormValues;
recipients: TCreateDocumentV2Request['recipients'];
};
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
requestMetadata: ApiRequestMetadata;
};
export const createDocumentV2 = async ({
userId,
teamId,
documentDataId,
normalizePdf,
data,
meta,
requestMetadata,
}: CreateDocumentOptions) => {
const { title, formValues } = data;
const team = teamId
? await prisma.team.findFirst({
where: {
id: teamId,
members: {
some: {
userId,
},
},
},
include: {
teamGlobalSettings: true,
members: {
where: {
userId: userId,
},
select: {
role: true,
},
},
},
})
: null;
if (teamId !== undefined && !team) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Team not found',
});
}
if (normalizePdf) {
const documentData = await prisma.documentData.findFirst({
where: {
id: documentDataId,
},
});
if (documentData) {
const buffer = await getFile(documentData);
const normalizedPdf = await makeNormalizedPdf(Buffer.from(buffer));
const newDocumentData = await putPdfFile({
name: title.endsWith('.pdf') ? title : `${title}.pdf`,
type: 'application/pdf',
arrayBuffer: async () => Promise.resolve(normalizedPdf),
});
// eslint-disable-next-line require-atomic-updates
documentDataId = newDocumentData.id;
}
}
const authOptions = createDocumentAuthOptions({
globalAccessAuth: data?.globalAccessAuth || null,
globalActionAuth: data?.globalActionAuth || null,
});
const recipientsHaveActionAuth = data.recipients?.some((recipient) => recipient.actionAuth);
// Check if user has permission to set the global action auth.
if (authOptions.globalActionAuth || recipientsHaveActionAuth) {
const isDocumentEnterprise = await isUserEnterprise({
userId,
teamId,
});
if (!isDocumentEnterprise) {
throw new AppError(AppErrorCode.UNAUTHORIZED, {
message: 'You do not have permission to set the action auth',
});
}
}
const visibility = determineDocumentVisibility(
team?.teamGlobalSettings?.documentVisibility,
team?.members[0].role ?? TeamMemberRole.MEMBER,
);
return await prisma.$transaction(async (tx) => {
const document = await tx.document.create({
data: {
title,
externalId: data.externalId,
documentDataId,
userId,
teamId,
authOptions,
visibility,
formValues,
source: DocumentSource.DOCUMENT,
documentMeta: {
create: {
...meta,
signingOrder: meta?.signingOrder || undefined,
emailSettings: meta?.emailSettings || undefined,
language: meta?.language || team?.teamGlobalSettings?.documentLanguage,
typedSignatureEnabled:
meta?.typedSignatureEnabled ?? team?.teamGlobalSettings?.typedSignatureEnabled,
},
},
},
});
await Promise.all(
(data.recipients || []).map(async (recipient) => {
const recipientAuthOptions = createRecipientAuthOptions({
accessAuth: recipient.accessAuth || null,
actionAuth: recipient.actionAuth || null,
});
await tx.recipient.create({
data: {
documentId: document.id,
name: recipient.name,
email: recipient.email,
role: recipient.role,
signingOrder: recipient.signingOrder,
token: nanoid(),
sendStatus: recipient.role === RecipientRole.CC ? SendStatus.SENT : SendStatus.NOT_SENT,
signingStatus:
recipient.role === RecipientRole.CC ? SigningStatus.SIGNED : SigningStatus.NOT_SIGNED,
authOptions: recipientAuthOptions,
fields: {
createMany: {
data: (recipient.fields || []).map((field) => ({
documentId: document.id,
type: field.type,
page: field.pageNumber,
positionX: field.pageX,
positionY: field.pageY,
width: field.width,
height: field.height,
customText: '',
inserted: false,
fieldMeta: field.fieldMeta,
})),
},
},
},
});
}),
);
// Todo: Is it necessary to create a full audit log with all fields and recipients audit logs?
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
documentId: document.id,
metadata: requestMetadata,
data: {
title,
source: {
type: DocumentSource.DOCUMENT,
},
},
}),
});
const createdDocument = await tx.document.findFirst({
where: {
id: document.id,
},
include: {
documentData: true,
documentMeta: true,
recipients: true,
fields: true,
},
});
if (!createdDocument) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Document not found',
});
}
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_CREATED,
data: ZWebhookDocumentSchema.parse(mapDocumentToWebhookDocumentPayload(createdDocument)),
userId,
teamId,
});
return createdDocument;
});
};

View File

@@ -6,7 +6,7 @@ import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-log
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import { DocumentSource, DocumentVisibility, WebhookTriggerEvents } from '@documenso/prisma/client';
import { DocumentSource, WebhookTriggerEvents } from '@documenso/prisma/client';
import type { Team, TeamGlobalSettings } from '@documenso/prisma/client';
import { TeamMemberRole } from '@documenso/prisma/client';
@@ -16,6 +16,7 @@ import {
} from '../../types/webhook-payload';
import { getFile } from '../../universal/upload/get-file';
import { putPdfFile } from '../../universal/upload/put-file';
import { determineDocumentVisibility } from '../../utils/document-visibility';
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
export type CreateDocumentOptions = {
@@ -88,25 +89,6 @@ export const createDocument = async ({
userTeamRole = teamWithUserRole.members[0]?.role;
}
const determineVisibility = (
globalVisibility: DocumentVisibility | null | undefined,
userRole: TeamMemberRole,
): DocumentVisibility => {
if (globalVisibility) {
return globalVisibility;
}
if (userRole === TeamMemberRole.ADMIN) {
return DocumentVisibility.ADMIN;
}
if (userRole === TeamMemberRole.MANAGER) {
return DocumentVisibility.MANAGER_AND_ABOVE;
}
return DocumentVisibility.EVERYONE;
};
if (normalizePdf) {
const documentData = await prisma.documentData.findFirst({
where: {
@@ -138,7 +120,7 @@ export const createDocument = async ({
documentDataId,
userId,
teamId,
visibility: determineVisibility(
visibility: determineDocumentVisibility(
team?.teamGlobalSettings?.documentVisibility,
userTeamRole ?? TeamMemberRole.MEMBER,
),

View File

@@ -1,9 +1,8 @@
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
import type { TFieldMetaSchema } from '@documenso/lib/types/field-meta';
import type { TFieldAndMeta } from '@documenso/lib/types/field-meta';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma';
import type { FieldType } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { canRecipientFieldsBeModified } from '../../utils/recipients';
@@ -12,16 +11,14 @@ export interface CreateDocumentFieldsOptions {
userId: number;
teamId?: number;
documentId: number;
fields: {
fields: (TFieldAndMeta & {
recipientId: number;
type: FieldType;
pageNumber: number;
pageX: number;
pageY: number;
width: number;
height: number;
fieldMeta?: TFieldMetaSchema;
}[];
})[];
requestMetadata: ApiRequestMetadata;
}

View File

@@ -1,7 +1,6 @@
'use server';
import { nanoid } from 'nanoid';
import type { z } from 'zod';
import {
DIRECT_TEMPLATE_RECIPIENT_EMAIL,
@@ -9,7 +8,6 @@ import {
} from '@documenso/lib/constants/direct-templates';
import { prisma } from '@documenso/prisma';
import type { Recipient } from '@documenso/prisma/client';
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
@@ -20,18 +18,12 @@ export type CreateTemplateDirectLinkOptions = {
directRecipientId?: number;
};
export const ZCreateTemplateDirectLinkResponseSchema = TemplateDirectLinkSchema;
export type TCreateTemplateDirectLinkResponse = z.infer<
typeof ZCreateTemplateDirectLinkResponseSchema
>;
export const createTemplateDirectLink = async ({
templateId,
userId,
teamId,
directRecipientId,
}: CreateTemplateDirectLinkOptions): Promise<TCreateTemplateDirectLinkResponse> => {
}: CreateTemplateDirectLinkOptions) => {
const template = await prisma.template.findFirst({
where: {
id: templateId,

View File

@@ -1,10 +1,8 @@
import { omit } from 'remeda';
import type { z } from 'zod';
import { nanoid } from '@documenso/lib/universal/id';
import { prisma } from '@documenso/prisma';
import type { Prisma } from '@documenso/prisma/client';
import { TemplateSchema } from '@documenso/prisma/generated/zod';
import type { TDuplicateTemplateMutationSchema } from '@documenso/trpc/server/template-router/schema';
export type DuplicateTemplateOptions = TDuplicateTemplateMutationSchema & {
@@ -12,15 +10,11 @@ export type DuplicateTemplateOptions = TDuplicateTemplateMutationSchema & {
teamId?: number;
};
export const ZDuplicateTemplateResponseSchema = TemplateSchema;
export type TDuplicateTemplateResponse = z.infer<typeof ZDuplicateTemplateResponseSchema>;
export const duplicateTemplate = async ({
templateId,
userId,
teamId,
}: DuplicateTemplateOptions): Promise<TDuplicateTemplateResponse> => {
}: DuplicateTemplateOptions) => {
const template = await prisma.template.findUnique({
where: {
id: templateId,

View File

@@ -1,5 +1,4 @@
import { match } from 'ts-pattern';
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import {
@@ -8,18 +7,9 @@ import {
TeamMemberRole,
type Template,
} from '@documenso/prisma/client';
import {
DocumentDataSchema,
FieldSchema,
RecipientSchema,
TeamSchema,
TemplateDirectLinkSchema,
TemplateMetaSchema,
TemplateSchema,
} from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { type FindResultResponse, ZFindResultResponse } from '../../types/search-params';
import { type FindResultResponse } from '../../types/search-params';
export type FindTemplatesOptions = {
userId: number;
@@ -29,36 +19,13 @@ export type FindTemplatesOptions = {
perPage?: number;
};
export const ZFindTemplatesResponseSchema = ZFindResultResponse.extend({
data: TemplateSchema.extend({
templateDocumentData: DocumentDataSchema,
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
fields: FieldSchema.array(),
recipients: RecipientSchema.array(),
templateMeta: TemplateMetaSchema.pick({
signingOrder: true,
distributionMethod: true,
}).nullable(),
directLink: TemplateDirectLinkSchema.pick({
token: true,
enabled: true,
}).nullable(),
}).array(), // Todo: openapi.
});
export type TFindTemplatesResponse = z.infer<typeof ZFindTemplatesResponseSchema>;
export type FindTemplateRow = TFindTemplatesResponse['data'][number];
export const findTemplates = async ({
userId,
teamId,
type,
page = 1,
perPage = 10,
}: FindTemplatesOptions): Promise<TFindTemplatesResponse> => {
}: FindTemplatesOptions) => {
const whereFilter: Prisma.TemplateWhereInput[] = [];
if (teamId === undefined) {
@@ -112,7 +79,6 @@ export const findTemplates = async ({
AND: whereFilter,
},
include: {
templateDocumentData: true,
team: {
select: {
id: true,

View File

@@ -1,15 +1,4 @@
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import {
DocumentDataSchema,
FieldSchema,
RecipientSchema,
TemplateDirectLinkSchema,
TemplateMetaSchema,
TemplateSchema,
UserSchema,
} from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
@@ -19,26 +8,7 @@ export type GetTemplateByIdOptions = {
teamId?: number;
};
export const ZGetTemplateByIdResponseSchema = TemplateSchema.extend({
directLink: TemplateDirectLinkSchema.nullable(),
templateDocumentData: DocumentDataSchema,
templateMeta: TemplateMetaSchema.nullable(),
recipients: RecipientSchema.array(),
fields: FieldSchema.array(),
user: UserSchema.pick({
id: true,
name: true,
email: true,
}),
});
export type TGetTemplateByIdResponse = z.infer<typeof ZGetTemplateByIdResponseSchema>;
export const getTemplateById = async ({
id,
userId,
teamId,
}: GetTemplateByIdOptions): Promise<TGetTemplateByIdResponse> => {
export const getTemplateById = async ({ id, userId, teamId }: GetTemplateByIdOptions) => {
const template = await prisma.template.findFirst({
where: {
id,

View File

@@ -1,7 +1,4 @@
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import { TemplateSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
@@ -11,15 +8,11 @@ export type MoveTemplateToTeamOptions = {
userId: number;
};
export const ZMoveTemplateToTeamResponseSchema = TemplateSchema;
export type TMoveTemplateToTeamResponse = z.infer<typeof ZMoveTemplateToTeamResponseSchema>;
export const moveTemplateToTeam = async ({
templateId,
teamId,
userId,
}: MoveTemplateToTeamOptions): Promise<TMoveTemplateToTeamResponse> => {
}: MoveTemplateToTeamOptions) => {
return await prisma.$transaction(async (tx) => {
const template = await tx.template.findFirst({
where: {

View File

@@ -1,9 +1,6 @@
'use server';
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
@@ -14,18 +11,12 @@ export type ToggleTemplateDirectLinkOptions = {
enabled: boolean;
};
export const ZToggleTemplateDirectLinkResponseSchema = TemplateDirectLinkSchema;
export type TToggleTemplateDirectLinkResponse = z.infer<
typeof ZToggleTemplateDirectLinkResponseSchema
>;
export const toggleTemplateDirectLink = async ({
templateId,
userId,
teamId,
enabled,
}: ToggleTemplateDirectLinkOptions): Promise<TToggleTemplateDirectLinkResponse> => {
}: ToggleTemplateDirectLinkOptions) => {
const template = await prisma.template.findFirst({
where: {
id: templateId,

View File

@@ -1,11 +1,8 @@
'use server';
import type { z } from 'zod';
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
import { prisma } from '@documenso/prisma';
import type { DocumentVisibility, Template, TemplateMeta } from '@documenso/prisma/client';
import { TemplateSchema } from '@documenso/prisma/generated/zod';
import { AppError, AppErrorCode } from '../../errors/app-error';
import type { TDocumentAccessAuthTypes, TDocumentActionAuthTypes } from '../../types/document-auth';
@@ -28,17 +25,13 @@ export type UpdateTemplateOptions = {
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
};
export const ZUpdateTemplateResponseSchema = TemplateSchema;
export type TUpdateTemplateResponse = z.infer<typeof ZUpdateTemplateResponseSchema>;
export const updateTemplate = async ({
userId,
teamId,
templateId,
meta = {},
data = {},
}: UpdateTemplateOptions): Promise<TUpdateTemplateResponse> => {
}: UpdateTemplateOptions) => {
const template = await prisma.template.findFirstOrThrow({
where: {
id: templateId,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
import { z } from 'zod';
import { FieldType } from '@documenso/prisma/client';
export const ZBaseFieldMeta = z.object({
label: z.string().optional(),
placeholder: z.string().optional(),
@@ -114,3 +116,53 @@ export type TFieldMetaNotOptionalSchema = z.infer<typeof ZFieldMetaNotOptionalSc
export const ZFieldMetaSchema = ZFieldMetaNotOptionalSchema.optional();
export type TFieldMetaSchema = z.infer<typeof ZFieldMetaSchema>;
export const ZFieldAndMetaSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal(FieldType.SIGNATURE),
// Do not use z.undefined(), or void since this will create an invalid schema for Speakeasy SDK generation.
fieldMeta: z.literal(undefined),
}),
z.object({
type: z.literal(FieldType.FREE_SIGNATURE),
fieldMeta: z.literal(undefined), // Same as above.
}),
z.object({
type: z.literal(FieldType.INITIALS),
fieldMeta: ZInitialsFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.NAME),
fieldMeta: ZNameFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.EMAIL),
fieldMeta: ZEmailFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.DATE),
fieldMeta: ZDateFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.TEXT),
fieldMeta: ZTextFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.NUMBER),
fieldMeta: ZNumberFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.RADIO),
fieldMeta: ZRadioFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.CHECKBOX),
fieldMeta: ZCheckboxFieldMeta.optional(),
}),
z.object({
type: z.literal(FieldType.DROPDOWN),
fieldMeta: ZDropdownFieldMeta.optional(),
}),
]);
export type TFieldAndMeta = z.infer<typeof ZFieldAndMetaSchema>;

View File

@@ -1,3 +1,5 @@
import { z } from 'zod';
import { FieldSchema } from '@documenso/prisma/generated/zod';
/**
@@ -28,3 +30,22 @@ export const ZFieldSchema = FieldSchema.pick({
inserted: true,
fieldMeta: true,
});
export const ZFieldPageNumberSchema = z
.number()
.min(1)
.describe('The page number the field will be on.');
export const ZFieldPageXSchema = z
.number()
.min(0)
.describe('The X coordinate of where the field will be placed.');
export const ZFieldPageYSchema = z
.number()
.min(0)
.describe('The Y coordinate of where the field will be placed.');
export const ZFieldWidthSchema = z.number().min(1).describe('The width of the field.');
export const ZFieldHeightSchema = z.number().min(1).describe('The height of the field.');

View File

@@ -0,0 +1,119 @@
import type { z } from 'zod';
import {
DocumentDataSchema,
TeamSchema,
TemplateDirectLinkSchema,
TemplateMetaSchema,
TemplateSchema,
UserSchema,
} from '@documenso/prisma/generated/zod';
import { ZFieldSchema } from './field';
import { ZRecipientLiteSchema } from './recipient';
/**
* The full template response schema.
*
* Mainly used for returning a single template from the API.
*/
export const ZTemplateSchema = TemplateSchema.pick({
type: true,
visibility: true,
id: true,
externalId: true,
title: true,
userId: true,
teamId: true,
authOptions: true,
templateDocumentDataId: true,
createdAt: true,
updatedAt: true,
publicTitle: true,
publicDescription: true,
}).extend({
// Todo: Maybe we want to alter this a bit since this returns a lot of data.
templateDocumentData: DocumentDataSchema.pick({
type: true,
id: true,
data: true,
initialData: true,
}),
templateMeta: TemplateMetaSchema.pick({
id: true,
subject: true,
message: true,
timezone: true,
dateFormat: true,
signingOrder: true,
typedSignatureEnabled: true,
distributionMethod: true,
templateId: true,
redirectUrl: true,
language: true,
emailSettings: true,
}).nullable(),
directLink: TemplateDirectLinkSchema.nullable(),
user: UserSchema.pick({
id: true,
name: true,
email: true,
}),
recipients: ZRecipientLiteSchema.array(),
fields: ZFieldSchema.array(),
});
export type TTemplate = z.infer<typeof ZTemplateSchema>;
/**
* A lite version of the template response schema without relations.
*/
export const ZTemplateLiteSchema = TemplateSchema.pick({
type: true,
visibility: true,
id: true,
externalId: true,
title: true,
userId: true,
teamId: true,
authOptions: true,
templateDocumentDataId: true,
createdAt: true,
updatedAt: true,
publicTitle: true,
publicDescription: true,
});
/**
* A version of the template response schema when returning multiple template at once from a single API endpoint.
*/
export const ZTemplateManySchema = TemplateSchema.pick({
type: true,
visibility: true,
id: true,
externalId: true,
title: true,
userId: true,
teamId: true,
authOptions: true,
templateDocumentDataId: true,
createdAt: true,
updatedAt: true,
publicTitle: true,
publicDescription: true,
}).extend({
team: TeamSchema.pick({
id: true,
url: true,
}).nullable(),
fields: ZFieldSchema.array(),
recipients: ZRecipientLiteSchema.array(),
templateMeta: TemplateMetaSchema.pick({
signingOrder: true,
distributionMethod: true,
}).nullable(),
directLink: TemplateDirectLinkSchema.pick({
token: true,
enabled: true,
}).nullable(),
});

View File

@@ -0,0 +1,20 @@
import { DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
export const determineDocumentVisibility = (
globalVisibility: DocumentVisibility | null | undefined,
userRole: TeamMemberRole,
): DocumentVisibility => {
if (globalVisibility) {
return globalVisibility;
}
if (userRole === TeamMemberRole.ADMIN) {
return DocumentVisibility.ADMIN;
}
if (userRole === TeamMemberRole.MANAGER) {
return DocumentVisibility.MANAGER_AND_ABOVE;
}
return DocumentVisibility.EVERYONE;
};