mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 08:42:12 +10:00
feat: add create document beta endpoint (#1584)
This commit is contained in:
@ -7,8 +7,10 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { DOCUMENSO_ENCRYPTION_KEY } from '@documenso/lib/constants/crypto';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { encryptSecondaryData } from '@documenso/lib/server-only/crypto/encrypt';
|
||||
import { createDocumentData } from '@documenso/lib/server-only/document-data/create-document-data';
|
||||
import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta';
|
||||
import { createDocument } from '@documenso/lib/server-only/document/create-document';
|
||||
import { createDocumentV2 } from '@documenso/lib/server-only/document/create-document-v2';
|
||||
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
|
||||
import { duplicateDocument } from '@documenso/lib/server-only/document/duplicate-document-by-id';
|
||||
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
|
||||
@ -22,11 +24,14 @@ import { searchDocumentsWithKeyword } from '@documenso/lib/server-only/document/
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
import { updateDocument } from '@documenso/lib/server-only/document/update-document';
|
||||
import { symmetricEncrypt } from '@documenso/lib/universal/crypto';
|
||||
import { DocumentStatus } from '@documenso/prisma/client';
|
||||
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
|
||||
import { DocumentDataType, DocumentStatus } from '@documenso/prisma/client';
|
||||
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
ZCreateDocumentRequestSchema,
|
||||
ZCreateDocumentV2RequestSchema,
|
||||
ZCreateDocumentV2ResponseSchema,
|
||||
ZDeleteDocumentMutationSchema,
|
||||
ZDistributeDocumentRequestSchema,
|
||||
ZDistributeDocumentResponseSchema,
|
||||
@ -146,6 +151,79 @@ export const documentRouter = router({
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Temporariy endpoint for V2 Beta until we allow passthrough documents on create.
|
||||
*
|
||||
* @public
|
||||
* @deprecated
|
||||
*/
|
||||
createDocumentTemporary: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'POST',
|
||||
path: '/document/create/beta',
|
||||
summary: 'Create document',
|
||||
description:
|
||||
'You will need to upload the PDF to the provided URL returned. Note: Once V2 API is released, this will be removed since we will allow direct uploads, instead of using an upload URL.',
|
||||
tags: ['Document'],
|
||||
},
|
||||
})
|
||||
.input(ZCreateDocumentV2RequestSchema)
|
||||
.output(ZCreateDocumentV2ResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = ctx;
|
||||
|
||||
const {
|
||||
title,
|
||||
externalId,
|
||||
visibility,
|
||||
globalAccessAuth,
|
||||
globalActionAuth,
|
||||
recipients,
|
||||
meta,
|
||||
} = input;
|
||||
|
||||
const { remaining } = await getServerLimits({ email: ctx.user.email, teamId });
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'You have reached your document limit for this month. Please upgrade your plan.',
|
||||
});
|
||||
}
|
||||
|
||||
const fileName = title.endsWith('.pdf') ? title : `${title}.pdf`;
|
||||
|
||||
const { url, key } = await getPresignPostUrl(fileName, 'application/pdf');
|
||||
|
||||
const documentData = await createDocumentData({
|
||||
data: key,
|
||||
type: DocumentDataType.S3_PATH,
|
||||
});
|
||||
|
||||
const createdDocument = await createDocumentV2({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
documentDataId: documentData.id,
|
||||
normalizePdf: false, // Not normalizing because of presigned URL.
|
||||
data: {
|
||||
title,
|
||||
externalId,
|
||||
visibility,
|
||||
globalAccessAuth,
|
||||
globalActionAuth,
|
||||
recipients,
|
||||
},
|
||||
meta,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
document: createdDocument,
|
||||
uploadUrl: url,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* Wait until RR7 so we can passthrough documents.
|
||||
*
|
||||
@ -220,6 +298,7 @@ export const documentRouter = router({
|
||||
typedSignatureEnabled: meta.typedSignatureEnabled,
|
||||
redirectUrl: meta.redirectUrl,
|
||||
distributionMethod: meta.distributionMethod,
|
||||
signingOrder: meta.signingOrder,
|
||||
emailSettings: meta.emailSettings,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
@ -240,8 +319,8 @@ export const documentRouter = router({
|
||||
deleteDocument: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/document/{documentId}',
|
||||
method: 'POST',
|
||||
path: '/document/delete',
|
||||
summary: 'Delete document',
|
||||
tags: ['Document'],
|
||||
},
|
||||
@ -320,6 +399,8 @@ export const documentRouter = router({
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Todo: Remove and use `updateDocument` endpoint instead.
|
||||
*/
|
||||
setSigningOrderForDocument: authenticatedProcedure
|
||||
.input(ZSetSigningOrderForDocumentMutationSchema)
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { VALID_DATE_FORMAT_VALUES } from '@documenso/lib/constants/date-formats';
|
||||
import { SUPPORTED_LANGUAGE_CODES } from '@documenso/lib/constants/i18n';
|
||||
import {
|
||||
ZDocumentLiteSchema,
|
||||
@ -11,6 +12,15 @@ import {
|
||||
ZDocumentActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
||||
import { ZDocumentFormValuesSchema } from '@documenso/lib/types/document-form-values';
|
||||
import {
|
||||
ZFieldHeightSchema,
|
||||
ZFieldPageNumberSchema,
|
||||
ZFieldPageXSchema,
|
||||
ZFieldPageYSchema,
|
||||
ZFieldWidthSchema,
|
||||
} from '@documenso/lib/types/field';
|
||||
import { ZFieldAndMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
|
||||
import {
|
||||
@ -22,14 +32,42 @@ import {
|
||||
FieldType,
|
||||
} from '@documenso/prisma/client';
|
||||
|
||||
import { ZCreateRecipientSchema } from '../recipient-router/schema';
|
||||
|
||||
export const ZDocumentTitleSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(255)
|
||||
.describe('The title of the document.');
|
||||
|
||||
export const ZDocumentExternalIdSchema = z
|
||||
.string()
|
||||
.trim()
|
||||
.describe('The external ID of the document.');
|
||||
|
||||
export const ZDocumentVisibilitySchema = z
|
||||
.nativeEnum(DocumentVisibility)
|
||||
.describe('The visibility of the document.');
|
||||
|
||||
export const ZDocumentMetaTimezoneSchema = z
|
||||
.string()
|
||||
.describe('The timezone to use for date fields and signing the document.');
|
||||
.describe(
|
||||
'The timezone to use for date fields and signing the document. Example Etc/UTC, Australia/Melbourne',
|
||||
);
|
||||
// Cooked.
|
||||
// .refine((value) => TIME_ZONES.includes(value), {
|
||||
// message: 'Invalid timezone. Please provide a valid timezone',
|
||||
// });
|
||||
|
||||
export type TDocumentMetaTimezone = z.infer<typeof ZDocumentMetaTimezoneSchema>;
|
||||
|
||||
export const ZDocumentMetaDateFormatSchema = z
|
||||
.string()
|
||||
.enum(VALID_DATE_FORMAT_VALUES)
|
||||
.describe('The date format to use for date fields and signing the document.');
|
||||
|
||||
export type TDocumentMetaDateFormat = z.infer<typeof ZDocumentMetaDateFormatSchema>;
|
||||
|
||||
export const ZDocumentMetaRedirectUrlSchema = z
|
||||
.string()
|
||||
.describe('The URL to which the recipient should be redirected after signing the document.')
|
||||
@ -55,7 +93,7 @@ export const ZDocumentMetaDistributionMethodSchema = z
|
||||
|
||||
export const ZDocumentMetaTypedSignatureEnabledSchema = z
|
||||
.boolean()
|
||||
.describe('Whether to allow typed signatures.');
|
||||
.describe('Whether to allow recipients to sign using a typed signature.');
|
||||
|
||||
export const ZFindDocumentsRequestSchema = ZFindSearchParamsSchema.extend({
|
||||
templateId: z
|
||||
@ -113,23 +151,79 @@ export const ZGetDocumentWithDetailsByIdRequestSchema = z.object({
|
||||
export const ZGetDocumentWithDetailsByIdResponseSchema = ZDocumentSchema;
|
||||
|
||||
export const ZCreateDocumentRequestSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
title: ZDocumentTitleSchema,
|
||||
documentDataId: z.string().min(1),
|
||||
timezone: z.string().optional(),
|
||||
timezone: ZDocumentMetaTimezoneSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZCreateDocumentV2RequestSchema = z.object({
|
||||
title: ZDocumentTitleSchema,
|
||||
externalId: ZDocumentExternalIdSchema.optional(),
|
||||
visibility: ZDocumentVisibilitySchema.optional(),
|
||||
globalAccessAuth: ZDocumentAccessAuthTypesSchema.optional(),
|
||||
globalActionAuth: ZDocumentActionAuthTypesSchema.optional(),
|
||||
formValues: ZDocumentFormValuesSchema.optional(),
|
||||
recipients: z
|
||||
.array(
|
||||
ZCreateRecipientSchema.extend({
|
||||
fields: ZFieldAndMetaSchema.and(
|
||||
z.object({
|
||||
pageNumber: ZFieldPageNumberSchema,
|
||||
pageX: ZFieldPageXSchema,
|
||||
pageY: ZFieldPageYSchema,
|
||||
width: ZFieldWidthSchema,
|
||||
height: ZFieldHeightSchema,
|
||||
}),
|
||||
)
|
||||
.array()
|
||||
.optional(),
|
||||
}),
|
||||
)
|
||||
.refine(
|
||||
(recipients) => {
|
||||
const emails = recipients.map((recipient) => recipient.email);
|
||||
|
||||
return new Set(emails).size === emails.length;
|
||||
},
|
||||
{ message: 'Recipients must have unique emails' },
|
||||
)
|
||||
.optional(),
|
||||
meta: z
|
||||
.object({
|
||||
subject: ZDocumentMetaSubjectSchema.optional(),
|
||||
message: ZDocumentMetaMessageSchema.optional(),
|
||||
timezone: ZDocumentMetaTimezoneSchema.optional(),
|
||||
dateFormat: ZDocumentMetaDateFormatSchema.optional(),
|
||||
distributionMethod: ZDocumentMetaDistributionMethodSchema.optional(),
|
||||
signingOrder: z.nativeEnum(DocumentSigningOrder).optional(),
|
||||
redirectUrl: ZDocumentMetaRedirectUrlSchema.optional(),
|
||||
language: ZDocumentMetaLanguageSchema.optional(),
|
||||
typedSignatureEnabled: ZDocumentMetaTypedSignatureEnabledSchema.optional(),
|
||||
emailSettings: ZDocumentEmailSettingsSchema.optional(),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type TCreateDocumentV2Request = z.infer<typeof ZCreateDocumentV2RequestSchema>;
|
||||
|
||||
export const ZCreateDocumentV2ResponseSchema = z.object({
|
||||
document: ZDocumentSchema,
|
||||
uploadUrl: z
|
||||
.string()
|
||||
.describe(
|
||||
'The URL to upload the document PDF to. Use a PUT request with the file via form-data',
|
||||
),
|
||||
});
|
||||
|
||||
export const ZUpdateDocumentRequestSchema = z.object({
|
||||
documentId: z.number(),
|
||||
data: z
|
||||
.object({
|
||||
title: z.string().describe('The title of the document.').min(1).optional(),
|
||||
externalId: z.string().nullish().describe('The external ID of the document.'),
|
||||
visibility: z
|
||||
.nativeEnum(DocumentVisibility)
|
||||
.describe('The visibility of the document.')
|
||||
.optional(),
|
||||
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullable().optional(),
|
||||
globalActionAuth: ZDocumentActionAuthTypesSchema.nullable().optional(),
|
||||
title: ZDocumentTitleSchema.optional(),
|
||||
externalId: ZDocumentExternalIdSchema.nullish(),
|
||||
visibility: ZDocumentVisibilitySchema.optional(),
|
||||
globalAccessAuth: ZDocumentAccessAuthTypesSchema.nullish(),
|
||||
globalActionAuth: ZDocumentActionAuthTypesSchema.nullish(),
|
||||
})
|
||||
.optional(),
|
||||
meta: z
|
||||
@ -139,6 +233,7 @@ export const ZUpdateDocumentRequestSchema = z.object({
|
||||
timezone: ZDocumentMetaTimezoneSchema.optional(),
|
||||
dateFormat: ZDocumentMetaDateFormatSchema.optional(),
|
||||
distributionMethod: ZDocumentMetaDistributionMethodSchema.optional(),
|
||||
signingOrder: z.nativeEnum(DocumentSigningOrder).optional(),
|
||||
redirectUrl: ZDocumentMetaRedirectUrlSchema.optional(),
|
||||
language: ZDocumentMetaLanguageSchema.optional(),
|
||||
typedSignatureEnabled: ZDocumentMetaTypedSignatureEnabledSchema.optional(),
|
||||
|
||||
@ -193,8 +193,8 @@ export const fieldRouter = router({
|
||||
deleteDocumentField: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/document/field/{fieldId}',
|
||||
method: 'POST',
|
||||
path: '/document/field/delete',
|
||||
summary: 'Delete document field',
|
||||
tags: ['Document Fields'],
|
||||
},
|
||||
@ -370,8 +370,8 @@ export const fieldRouter = router({
|
||||
deleteTemplateField: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/template/field/{fieldId}',
|
||||
method: 'POST',
|
||||
path: '/template/field/delete',
|
||||
summary: 'Delete template field',
|
||||
tags: ['Template Fields'],
|
||||
},
|
||||
|
||||
@ -1,31 +1,38 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZRecipientActionAuthSchema } from '@documenso/lib/types/document-auth';
|
||||
import { ZFieldSchema } from '@documenso/lib/types/field';
|
||||
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import {
|
||||
ZFieldHeightSchema,
|
||||
ZFieldPageNumberSchema,
|
||||
ZFieldPageXSchema,
|
||||
ZFieldPageYSchema,
|
||||
ZFieldSchema,
|
||||
ZFieldWidthSchema,
|
||||
} from '@documenso/lib/types/field';
|
||||
import { ZFieldAndMetaSchema, ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
|
||||
const ZCreateFieldSchema = z.object({
|
||||
recipientId: z.number().describe('The ID of the recipient to create the field for.'),
|
||||
type: ZFieldSchema.shape.type.describe('The type of the field to create.'),
|
||||
pageNumber: z.number().describe('The page number the field will be on.'),
|
||||
pageX: z.number().describe('The X coordinate of where the field will be placed.'),
|
||||
pageY: z.number().describe('The Y coordinate of where the field will be placed.'),
|
||||
width: z.number().describe('The width of the field.'),
|
||||
height: z.number().describe('The height of the field.'),
|
||||
fieldMeta: ZFieldMetaSchema.optional(),
|
||||
});
|
||||
const ZCreateFieldSchema = ZFieldAndMetaSchema.and(
|
||||
z.object({
|
||||
recipientId: z.number().describe('The ID of the recipient to create the field for.'),
|
||||
pageNumber: ZFieldPageNumberSchema,
|
||||
pageX: ZFieldPageXSchema,
|
||||
pageY: ZFieldPageYSchema,
|
||||
width: ZFieldWidthSchema,
|
||||
height: ZFieldHeightSchema,
|
||||
}),
|
||||
);
|
||||
|
||||
const ZUpdateFieldSchema = z.object({
|
||||
id: z.number().describe('The ID of the field to update.'),
|
||||
type: ZFieldSchema.shape.type.optional().describe('The type of the field to update.'),
|
||||
pageNumber: z.number().optional().describe('The page number the field will be on.'),
|
||||
pageX: z.number().optional().describe('The X coordinate of where the field will be placed.'),
|
||||
pageY: z.number().optional().describe('The Y coordinate of where the field will be placed.'),
|
||||
width: z.number().optional().describe('The width of the field.'),
|
||||
height: z.number().optional().describe('The height of the field.'),
|
||||
fieldMeta: ZFieldMetaSchema.optional(),
|
||||
});
|
||||
const ZUpdateFieldSchema = ZFieldAndMetaSchema.and(
|
||||
z.object({
|
||||
id: z.number().describe('The ID of the field to update.'),
|
||||
pageNumber: ZFieldPageNumberSchema.optional(),
|
||||
pageX: ZFieldPageXSchema.optional(),
|
||||
pageY: ZFieldPageYSchema.optional(),
|
||||
width: ZFieldWidthSchema.optional(),
|
||||
height: ZFieldHeightSchema.optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
export const ZCreateDocumentFieldRequestSchema = z.object({
|
||||
documentId: z.number(),
|
||||
|
||||
@ -4,9 +4,27 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
|
||||
import { appRouter } from './router';
|
||||
|
||||
export const openApiDocument = generateOpenApiDocument(appRouter, {
|
||||
title: 'Documenso v2 beta API',
|
||||
description: 'Subject to breaking changes until v2 is fully released.',
|
||||
version: '0.0.0',
|
||||
baseUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/api/v2-beta`,
|
||||
});
|
||||
export const openApiDocument = {
|
||||
...generateOpenApiDocument(appRouter, {
|
||||
title: 'Documenso v2 beta API',
|
||||
description: 'Subject to breaking changes until v2 is fully released.',
|
||||
version: '0.0.0',
|
||||
baseUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/api/v2-beta`,
|
||||
securitySchemes: {
|
||||
apiKey: {
|
||||
type: 'apiKey',
|
||||
in: 'header',
|
||||
name: 'Authorization',
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Dirty way to pass through the security field.
|
||||
*/
|
||||
security: [
|
||||
{
|
||||
apiKey: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -193,8 +193,8 @@ export const recipientRouter = router({
|
||||
deleteDocumentRecipient: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/document/recipient/{recipientId}',
|
||||
method: 'POST',
|
||||
path: '/document/recipient/delete',
|
||||
summary: 'Delete document recipient',
|
||||
tags: ['Document Recipients'],
|
||||
},
|
||||
@ -367,8 +367,8 @@ export const recipientRouter = router({
|
||||
deleteTemplateRecipient: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/template/recipient/{recipientId}',
|
||||
method: 'POST',
|
||||
path: '/template/recipient/delete',
|
||||
summary: 'Delete template recipient',
|
||||
tags: ['Template Recipients'],
|
||||
},
|
||||
|
||||
@ -14,7 +14,14 @@ export const ZGetRecipientRequestSchema = z.object({
|
||||
|
||||
export const ZGetRecipientResponseSchema = ZRecipientSchema;
|
||||
|
||||
const ZCreateRecipientSchema = z.object({
|
||||
/**
|
||||
* When changing this, ensure everything that uses this schema is updated correctly
|
||||
* since this will change the Openapi schema.
|
||||
*
|
||||
* Example `createDocument` uses this, so you will need to update that function to
|
||||
* pass along required details.
|
||||
*/
|
||||
export const ZCreateRecipientSchema = z.object({
|
||||
email: z.string().toLowerCase().email().min(1),
|
||||
name: z.string(),
|
||||
role: z.nativeEnum(RecipientRole),
|
||||
@ -42,11 +49,16 @@ export const ZCreateDocumentRecipientResponseSchema = ZRecipientLiteSchema;
|
||||
|
||||
export const ZCreateDocumentRecipientsRequestSchema = z.object({
|
||||
documentId: z.number(),
|
||||
recipients: z.array(ZCreateRecipientSchema).refine((recipients) => {
|
||||
const emails = recipients.map((recipient) => recipient.email.toLowerCase());
|
||||
recipients: z.array(ZCreateRecipientSchema).refine(
|
||||
(recipients) => {
|
||||
const emails = recipients.map((recipient) => recipient.email.toLowerCase());
|
||||
|
||||
return new Set(emails).size === emails.length;
|
||||
}),
|
||||
return new Set(emails).size === emails.length;
|
||||
},
|
||||
{
|
||||
message: 'Recipients must have unique emails',
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
export const ZCreateDocumentRecipientsResponseSchema = z.object({
|
||||
@ -62,13 +74,18 @@ export const ZUpdateDocumentRecipientResponseSchema = ZRecipientSchema;
|
||||
|
||||
export const ZUpdateDocumentRecipientsRequestSchema = z.object({
|
||||
documentId: z.number(),
|
||||
recipients: z.array(ZUpdateRecipientSchema).refine((recipients) => {
|
||||
const emails = recipients
|
||||
.filter((recipient) => recipient.email !== undefined)
|
||||
.map((recipient) => recipient.email?.toLowerCase());
|
||||
recipients: z.array(ZUpdateRecipientSchema).refine(
|
||||
(recipients) => {
|
||||
const emails = recipients
|
||||
.filter((recipient) => recipient.email !== undefined)
|
||||
.map((recipient) => recipient.email?.toLowerCase());
|
||||
|
||||
return new Set(emails).size === emails.length;
|
||||
}),
|
||||
return new Set(emails).size === emails.length;
|
||||
},
|
||||
{
|
||||
message: 'Recipients must have unique emails',
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
export const ZUpdateDocumentRecipientsResponseSchema = z.object({
|
||||
@ -100,7 +117,7 @@ export const ZSetDocumentRecipientsRequestSchema = z
|
||||
return new Set(emails).size === emails.length;
|
||||
},
|
||||
// Dirty hack to handle errors when .root is populated for an array type
|
||||
{ message: 'Signers must have unique emails', path: ['signers__root'] },
|
||||
{ message: 'Recipients must have unique emails', path: ['recipients__root'] },
|
||||
);
|
||||
|
||||
export const ZSetDocumentRecipientsResponseSchema = z.object({
|
||||
@ -116,11 +133,16 @@ export const ZCreateTemplateRecipientResponseSchema = ZRecipientLiteSchema;
|
||||
|
||||
export const ZCreateTemplateRecipientsRequestSchema = z.object({
|
||||
templateId: z.number(),
|
||||
recipients: z.array(ZCreateRecipientSchema).refine((recipients) => {
|
||||
const emails = recipients.map((recipient) => recipient.email);
|
||||
recipients: z.array(ZCreateRecipientSchema).refine(
|
||||
(recipients) => {
|
||||
const emails = recipients.map((recipient) => recipient.email);
|
||||
|
||||
return new Set(emails).size === emails.length;
|
||||
}),
|
||||
return new Set(emails).size === emails.length;
|
||||
},
|
||||
{
|
||||
message: 'Recipients must have unique emails',
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
export const ZCreateTemplateRecipientsResponseSchema = z.object({
|
||||
@ -136,13 +158,18 @@ export const ZUpdateTemplateRecipientResponseSchema = ZRecipientSchema;
|
||||
|
||||
export const ZUpdateTemplateRecipientsRequestSchema = z.object({
|
||||
templateId: z.number(),
|
||||
recipients: z.array(ZUpdateRecipientSchema).refine((recipients) => {
|
||||
const emails = recipients
|
||||
.filter((recipient) => recipient.email !== undefined)
|
||||
.map((recipient) => recipient.email);
|
||||
recipients: z.array(ZUpdateRecipientSchema).refine(
|
||||
(recipients) => {
|
||||
const emails = recipients
|
||||
.filter((recipient) => recipient.email !== undefined)
|
||||
.map((recipient) => recipient.email);
|
||||
|
||||
return new Set(emails).size === emails.length;
|
||||
}),
|
||||
return new Set(emails).size === emails.length;
|
||||
},
|
||||
{
|
||||
message: 'Recipients must have unique emails',
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
export const ZUpdateTemplateRecipientsResponseSchema = z.object({
|
||||
|
||||
@ -13,36 +13,15 @@ import {
|
||||
ZCreateTemplateResponseSchema,
|
||||
createTemplate,
|
||||
} from '@documenso/lib/server-only/template/create-template';
|
||||
import {
|
||||
ZCreateTemplateDirectLinkResponseSchema,
|
||||
createTemplateDirectLink,
|
||||
} from '@documenso/lib/server-only/template/create-template-direct-link';
|
||||
import { createTemplateDirectLink } from '@documenso/lib/server-only/template/create-template-direct-link';
|
||||
import { deleteTemplate } from '@documenso/lib/server-only/template/delete-template';
|
||||
import { deleteTemplateDirectLink } from '@documenso/lib/server-only/template/delete-template-direct-link';
|
||||
import {
|
||||
ZDuplicateTemplateResponseSchema,
|
||||
duplicateTemplate,
|
||||
} from '@documenso/lib/server-only/template/duplicate-template';
|
||||
import {
|
||||
ZFindTemplatesResponseSchema,
|
||||
findTemplates,
|
||||
} from '@documenso/lib/server-only/template/find-templates';
|
||||
import {
|
||||
ZGetTemplateByIdResponseSchema,
|
||||
getTemplateById,
|
||||
} from '@documenso/lib/server-only/template/get-template-by-id';
|
||||
import {
|
||||
ZMoveTemplateToTeamResponseSchema,
|
||||
moveTemplateToTeam,
|
||||
} from '@documenso/lib/server-only/template/move-template-to-team';
|
||||
import {
|
||||
ZToggleTemplateDirectLinkResponseSchema,
|
||||
toggleTemplateDirectLink,
|
||||
} from '@documenso/lib/server-only/template/toggle-template-direct-link';
|
||||
import {
|
||||
ZUpdateTemplateResponseSchema,
|
||||
updateTemplate,
|
||||
} from '@documenso/lib/server-only/template/update-template';
|
||||
import { duplicateTemplate } from '@documenso/lib/server-only/template/duplicate-template';
|
||||
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
|
||||
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
|
||||
import { moveTemplateToTeam } from '@documenso/lib/server-only/template/move-template-to-team';
|
||||
import { toggleTemplateDirectLink } from '@documenso/lib/server-only/template/toggle-template-direct-link';
|
||||
import { updateTemplate } from '@documenso/lib/server-only/template/update-template';
|
||||
import type { Document } from '@documenso/prisma/client';
|
||||
|
||||
import { authenticatedProcedure, maybeAuthenticatedProcedure, router } from '../trpc';
|
||||
@ -50,16 +29,23 @@ import {
|
||||
ZCreateDocumentFromDirectTemplateRequestSchema,
|
||||
ZCreateDocumentFromTemplateRequestSchema,
|
||||
ZCreateDocumentFromTemplateResponseSchema,
|
||||
ZCreateTemplateDirectLinkMutationSchema,
|
||||
ZCreateTemplateDirectLinkRequestSchema,
|
||||
ZCreateTemplateDirectLinkResponseSchema,
|
||||
ZCreateTemplateMutationSchema,
|
||||
ZDeleteTemplateDirectLinkMutationSchema,
|
||||
ZDeleteTemplateDirectLinkRequestSchema,
|
||||
ZDeleteTemplateMutationSchema,
|
||||
ZDuplicateTemplateMutationSchema,
|
||||
ZDuplicateTemplateResponseSchema,
|
||||
ZFindTemplatesRequestSchema,
|
||||
ZFindTemplatesResponseSchema,
|
||||
ZGetTemplateByIdRequestSchema,
|
||||
ZMoveTemplatesToTeamRequestSchema,
|
||||
ZToggleTemplateDirectLinkMutationSchema,
|
||||
ZGetTemplateByIdResponseSchema,
|
||||
ZMoveTemplateToTeamRequestSchema,
|
||||
ZMoveTemplateToTeamResponseSchema,
|
||||
ZToggleTemplateDirectLinkRequestSchema,
|
||||
ZToggleTemplateDirectLinkResponseSchema,
|
||||
ZUpdateTemplateRequestSchema,
|
||||
ZUpdateTemplateResponseSchema,
|
||||
} from './schema';
|
||||
|
||||
export const templateRouter = router({
|
||||
@ -202,8 +188,8 @@ export const templateRouter = router({
|
||||
deleteTemplate: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/template/{templateId}',
|
||||
method: 'POST',
|
||||
path: '/template/delete',
|
||||
summary: 'Delete template',
|
||||
tags: ['Template'],
|
||||
},
|
||||
@ -331,7 +317,7 @@ export const templateRouter = router({
|
||||
tags: ['Template'],
|
||||
},
|
||||
})
|
||||
.input(ZCreateTemplateDirectLinkMutationSchema)
|
||||
.input(ZCreateTemplateDirectLinkRequestSchema)
|
||||
.output(ZCreateTemplateDirectLinkResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = ctx;
|
||||
@ -358,14 +344,14 @@ export const templateRouter = router({
|
||||
deleteTemplateDirectLink: authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'DELETE',
|
||||
path: '/template/direct/{templateId}',
|
||||
method: 'POST',
|
||||
path: '/template/direct/delete',
|
||||
summary: 'Delete direct link',
|
||||
description: 'Delete a direct link for a template',
|
||||
tags: ['Template'],
|
||||
},
|
||||
})
|
||||
.input(ZDeleteTemplateDirectLinkMutationSchema)
|
||||
.input(ZDeleteTemplateDirectLinkRequestSchema)
|
||||
.output(z.void())
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = ctx;
|
||||
@ -389,7 +375,7 @@ export const templateRouter = router({
|
||||
tags: ['Template'],
|
||||
},
|
||||
})
|
||||
.input(ZToggleTemplateDirectLinkMutationSchema)
|
||||
.input(ZToggleTemplateDirectLinkRequestSchema)
|
||||
.output(ZToggleTemplateDirectLinkResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = ctx;
|
||||
@ -413,7 +399,7 @@ export const templateRouter = router({
|
||||
tags: ['Template'],
|
||||
},
|
||||
})
|
||||
.input(ZMoveTemplatesToTeamRequestSchema)
|
||||
.input(ZMoveTemplateToTeamRequestSchema)
|
||||
.output(ZMoveTemplateToTeamResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { templateId, teamId } = input;
|
||||
|
||||
@ -6,8 +6,14 @@ import {
|
||||
ZDocumentActionAuthTypesSchema,
|
||||
} from '@documenso/lib/types/document-auth';
|
||||
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
||||
import { ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import { ZFindResultResponse, ZFindSearchParamsSchema } from '@documenso/lib/types/search-params';
|
||||
import {
|
||||
ZTemplateLiteSchema,
|
||||
ZTemplateManySchema,
|
||||
ZTemplateSchema,
|
||||
} from '@documenso/lib/types/template';
|
||||
import { DocumentSigningOrder, DocumentVisibility, TemplateType } from '@documenso/prisma/client';
|
||||
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod';
|
||||
|
||||
import {
|
||||
ZDocumentMetaDateFormatSchema,
|
||||
@ -69,7 +75,9 @@ export const ZDuplicateTemplateMutationSchema = z.object({
|
||||
templateId: z.number(),
|
||||
});
|
||||
|
||||
export const ZCreateTemplateDirectLinkMutationSchema = z.object({
|
||||
export const ZDuplicateTemplateResponseSchema = ZTemplateLiteSchema;
|
||||
|
||||
export const ZCreateTemplateDirectLinkRequestSchema = z.object({
|
||||
templateId: z.number(),
|
||||
directRecipientId: z
|
||||
.number()
|
||||
@ -79,15 +87,28 @@ export const ZCreateTemplateDirectLinkMutationSchema = z.object({
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const ZDeleteTemplateDirectLinkMutationSchema = z.object({
|
||||
const GenericDirectLinkResponseSchema = TemplateDirectLinkSchema.pick({
|
||||
id: true,
|
||||
templateId: true,
|
||||
token: true,
|
||||
createdAt: true,
|
||||
enabled: true,
|
||||
directTemplateRecipientId: true,
|
||||
});
|
||||
|
||||
export const ZCreateTemplateDirectLinkResponseSchema = GenericDirectLinkResponseSchema;
|
||||
|
||||
export const ZDeleteTemplateDirectLinkRequestSchema = z.object({
|
||||
templateId: z.number(),
|
||||
});
|
||||
|
||||
export const ZToggleTemplateDirectLinkMutationSchema = z.object({
|
||||
export const ZToggleTemplateDirectLinkRequestSchema = z.object({
|
||||
templateId: z.number(),
|
||||
enabled: z.boolean(),
|
||||
});
|
||||
|
||||
export const ZToggleTemplateDirectLinkResponseSchema = GenericDirectLinkResponseSchema;
|
||||
|
||||
export const ZDeleteTemplateMutationSchema = z.object({
|
||||
templateId: z.number(),
|
||||
});
|
||||
@ -141,19 +162,32 @@ export const ZUpdateTemplateRequestSchema = z.object({
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export const ZUpdateTemplateResponseSchema = ZTemplateLiteSchema;
|
||||
|
||||
export const ZFindTemplatesRequestSchema = ZFindSearchParamsSchema.extend({
|
||||
type: z.nativeEnum(TemplateType).describe('Filter templates by type.').optional(),
|
||||
});
|
||||
|
||||
export const ZFindTemplatesResponseSchema = ZFindResultResponse.extend({
|
||||
data: ZTemplateManySchema.array(),
|
||||
});
|
||||
|
||||
export type TFindTemplatesResponse = z.infer<typeof ZFindTemplatesResponseSchema>;
|
||||
export type FindTemplateRow = TFindTemplatesResponse['data'][number];
|
||||
|
||||
export const ZGetTemplateByIdRequestSchema = z.object({
|
||||
templateId: z.number(),
|
||||
});
|
||||
|
||||
export const ZMoveTemplatesToTeamRequestSchema = z.object({
|
||||
export const ZGetTemplateByIdResponseSchema = ZTemplateSchema;
|
||||
|
||||
export const ZMoveTemplateToTeamRequestSchema = z.object({
|
||||
templateId: z.number().describe('The ID of the template to move to.'),
|
||||
teamId: z.number().describe('The ID of the team to move the template to.'),
|
||||
});
|
||||
|
||||
export const ZMoveTemplateToTeamResponseSchema = ZTemplateLiteSchema;
|
||||
|
||||
export type TCreateTemplateMutationSchema = z.infer<typeof ZCreateTemplateMutationSchema>;
|
||||
export type TDuplicateTemplateMutationSchema = z.infer<typeof ZDuplicateTemplateMutationSchema>;
|
||||
export type TDeleteTemplateMutationSchema = z.infer<typeof ZDeleteTemplateMutationSchema>;
|
||||
|
||||
Reference in New Issue
Block a user